sockjs
Advanced tools
Comparing version 0.0.0-rc2 to 0.0.1
@@ -46,3 +46,3 @@ (function() { | ||
if (this.options.sockjs_url) { | ||
throw "options.sockjs_url is required!"; | ||
throw new Error("options.sockjs_url is required!"); | ||
} | ||
@@ -67,3 +67,3 @@ if (user_options) { | ||
opts_filters = ['xhr_cors', 'xhr_options', 'cache_for', 'expose']; | ||
dispatcher = [['GET', p(''), ['welcome_screen']], ['GET', p('/iframe[0-9-.a-z_]*.html'), ['iframe', 'cache_for', 'expose']], ['GET', t('/jsonp'), ['h_no_cache', 'jsonp']], ['POST', t('/jsonp_send'), ['expect_form', 'jsonp_send']], ['POST', t('/xhr'), ['xhr_cors', 'xhr_poll']], ['OPTIONS', t('/xhr'), opts_filters], ['POST', t('/xhr_send'), ['xhr_cors', 'expect_xhr', 'xhr_send']], ['OPTIONS', t('/xhr_send'), opts_filters]]; | ||
dispatcher = [['GET', p(''), ['welcome_screen']], ['GET', p('/iframe[0-9-.a-z_]*.html'), ['iframe', 'cache_for', 'expose']], ['GET', t('/jsonp'), ['h_no_cache', 'jsonp']], ['POST', t('/jsonp_send'), ['expect_form', 'jsonp_send']], ['POST', t('/xhr'), ['xhr_cors', 'xhr_poll']], ['OPTIONS', t('/xhr'), opts_filters], ['POST', t('/xhr_send'), ['xhr_cors', 'expect_xhr', 'xhr_send']], ['OPTIONS', t('/xhr_send'), opts_filters], ['POST', t('/xhr_streaming'), ['xhr_cors', 'xhr_streaming']], ['OPTIONS', t('/xhr_streaming'), opts_filters]]; | ||
maybe_add_transport = function(name, urls) { | ||
@@ -87,3 +87,2 @@ var filters, method, url; | ||
maybe_add_transport('eventsource', [['GET', t('/eventsource'), ['h_no_cache', 'eventsource']]]); | ||
maybe_add_transport('xhr-streaming', [['POST', t('/xhr_streaming'), ['xhr_cors', 'xhr_streaming']], ['OPTIONS', t('/xhr_streaming'), opts_filters]]); | ||
webjs_handler = new webjs.WebJS(app, dispatcher); | ||
@@ -90,0 +89,0 @@ install_handler = function(ee, event, handler) { |
(function() { | ||
var iframe_template, utils; | ||
utils = require('./utils'); | ||
iframe_template = "<!DOCTYPE html>\n<html>\n<head>\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n <script>\n document.domain = document.domain;\n _sockjs_onload = function(){SockJS.bootstrap_iframe();};\n </script>\n <script src=\"{{ sockjs_url }}\"></script>\n</head>\n<body>\n <h2>Don't panic!</h2>\n <p>This is a SockJS hidden iframe. It's used for cross domain magic.</p>\n</body>\n<html>"; | ||
exports.app = { | ||
iframe: function(req, res) { | ||
var content, context, k, quoted_md5; | ||
context = { | ||
'{{ sockjs_url }}': req.sockjs_server.options.sockjs_url | ||
}; | ||
content = iframe_template; | ||
for (k in context) { | ||
content = content.replace(k, context[k]); | ||
} | ||
quoted_md5 = '"' + utils.md5_hex(content) + '"'; | ||
if ('if-none-match' in req.headers && req.headers['if-none-match'] === quoted_md5) { | ||
res.statusCode = 304; | ||
return ''; | ||
} | ||
res.setHeader('Content-Type', 'text/html; charset=UTF-8'); | ||
res.setHeader('ETag', quoted_md5); | ||
res.cache_for = 365 * 24 * 60 * 60; | ||
return content; | ||
} | ||
}; | ||
}).call(this); |
@@ -8,3 +8,3 @@ (function() { | ||
websocket: function(req, connection, head) { | ||
var location, origin; | ||
var location, origin, ver; | ||
if (req.headers.upgrade.toLowerCase() !== 'websocket') { | ||
@@ -25,3 +25,4 @@ throw { | ||
location += '://' + req.headers.host + req.url; | ||
if (req.headers['sec-websocket-version'] === '8') { | ||
ver = req.headers['sec-websocket-version']; | ||
if (ver === '8' || ver === '7') { | ||
new websocket_hybi10.WebHandshake8(req, connection, head || '', origin, location); | ||
@@ -28,0 +29,0 @@ } else { |
{ | ||
"name": "sockjs", | ||
"author": "Marek Majkowski", | ||
"version": "0.0.0-rc2", | ||
"version": "0.0.1", | ||
"repository": {"type": "git", | ||
@@ -6,0 +6,0 @@ "url": "https://github.com/majek/sockjs-client.git"}, |
220
README.md
@@ -9,28 +9,212 @@ SockJS-node server | ||
A fully working echo server would look like: | ||
An simplified echo SockJS server could look more or less like: | ||
var http = require('http'); | ||
var sockjs = require('sockjs'); | ||
```javascript | ||
var http = require('http'); | ||
var sockjs = require('sockjs'); | ||
var sockjs_opts = {sockjs_url: "http://127.0.0.1:8000/lib/sockjs.js"}; | ||
var echo = new sockjs.Server(sockjs_opts); | ||
echo.on('open', function(conn) { | ||
conn.on('message', function(e) { | ||
conn.send(e.data); | ||
}); | ||
}); | ||
var sjs_echo = new sockjs.Server(sockjs_opts); | ||
sjs_echo.on('open', function(conn) { | ||
conn.on('message', function(e) { | ||
conn.send(e.data); | ||
}); | ||
var server = http.createServer(); | ||
echo.installHandlers(server, {prefix:'[/]echo'}); | ||
server.listen(9999, '0.0.0.0'); | ||
``` | ||
Live QUnit tests and smoke tests | ||
-------------------------------- | ||
[SockJS-client](https://github.com/majek/sockjs-client) comes with | ||
some QUnit tests and a few smoke tests that are using SockJS-node. At | ||
the moment they are deployed in few places: | ||
* http://sockjs.popcnt.org/ (hosted in Europe) | ||
* http://sockjs.cloudfoundry.com/ (CloudFoundry, websockets not working) | ||
* https://sockjs.cloudfoundry.com/ (CloudFoundry SSL, websockets not working) | ||
* http://sockjs.herokuapp.com/ (Heroku, websockets not working) | ||
SockJS-node API | ||
--------------- | ||
The API is highly influenced by the | ||
[HTML5 Websockets API](http://dev.w3.org/html5/websockets/). The goal | ||
is to follow it as closely as possible, but we're not there yet. | ||
### Server class | ||
Server class on one side is generating an http handler, compatible | ||
with the common | ||
[Node.js http](http://nodejs.org/docs/v0.4.10/api/http.html#http.createServer) | ||
module. | ||
```javascript | ||
var sjs = new sockjs.Server(options); | ||
``` | ||
Where `options` is a hash which can contain: | ||
<dl> | ||
<dt>sockjs_url (required)</dt> | ||
<dd>Transports which don't support cross-domain communication natively | ||
('eventsource' to name one) use an iframe trick. A simple page is | ||
served from the SockJS server (using its foreign domain) and is | ||
placed in an invisible iframe. Code run from this iframe doesn't | ||
need to worry about cross-domain issues, as it's being run from | ||
domain local to the SockJS server. This iframe also does need to | ||
load SockJS javascript client library, and this option specifies | ||
its url (if you're unsure, point it to | ||
<a href="http://majek.github.com/sockjs-client/sockjs-latest.min.js"> | ||
the latest minified SockJS client release</a>).</dd> | ||
<dt>prefix</dt> | ||
<dd>A url prefix for the server. All http requests which paths begins | ||
with selected prefix will be handled by SockJS. All other requests | ||
will be passed through, to previously registered handlers.</dd> | ||
<dt>disabled_transports</dt> | ||
<dd>A list of streaming transports that should not be handled by the | ||
server. This may be useful, when it's known that the server stands | ||
behind a proxy which doesn't like some streaming transports, for | ||
example websockets. Valid values are: 'websockets', 'eventsource'.</dd> | ||
</dl> | ||
### Server instance | ||
Once you have instantiated `Server` class you can hook it to the | ||
[http server instance](http://nodejs.org/docs/v0.4.10/api/http.html#http.createServer). | ||
```javascript | ||
var http_server = http.createServer(); | ||
sjs.installHandlers(http_server, options); | ||
http_server.listen(...); | ||
``` | ||
Where `options` can overwrite options set by `Server` class | ||
constructor. | ||
`Server` instance is an | ||
[EventEmitter](http://nodejs.org/docs/v0.4.10/api/events.html#events.EventEmitter), | ||
and emits following event: | ||
<dl> | ||
<dt>open(connection)</dt> | ||
<dd>A new connection has been successfully opened.</dd> | ||
</dl> | ||
All http requests that don't go under the path selected by `prefix` | ||
will remain unanswered and will be passed to previously registered | ||
handlers. | ||
### Connection instance | ||
A `Connection` instance has following methods and properties: | ||
<dl> | ||
<dt>readyState</dt> | ||
<dd>A property describing a state of the connecion.</dd> | ||
<dt>send(message)</dt> | ||
<dd>Sends a message over opened connection. It's illegal to send a | ||
message after the connection was closed (either by 'close' method | ||
or 'close' event).</dd> | ||
<dt>close([status], [reason])</dt> | ||
<dd>Asks the remote client to disconnect. 'status' and 'reason' | ||
parameters are optional and can be used to share the reason of | ||
disconnection.</dd> | ||
</dl> | ||
A `Connection` instance is also an | ||
[EventEmitter](http://nodejs.org/docs/v0.4.10/api/events.html#events.EventEmitter), | ||
and emits following events: | ||
<dl> | ||
<dt>message(event)</dt> | ||
<dd>A message arrived on the connection. Data is available at 'event.data'.</dd> | ||
<dt>close(event)</dt> | ||
<dd>Connection was closed. This event is triggered exactly once for | ||
every connection.</dd> | ||
</dl> | ||
For example: | ||
```javascript | ||
sjs.on('open', function(conn) { | ||
console.log('open' + conn); | ||
conn.on('close', function(e) { | ||
console.log('close ' + conn, e); | ||
}); | ||
conn.on('message', function(e) { | ||
console.log('message ' + conn, | ||
e.data); | ||
}); | ||
}); | ||
``` | ||
var normal_handler = function(req, res) { | ||
res.writeHead(404); | ||
res.end("Not found."); | ||
}; | ||
### Footnote | ||
var server = http.createServer(); | ||
server.addListener('request', normal_handler); | ||
server.addListener('upgrade', normal_handler); | ||
A fully working echo server does need a bit more boilerplate (to | ||
handle unanswered requests), here it is: | ||
sjs_echo.installHandlers(server, {prefix:'[/]echo'}); | ||
```javascript | ||
var http = require('http'); | ||
var sockjs = require('sockjs'); | ||
server.listen(9999, '0.0.0.0'); | ||
var sockjs_opts = {sockjs_url: | ||
"http://majek.github.com/sockjs-client/sockjs-latest.min.js"}; | ||
var sjs = new sockjs.Server(sockjs_opts); | ||
sjs.on('open', function(conn) { | ||
conn.on('message', function(e) { | ||
conn.send(e.data); | ||
}); | ||
}); | ||
var server = http.createServer(); | ||
server.addListener('request', function(req, res) { | ||
res.writeHead(404); | ||
res.end('404 not found'); | ||
}); | ||
server.addListener('upgrade', function(req, con) { | ||
con.end(); | ||
}); | ||
sjs.installHandlers(server, {prefix:'[/]echo'}); | ||
server.listen(9999, '0.0.0.0'); | ||
``` | ||
### Examples | ||
If you want to see samples of running code, take a look at: | ||
* [./examples/echo](https://github.com/majek/sockjs-node/tree/master/examples/echo) | ||
directory, which contains a full example of a echo server. | ||
* [SockJS-client tests](https://github.com/majek/sockjs-client/blob/master/tests/sockjs_test_server.js). | ||
Deployment and load balancing | ||
----------------------------- | ||
Often WebSockets don't play nicely with proxies and loadbalancers. | ||
Deploying SockJS server behind nginx or apache could be | ||
painful. | ||
Fortunetely recent versions of an excellent loadbalancer | ||
[HAProxy](http://haproxy.1wt.eu/) are able to proxy WebSocket | ||
connections. We propose to put HAProxy as a front line load balancer | ||
and use it to split SockJS traffic from normal HTTP data. Take a look | ||
at the sample | ||
[SockJS HAProxy configuration](https://github.com/majek/sockjs-node/blob/master/examples/haproxy.cfg). | ||
The config also shows how to use HAproxy balancing to split traffic | ||
between multiple Node.js servers. You can also do balancing using dns | ||
names. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
111634
39
1807
220