local.http.subscribe()
This function takes a url and issues a GET request for the "text/event-stream" type. If targeting a remote server, it will leverage BrowserRemoteEventStream. Otherwise, it issues an HTTPL request and manually watches for updates on the stream. From your perspective, it all looks like this:
var stream = local.http.subscribe('httpl://foo.usr/some/resource'); stream.on('update', function(e) { console.log(e.data); }); stream.on(['foo','bar'], function(e) { console.log(e.event); // 'foo' or 'bar' }); // ... stream.close(); // client-side disconnect
In keeping with browser implementations, closing the event stream (from the server or the client) will emit an 'error' event with an undefined e.data.
navigator.subscribe()
If you find yourself needing to navigate to the event-stream, you can use navigator.subscribe(), which promises the EventStream interface:
local.http.navigator('httpl://foo.usr') .collection('some') .item('resource') .subscribe() .then(function(stream) { stream.on('foo', onFoo); });
data-subscribe
Grimwire doesn't allow any javascript to enter the document, meaning there's no client-side code. To create rich realtime UIs, servers use some added response directives, content-types, and HTML behaviors. One of the most common of these is the "data-subscribe" attribute.
<div data-subscribe="httpl://someserver.usr/some/resource"> <p>This was last updated at 10:27:54</p> </div> <p>This will not be updated.</p>
Any element with this attribute will open an event-stream to the target and listen for the "update" event. Every time this occurs, the client region will issue a GET request to the same resource, then replace its innerHTML with the response. You can think of this as triggering targeted page-refreshes.
If the content of the refresh is at a different location than the event-stream, you can specify that URL afterward.
<div data-subscribe="httpl://foo.usr/bar httpl://foo.usr/buz">
Lastly, if the "data-subscribe" attribute is set on a form element, the form's "accept" attribute will be used, and its inputs will be enumerated in the query parameters of the request. This can be used to push up information about the client state.
local.http.broadcaster()
On the server end of things, you'll need to track any event-stream responses you have open so that you can write events to it. Grimwire gives you local.http.broadcaster() to help with this.
// This server is an open event relay // - it rebroadcasts any data POSTed to it var mybroadcast = local.http.broadcaster(); function main(request, response) { if (/event-stream/.test(request.headers.accept)) { response.writeHead(200, 'ok', { 'content-type':'text/event-stream' }); mybroadcast.addStream(response); // NOTE: don't call end() on the response! } else if (request.method == 'POST') { response.writeHead(204, 'no content').end(); mybroadcast.emit('post', request.body); // ^ will broadcast to all open streams } else response.writeHead(400, 'bad request').end(); }
You can read more about its api in the Local documentation.
Wrapping up
Server-Sent Events are a very useful tool for synchronizing programs with other programs and with their interfaces. Consumers can access them using the subscriber APIs, and servers can broadcast them from any URL. Combining their "push" architecture with REST's "pull" requests makes it possible to build tightly-synced applications without tight coupling.
Read more at github.com/grimwire/grimwire.
No comments:
Post a Comment