First test usage of the channel stuff from a real browser. Properly sends an error back.

This commit is contained in:
Elliot Glaysher 2019-01-08 14:44:03 -08:00
parent 0f479f012d
commit bee2b01fb6
2 changed files with 151 additions and 13 deletions

View File

@ -36,17 +36,33 @@
; Time is ; Time is
;span#time; ;span#time;
== ==
;script:''' ;script(type "module", src "/~server/hello.js");
var evtSource = new EventSource("/~server/stream",
{ withCredentials: true } );
evtSource.onmessage = function(e) {
var message = document.getElementById("time");
message.innerHTML = e.data;
}
'''
== ==
== ==
::
++ hello-js
^- octs
%- as-octs:mimes:html
'''
import * as urb from '/~/channel/channel.js';
var c = urb.newChannel();
c.poke("zod", "server", "json", 5,
function() {
console.log("Poke worked");
},
function(err) {
console.log("Poke failed: " + err);
});
var evtSource = new EventSource("/~server/stream",
{ withCredentials: true } );
evtSource.onmessage = function(e) {
var message = document.getElementById("time");
message.innerHTML = e.data;
}
'''
:: helper library that lets an app handle an EventSource. :: helper library that lets an app handle an EventSource.
:: ::
:: TODO: This doesn't even attempt to deal with sequence numbers. :: TODO: This doesn't even attempt to deal with sequence numbers.
@ -194,6 +210,15 @@
(handle-start-stream inbound-request) (handle-start-stream inbound-request)
~& [%name name] ~& [%name name]
:: ::
?: =(name 'hello')
:_ this
:~ ^- move
:- ost.bow
:* %http-response
[%start 200 ['content-type' 'application/javascript']~ [~ hello-js] %.y]
==
==
::
:_ this :_ this
:~ ^- move :~ ^- move
:- ost.bow :- ost.bow

View File

