Projekt

Obecné

Profil

Stáhnout (16.3 KB) Statistiky
| Větev: | Revize:
1
[![NPM version](https://badge.fury.io/js/sockjs.svg)](http://badge.fury.io/js/sockjs)
2

    
3
SockJS family:
4

    
5
  * [SockJS-client](https://github.com/sockjs/sockjs-client) JavaScript client library
6
  * [SockJS-node](https://github.com/sockjs/sockjs-node) Node.js server
7
  * [SockJS-erlang](https://github.com/sockjs/sockjs-erlang) Erlang server
8
  * [SockJS-tornado](https://github.com/MrJoes/sockjs-tornado) Python/Tornado server
9
  * [vert.x](https://github.com/eclipse/vert.x) Java/vert.x server
10

    
11
Work in progress:
12

    
13
  * [SockJS-ruby](https://github.com/nyarly/sockjs-ruby)
14
  * [SockJS-netty](https://github.com/cgbystrom/sockjs-netty)
15
  * [SockJS-gevent](https://github.com/sdiehl/sockjs-gevent) ([and a fork](https://github.com/njoyce/sockjs-gevent))
16
  * [pyramid-SockJS](https://github.com/fafhrd91/pyramid_sockjs)
17
  * [wildcloud-websockets](https://github.com/wildcloud/wildcloud-websockets)
18
  * [SockJS-cyclone](https://github.com/flaviogrossi/sockjs-cyclone)
19
  * [SockJS-twisted](https://github.com/Fugiman/sockjs-twisted/)
20
  * [wai-SockJS](https://github.com/Palmik/wai-sockjs)
21
  * [SockJS-perl](https://github.com/vti/sockjs-perl)
22
  * [SockJS-go](https://github.com/igm/sockjs-go/)
23

    
24
What is SockJS?
25
===============
26

    
27
SockJS is a JavaScript library (for browsers) that provides a WebSocket-like
28
object. SockJS gives you a coherent, cross-browser, Javascript API
29
which creates a low latency, full duplex, cross-domain communication
30
channel between the browser and the web server, with WebSockets or without.
31
This necessitates the use of a server, which this is one version of, for Node.js.
32

    
33

    
34
SockJS-node server
35
==================
36

    
37
SockJS-node is a Node.js server side counterpart of
38
[SockJS-client browser library](https://github.com/sockjs/sockjs-client)
39
written in CoffeeScript.
40

    
41
To install `sockjs-node` run:
42

    
43
    npm install sockjs
44

    
45
A simplified echo SockJS server could look more or less like:
46

    
47
```javascript
48
var http = require('http');
49
var sockjs = require('sockjs');
50

    
51
var echo = sockjs.createServer({ sockjs_url: 'http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js' });
52
echo.on('connection', function(conn) {
53
    conn.on('data', function(message) {
54
        conn.write(message);
55
    });
56
    conn.on('close', function() {});
57
});
58

    
59
var server = http.createServer();
60
echo.installHandlers(server, {prefix:'/echo'});
61
server.listen(9999, '0.0.0.0');
62
```
63

    
64
(Take look at
65
[examples](https://github.com/sockjs/sockjs-node/tree/master/examples/echo)
66
directory for a complete version.)
67

    
68
Subscribe to
69
[SockJS mailing list](https://groups.google.com/forum/#!forum/sockjs) for
70
discussions and support.
71

    
72

    
73
SockJS-node API
74
---------------
75

    
76
The API design is based on common Node APIs like the
77
[Streams API](http://nodejs.org/docs/v0.5.8/api/streams.html) or the
78
[Http.Server API](http://nodejs.org/docs/v0.5.8/api/http.html#http.Server).
79

    
80
### Server class
81

    
82
SockJS module is generating a `Server` class, similar to
83
[Node.js http.createServer](http://nodejs.org/docs/v0.5.8/api/http.html#http.createServer)
84
module.
85

    
86
```javascript
87
var sockjs_server = sockjs.createServer(options);
88
```
89

    
90
Where `options` is a hash which can contain:
91

    
92
<dl>
93
<dt>sockjs_url (string, required)</dt>
94
<dd>Transports which don't support cross-domain communication natively
95
   ('eventsource' to name one) use an iframe trick. A simple page is
96
   served from the SockJS server (using its foreign domain) and is
97
   placed in an invisible iframe. Code run from this iframe doesn't
98
   need to worry about cross-domain issues, as it's being run from
99
   domain local to the SockJS server. This iframe also does need to
100
   load SockJS javascript client library, and this option lets you specify
101
   its url (if you're unsure, point it to
102
   <a href="http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js">
103
   the latest minified SockJS client release</a>, this is the default).
104
   You must explicitly specify this url on the server side for security
105
   reasons - we don't want the possibility of running any foreign
106
   javascript within the SockJS domain (aka cross site scripting attack).
107
   Also, sockjs javascript library is probably already cached by the
108
   browser - it makes sense to reuse the sockjs url you're using in
109
   normally.</dd>
110

    
111
<dt>prefix (string regex)</dt>
112
<dd>A url prefix for the server. All http requests which paths begins
113
   with selected prefix will be handled by SockJS. All other requests
114
   will be passed through, to previously registered handlers.</dd>
115

    
116
<dt>response_limit (integer)</dt>
117
<dd>Most streaming transports save responses on the client side and
118
   don't free memory used by delivered messages. Such transports need
119
   to be garbage-collected once in a while. `response_limit` sets
120
   a minimum number of bytes that can be send over a single http streaming
121
   request before it will be closed. After that client needs to open
122
   new request. Setting this value to one effectively disables
123
   streaming and will make streaming transports to behave like polling
124
   transports. The default value is 128K.</dd>
125

    
126
<dt>websocket (boolean)</dt>
127
<dd>Some load balancers don't support websockets. This option can be used
128
   to disable websockets support by the server. By default websockets are
129
   enabled.</dd>
130

    
131
<dt>jsessionid (boolean or function)</dt>
132
<dd>Some hosting providers enable sticky sessions only to requests that
133
  have JSESSIONID cookie set. This setting controls if the server should
134
  set this cookie to a dummy value. By default setting JSESSIONID cookie
135
  is disabled. More sophisticated behaviour can be achieved by supplying
136
  a function.</dd>
137

    
138
<dt>log (function(severity, message))</dt>
139
<dd>It's quite useful, especially for debugging, to see some messages
140
  printed by a SockJS-node library. This is done using this `log`
141
  function, which is by default set to `console.log`. If this
142
  behaviour annoys you for some reason, override `log` setting with a
143
  custom handler.  The following `severities` are used: `debug`
144
  (miscellaneous logs), `info` (requests logs), `error` (serious
145
  errors, consider filing an issue).</dd>
146

    
147
<dt>heartbeat_delay (milliseconds)</dt>
148
<dd>In order to keep proxies and load balancers from closing long
149
  running http requests we need to pretend that the connection is
150
  active and send a heartbeat packet once in a while. This setting
151
  controls how often this is done. By default a heartbeat packet is
152
  sent every 25 seconds.  </dd>
153

    
154
<dt>disconnect_delay (milliseconds)</dt>
155
<dd>The server sends a `close` event when a client receiving
156
  connection have not been seen for a while. This delay is configured
157
  by this setting. By default the `close` event will be emitted when a
158
  receiving connection wasn't seen for 5 seconds.  </dd>
159

    
160
<dt>disable_cors (boolean)</dt>
161
<dd>Enabling this option will prevent
162
  <a href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing">CORS</a>
163
  headers from being included in the HTTP response. Can be used when the
164
  sockjs client is known to be connecting from the same origin as the 
165
  sockjs server.</dd>
166
</dl>
167

    
168

    
169
### Server instance
170

    
171
Once you have create `Server` instance you can hook it to the
172
[http.Server instance](http://nodejs.org/docs/v0.5.8/api/http.html#http.createServer).
173

    
174
```javascript
175
var http_server = http.createServer();
176
sockjs_server.installHandlers(http_server, options);
177
http_server.listen(...);
178
```
179

    
180
Where `options` can overshadow options given when creating `Server`
181
instance.
182

    
183
`Server` instance is an
184
[EventEmitter](http://nodejs.org/docs/v0.4.10/api/events.html#events.EventEmitter),
185
and emits following event:
186

    
187
<dl>
188
<dt>Event: connection (connection)</dt>
189
<dd>A new connection has been successfully opened.</dd>
190
</dl>
191

    
192
All http requests that don't go under the path selected by `prefix`
193
will remain unanswered and will be passed to previously registered
194
handlers. You must install your custom http handlers before calling
195
`installHandlers`.
196

    
197
### Connection instance
198

    
199
A `Connection` instance supports
200
[Node Stream API](http://nodejs.org/docs/v0.5.8/api/streams.html) and
201
has following methods and properties:
202

    
203
<dl>
204
<dt>Property: readable (boolean)</dt>
205
<dd>Is the stream readable?</dd>
206

    
207
<dt>Property: writable (boolean)</dt>
208
<dd>Is the stream writable?</dd>
209

    
210
<dt>Property: remoteAddress (string)</dt>
211
<dd>Last known IP address of the client.</dd>
212

    
213
<dt>Property: remotePort (number)</dt>
214
<dd>Last known port number of the client.</dd>
215

    
216
<dt>Property: address (object)</dt>
217
<dd>Hash with 'address' and 'port' fields.</dd>
218

    
219
<dt>Property: headers (object)</dt>
220
<dd>Hash containing various headers copied from last receiving request
221
    on that connection. Exposed headers include: `origin`, `referer`
222
    and `x-forwarded-for` (and friends). We explicitly do not grant
223
    access to `cookie` header, as using it may easily lead to security
224
    issues (for details read the section "Authorisation").</dd>
225

    
226
<dt>Property: url (string)</dt>
227
<dd><a href="http://nodejs.org/docs/v0.4.10/api/http.html#request.url">Url</a>
228
    property copied from last request.</dd>
229

    
230
<dt>Property: pathname (string)</dt>
231
<dd>`pathname` from parsed url, for convenience.</dd>
232

    
233
<dt>Property: prefix (string)</dt>
234
<dd>Prefix of the url on which the request was handled.</dd>
235

    
236
<dt>Property: protocol (string)</dt>
237
<dd>Protocol used by the connection. Keep in mind that some protocols
238
   are indistinguishable - for example "xhr-polling" and "xdr-polling".</dd>
239

    
240
<dt>Property: readyState (integer)</dt>
241
<dd>Current state of the connection:
242
   0-connecting, 1-open, 2-closing, 3-closed.</dd>
243

    
244
<dt>write(message)</dt>
245
<dd>Sends a message over opened connection. A message must be a
246
  non-empty string. It's illegal to send a message after the connection was
247
  closed (either after 'close' or 'end' method or 'close' event).</dd>
248

    
249
<dt>close([code], [reason])</dt>
250
<dd>Asks the remote client to disconnect. 'code' and 'reason'
251
   parameters are optional and can be used to share the reason of
252
   disconnection.</dd>
253

    
254
<dt>end()</dt>
255
<dd>Asks the remote client to disconnect with default 'code' and
256
   'reason' values.</dd>
257

    
258
</dl>
259

    
260
A `Connection` instance emits the following events:
261

    
262
<dl>
263
<dt>Event: data (message)</dt>
264
<dd>A message arrived on the connection. Message is a unicode
265
  string.</dd>
266

    
267
<dt>Event: close ()</dt>
268
<dd>Connection was closed. This event is triggered exactly once for
269
   every connection.</dd>
270
</dl>
271

    
272
For example:
273

    
274
```javascript
275
sockjs_server.on('connection', function(conn) {
276
    console.log('connection' + conn);
277
    conn.on('close', function() {
278
        console.log('close ' + conn);
279
    });
280
    conn.on('data', function(message) {
281
        console.log('message ' + conn,
282
                    message);
283
    });
284
});
285
```
286

    
287
### Footnote
288

    
289
A fully working echo server does need a bit more boilerplate (to
290
handle requests unanswered by SockJS), see the
291
[`echo` example](https://github.com/sockjs/sockjs-node/tree/master/examples/echo)
292
for a complete code.
293

    
294
### Examples
295

    
296
If you want to see samples of running code, take a look at:
297

    
298
 * [./examples/echo](https://github.com/sockjs/sockjs-node/tree/master/examples/echo)
299
   directory, which contains a full example of a echo server.
300
 * [./examples/test_server](https://github.com/sockjs/sockjs-node/tree/master/examples/test_server) a standard SockJS test server.
301

    
302

    
303
Connecting to SockJS-node without the client
304
--------------------------------------------
305

    
306
Although the main point of SockJS it to enable browser-to-server
307
connectivity, it is possible to connect to SockJS from an external
308
application. Any SockJS server complying with 0.3 protocol does
309
support a raw WebSocket url. The raw WebSocket url for the test server
310
looks like:
311

    
312
 * ws://localhost:8081/echo/websocket
313

    
314
You can connect any WebSocket RFC 6455 compliant WebSocket client to
315
this url. This can be a command line client, external application,
316
third party code or even a browser (though I don't know why you would
317
want to do so).
318

    
319
Note: This endpoint will *not send any heartbeat packets*.
320

    
321

    
322
Deployment and load balancing
323
-----------------------------
324

    
325
There are two issues that need to be considered when planning a
326
non-trivial SockJS-node deployment: WebSocket-compatible load balancer
327
and sticky sessions (aka session affinity).
328

    
329
### WebSocket compatible load balancer
330

    
331
Often WebSockets don't play nicely with proxies and load balancers.
332
Deploying a SockJS server behind Nginx or Apache could be painful.
333

    
334
Fortunately recent versions of an excellent load balancer
335
[HAProxy](http://haproxy.1wt.eu/) are able to proxy WebSocket
336
connections. We propose to put HAProxy as a front line load balancer
337
and use it to split SockJS traffic from normal HTTP data. Take a look
338
at the sample
339
[SockJS HAProxy configuration](https://github.com/sockjs/sockjs-node/blob/master/examples/haproxy.cfg).
340

    
341
The config also shows how to use HAproxy balancing to split traffic
342
between multiple Node.js servers. You can also do balancing using dns
343
names.
344

    
345
### Sticky sessions
346

    
347
If you plan deploying more than one SockJS server, you must make sure
348
that all HTTP requests for a single session will hit the same server.
349
SockJS has two mechanisms that can be useful to achieve that:
350

    
351
 * Urls are prefixed with server and session id numbers, like:
352
   `/resource/<server_number>/<session_id>/transport`.  This is
353
   useful for load balancers that support prefix-based affinity
354
   (HAProxy does).
355
 * `JSESSIONID` cookie is being set by SockJS-node. Many load
356
   balancers turn on sticky sessions if that cookie is set. This
357
   technique is derived from Java applications, where sticky sessions
358
   are often necessary. HAProxy does support this method, as well as
359
   some hosting providers, for example CloudFoundry.  In order to
360
   enable this method on the client side, please supply a
361
   `cookie:true` option to SockJS constructor.
362

    
363

    
364
Development and testing
365
-----------------------
366

    
367
If you want to work on SockJS-node source code, you need to clone the
368
git repo and follow these steps. First you need to install
369
dependencies:
370

    
371
    cd sockjs-node
372
    npm install
373
    npm install --dev
374
    ln -s .. node_modules/sockjs
375

    
376
You're ready to compile CoffeeScript:
377

    
378
    make build
379

    
380
If compilation succeeds you may want to test if your changes pass all
381
the tests. Currently, there are two separate test suites. For both of
382
them you need to start a SockJS-node test server (by default listening
383
on port 8081):
384

    
385
    make test_server
386

    
387
### SockJS-protocol Python tests
388

    
389
To run it run something like:
390

    
391
    cd sockjs-protocol
392
    make test_deps
393
    ./venv/bin/python sockjs-protocol.py
394

    
395
For details see
396
[SockJS-protocol README](https://github.com/sockjs/sockjs-protocol#readme).
397

    
398
### SockJS-client QUnit tests
399

    
400
You need to start a second web server (by default listening on 8080)
401
that is serving various static html and javascript files:
402

    
403
    cd sockjs-client
404
    make test
405

    
406
At that point you should have two web servers running: sockjs-node on
407
8081 and sockjs-client on 8080. When you open the browser on
408
[http://localhost:8080/](http://localhost:8080/) you should be able
409
run the QUnit tests against your sockjs-node server.
410

    
411
For details see
412
[SockJS-client README](https://github.com/sockjs/sockjs-client#readme).
413

    
414
Additionally, if you're doing more serious development consider using
415
`make serve`, which will automatically the server when you modify the
416
source code.
417

    
418

    
419
Various issues and design considerations
420
----------------------------------------
421

    
422
### Authorisation
423

    
424
SockJS-node does not expose cookies to the application. This is done
425
deliberately as using cookie-based authorisation with SockJS simply
426
doesn't make sense and will lead to security issues.
427

    
428
Cookies are a contract between a browser and an http server, and are
429
identified by a domain name. If a browser has a cookie set for
430
particular domain, it will pass it as a part of all http requests to
431
the host. But to get various transports working, SockJS uses a middleman
432
- an iframe hosted from target SockJS domain. That means the server
433
will receive requests from the iframe, and not from the real
434
domain. The domain of an iframe is the same as the SockJS domain. The
435
problem is that any website can embed the iframe and communicate with
436
it - and request establishing SockJS connection. Using cookies for
437
authorisation in this scenario will result in granting full access to
438
SockJS communication with your website from any website. This is a
439
classic CSRF attack.
440

    
441
Basically - cookies are not suited for SockJS model. If you want to
442
authorise a session - provide a unique token on a page, send it as a
443
first thing over SockJS connection and validate it on the server
444
side. In essence, this is how cookies work.
445

    
446

    
447
### Deploying SockJS on Heroku
448

    
449
Long polling is known to cause problems on Heroku, but
450
[workaround for SockJS is available](https://github.com/sockjs/sockjs-node/issues/57#issuecomment-5242187).
(5-5/7)