@ -415,6 +415,107 @@
~ ~
== ==
== ==
:: +channel-js: the urbit javascript interface
::
++ channel-js
^- octs
%- as-octs:mimes:html
'''
class Channel {
constructor() {
// unique identifier: current time and random number
//
this.uid =
new Date().getTime().toString() +
"-" +
Math.random().toString(16).slice(-6);
this.requestId = 1;
this.connection = null;
// a registry of requestId to successFunc/failureFunc
//
// These functions are registered during a +poke and are executed
// in the onServerEvent()/onServerError() callbacks. Only one of
// the functions will be called, and the outstanding poke will be
// removed after calling the success or failure function.
//
this.outstandingPokes = new Map();
// a registry of requestId to eventFunc/disconnectFunc
//
// These functions are registered during a +subscribe and are
// executed in the onServerEvent()/onServerError() callbacks. The
// event function will be called whenever a new piece of data on this
// subscription is available, which may be 0, 1, or many times. The
// disconnect function may be called exactly once.
//
this.outstandingSubscriptions = new Map();
}
// sends a poke to an app on an urbit ship
//
poke(ship, app, mark, json, successFunc, failureFunc) {
var id = this.nextId();
this.outstandingPokes.set(
id, {"success": successFunc, "fail": failureFunc});
var req = new XMLHttpRequest();
req.open("PUT", this.channelURL());
req.setRequestHeader("Content-Type", "application/json");
// TODO: Need to stuff an "ack" in here, too.
var x = JSON.stringify([{
"id": id,
"action": "poke",
"ship": ship,
"app": app,
"mark": mark,
"json": json
}]);
req.send(x);
this.connectIfDisconnected();
}
connectIfDisconnected() {
if (this.connection)
return;
this.eventSource = new EventSource(this.channelURL(), {withCredentials:true});
this.eventSource.onmessage = e => {
var obj = JSON.parse(e.data);
if (obj.response == "poke") {
var funcs = this.outstandingPokes.get(obj.id);
if (obj.hasOwnProperty("ok"))
funcs["success"]()
else
funcs["fail"](obj.err)
this.outstandingPokes.delete(obj.id);
} else {
console.log("Unrecognized response: ", e);
}
}
this.eventSource.onerror = e => {
// TODO: The server broke the connection. Call every poke cancel and every
// subscription disconnect.
console.log(e);
}
}
channelURL() {
return "/~/channel/" + this.uid;
}
nextId() {
return this.requestId++;
}
};
export function newChannel() {
return new Channel;
}
'''
:: +format-ud-as-integer: prints a number for consumption outside urbit :: +format-ud-as-integer: prints a number for consumption outside urbit
:: ::
++ format-ud-as-integer ++ format-ud-as-integer
@ -651,7 +752,6 @@
:: if we are not a post, return an error :: if we are not a post, return an error
:: ::
?. =('POST' method.http-request) ?. =('POST' method.http-request)
~& [%something-other-than-get-post-on-login method.http-request]
(return-static-data-on-duct 400 'text/html' (login-page ~)) (return-static-data-on-duct 400 'text/html' (login-page ~))
:: we are a post, and must process the body type as form data :: we are a post, and must process the body type as form data
:: ::
@ -690,7 +790,6 @@
?~ redirect=(get-header 'redirect' u.parsed) ?~ redirect=(get-header 'redirect' u.parsed)
'/' '/'
u.redirect u.redirect
~& [%minting http-request]
:: ::
:_ state :_ state
:_ ~ :_ ~
@ -770,6 +869,7 @@
:: page; issuing a redirect won't help. :: page; issuing a redirect won't help.
:: ::
?. authenticated ?. authenticated
~& %unauthenticated
:: TODO: Real 400 page. :: TODO: Real 400 page.
:: ::
%^ return-static-data-on-duct 400 'text/html' %^ return-static-data-on-duct 400 'text/html'
@ -778,7 +878,8 @@
:: ::
=+ request-line=(parse-request-line url.http-request) =+ request-line=(parse-request-line url.http-request)
?. ?=([@t @t @t ~] site.request-line) ?. ?=([@t @t @t ~] site.request-line)
:: url is not of the form '/~/subscription/' ~& %bad-request-line
:: url is not of the form '/~/channel/'
:: ::
%^ return-static-data-on-duct 400 'text/html' %^ return-static-data-on-duct 400 'text/html'
(internal-server-error authenticated url.http-request ~) (internal-server-error authenticated url.http-request ~)
@ -786,6 +887,13 @@
:: ::
=+ channel-id=i.t.t.site.request-line =+ channel-id=i.t.t.site.request-line
:: ::
?: ?& =('channel' channel-id)
=([~ ~.js] ext.request-line)
==
:: client is requesting the javascript shim
::
(return-static-data-on-duct 200 'application/javascript' channel-js)
::
?: =('PUT' method.http-request) ?: =('PUT' method.http-request)
:: PUT methods starts/modifies a channel, and returns a result immediately :: PUT methods starts/modifies a channel, and returns a result immediately
:: ::
@ -977,24 +1085,29 @@
++ on-put-request ++ on-put-request
|= [channel-id=@t =http-request] |= [channel-id=@t =http-request]
^- [(list move) server-state] ^- [(list move) server-state]
~& %on-put-request
:: error when there's no body :: error when there's no body
:: ::
?~ body.http-request ?~ body.http-request
~& %no-body
%^ return-static-data-on-duct 400 'text/html' %^ return-static-data-on-duct 400 'text/html'
(internal-server-error %.y url.http-request ~) (internal-server-error %.y url.http-request ~)
:: if the incoming body isn't json, this is a bad request, 400. :: if the incoming body isn't json, this is a bad request, 400.
:: ::
?~ maybe-json=(de-json:html q.u.body.http-request) ?~ maybe-json=(de-json:html q.u.body.http-request)
~& %no-json
%^ return-static-data-on-duct 400 'text/html' %^ return-static-data-on-duct 400 'text/html'
(internal-server-error %.y url.http-request ~) (internal-server-error %.y url.http-request ~)
:: parse the json into an array of +channel-request items :: parse the json into an array of +channel-request items
:: ::
?~ maybe-requests=(parse-channel-request u.maybe-json) ?~ maybe-requests=(parse-channel-request u.maybe-json)
~& %no-parse
%^ return-static-data-on-duct 400 'text/html' %^ return-static-data-on-duct 400 'text/html'
(internal-server-error %.y url.http-request ~) (internal-server-error %.y url.http-request ~)
:: while weird, the request list could be empty :: while weird, the request list could be empty
:: ::
?: =(~ u.maybe-requests) ?: =(~ u.maybe-requests)
~& %empty-list
%^ return-static-data-on-duct 400 'text/html' %^ return-static-data-on-duct 400 'text/html'
(internal-server-error %.y url.http-request ~) (internal-server-error %.y url.http-request ~)
:: check for the existence of the channel-id :: check for the existence of the channel-id
@ -1411,7 +1524,7 @@
:: ::
++ parse-request-line ++ parse-request-line
|= url=@t |= url=@t
^- [[(unit @ta) site=(list @t)] args=(list [key=@t value=@t])] ^- [[ext=(unit @ta) site=(list @t)] args=(list [key=@t value=@t])]
(fall (rush url ;~(plug apat:de-purl:html yque:de-purl:html)) [[~ ~] ~]) (fall (rush url ;~(plug apat:de-purl:html yque:de-purl:html)) [[~ ~] ~])
-- --
:: end the =~ :: end the =~