You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

450 lines
16 KiB

4 years ago
  1. [![NPM version](https://badge.fury.io/js/sockjs.svg)](http://badge.fury.io/js/sockjs)
  2. SockJS family:
  3. * [SockJS-client](https://github.com/sockjs/sockjs-client) JavaScript client library
  4. * [SockJS-node](https://github.com/sockjs/sockjs-node) Node.js server
  5. * [SockJS-erlang](https://github.com/sockjs/sockjs-erlang) Erlang server
  6. * [SockJS-tornado](https://github.com/MrJoes/sockjs-tornado) Python/Tornado server
  7. * [vert.x](https://github.com/eclipse/vert.x) Java/vert.x server
  8. Work in progress:
  9. * [SockJS-ruby](https://github.com/nyarly/sockjs-ruby)
  10. * [SockJS-netty](https://github.com/cgbystrom/sockjs-netty)
  11. * [SockJS-gevent](https://github.com/sdiehl/sockjs-gevent) ([and a fork](https://github.com/njoyce/sockjs-gevent))
  12. * [pyramid-SockJS](https://github.com/fafhrd91/pyramid_sockjs)
  13. * [wildcloud-websockets](https://github.com/wildcloud/wildcloud-websockets)
  14. * [SockJS-cyclone](https://github.com/flaviogrossi/sockjs-cyclone)
  15. * [SockJS-twisted](https://github.com/Fugiman/sockjs-twisted/)
  16. * [wai-SockJS](https://github.com/Palmik/wai-sockjs)
  17. * [SockJS-perl](https://github.com/vti/sockjs-perl)
  18. * [SockJS-go](https://github.com/igm/sockjs-go/)
  19. What is SockJS?
  20. ===============
  21. SockJS is a JavaScript library (for browsers) that provides a WebSocket-like
  22. object. SockJS gives you a coherent, cross-browser, Javascript API
  23. which creates a low latency, full duplex, cross-domain communication
  24. channel between the browser and the web server, with WebSockets or without.
  25. This necessitates the use of a server, which this is one version of, for Node.js.
  26. SockJS-node server
  27. ==================
  28. SockJS-node is a Node.js server side counterpart of
  29. [SockJS-client browser library](https://github.com/sockjs/sockjs-client)
  30. written in CoffeeScript.
  31. To install `sockjs-node` run:
  32. npm install sockjs
  33. A simplified echo SockJS server could look more or less like:
  34. ```javascript
  35. var http = require('http');
  36. var sockjs = require('sockjs');
  37. var echo = sockjs.createServer({ sockjs_url: 'http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js' });
  38. echo.on('connection', function(conn) {
  39. conn.on('data', function(message) {
  40. conn.write(message);
  41. });
  42. conn.on('close', function() {});
  43. });
  44. var server = http.createServer();
  45. echo.installHandlers(server, {prefix:'/echo'});
  46. server.listen(9999, '0.0.0.0');
  47. ```
  48. (Take look at
  49. [examples](https://github.com/sockjs/sockjs-node/tree/master/examples/echo)
  50. directory for a complete version.)
  51. Subscribe to
  52. [SockJS mailing list](https://groups.google.com/forum/#!forum/sockjs) for
  53. discussions and support.
  54. SockJS-node API
  55. ---------------
  56. The API design is based on common Node APIs like the
  57. [Streams API](http://nodejs.org/docs/v0.5.8/api/streams.html) or the
  58. [Http.Server API](http://nodejs.org/docs/v0.5.8/api/http.html#http.Server).
  59. ### Server class
  60. SockJS module is generating a `Server` class, similar to
  61. [Node.js http.createServer](http://nodejs.org/docs/v0.5.8/api/http.html#http.createServer)
  62. module.
  63. ```javascript
  64. var sockjs_server = sockjs.createServer(options);
  65. ```
  66. Where `options` is a hash which can contain:
  67. <dl>
  68. <dt>sockjs_url (string, required)</dt>
  69. <dd>Transports which don't support cross-domain communication natively
  70. ('eventsource' to name one) use an iframe trick. A simple page is
  71. served from the SockJS server (using its foreign domain) and is
  72. placed in an invisible iframe. Code run from this iframe doesn't
  73. need to worry about cross-domain issues, as it's being run from
  74. domain local to the SockJS server. This iframe also does need to
  75. load SockJS javascript client library, and this option lets you specify
  76. its url (if you're unsure, point it to
  77. <a href="http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js">
  78. the latest minified SockJS client release</a>, this is the default).
  79. You must explicitly specify this url on the server side for security
  80. reasons - we don't want the possibility of running any foreign
  81. javascript within the SockJS domain (aka cross site scripting attack).
  82. Also, sockjs javascript library is probably already cached by the
  83. browser - it makes sense to reuse the sockjs url you're using in
  84. normally.</dd>
  85. <dt>prefix (string regex)</dt>
  86. <dd>A url prefix for the server. All http requests which paths begins
  87. with selected prefix will be handled by SockJS. All other requests
  88. will be passed through, to previously registered handlers.</dd>
  89. <dt>response_limit (integer)</dt>
  90. <dd>Most streaming transports save responses on the client side and
  91. don't free memory used by delivered messages. Such transports need
  92. to be garbage-collected once in a while. `response_limit` sets
  93. a minimum number of bytes that can be send over a single http streaming
  94. request before it will be closed. After that client needs to open
  95. new request. Setting this value to one effectively disables
  96. streaming and will make streaming transports to behave like polling
  97. transports. The default value is 128K.</dd>
  98. <dt>websocket (boolean)</dt>
  99. <dd>Some load balancers don't support websockets. This option can be used
  100. to disable websockets support by the server. By default websockets are
  101. enabled.</dd>
  102. <dt>jsessionid (boolean or function)</dt>
  103. <dd>Some hosting providers enable sticky sessions only to requests that
  104. have JSESSIONID cookie set. This setting controls if the server should
  105. set this cookie to a dummy value. By default setting JSESSIONID cookie
  106. is disabled. More sophisticated behaviour can be achieved by supplying
  107. a function.</dd>
  108. <dt>log (function(severity, message))</dt>
  109. <dd>It's quite useful, especially for debugging, to see some messages
  110. printed by a SockJS-node library. This is done using this `log`
  111. function, which is by default set to `console.log`. If this
  112. behaviour annoys you for some reason, override `log` setting with a
  113. custom handler. The following `severities` are used: `debug`
  114. (miscellaneous logs), `info` (requests logs), `error` (serious
  115. errors, consider filing an issue).</dd>
  116. <dt>heartbeat_delay (milliseconds)</dt>
  117. <dd>In order to keep proxies and load balancers from closing long
  118. running http requests we need to pretend that the connection is
  119. active and send a heartbeat packet once in a while. This setting
  120. controls how often this is done. By default a heartbeat packet is
  121. sent every 25 seconds. </dd>
  122. <dt>disconnect_delay (milliseconds)</dt>
  123. <dd>The server sends a `close` event when a client receiving
  124. connection have not been seen for a while. This delay is configured
  125. by this setting. By default the `close` event will be emitted when a
  126. receiving connection wasn't seen for 5 seconds. </dd>
  127. <dt>disable_cors (boolean)</dt>
  128. <dd>Enabling this option will prevent
  129. <a href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing">CORS</a>
  130. headers from being included in the HTTP response. Can be used when the
  131. sockjs client is known to be connecting from the same origin as the
  132. sockjs server.</dd>
  133. </dl>
  134. ### Server instance
  135. Once you have create `Server` instance you can hook it to the
  136. [http.Server instance](http://nodejs.org/docs/v0.5.8/api/http.html#http.createServer).
  137. ```javascript
  138. var http_server = http.createServer();
  139. sockjs_server.installHandlers(http_server, options);
  140. http_server.listen(...);
  141. ```
  142. Where `options` can overshadow options given when creating `Server`
  143. instance.
  144. `Server` instance is an
  145. [EventEmitter](http://nodejs.org/docs/v0.4.10/api/events.html#events.EventEmitter),
  146. and emits following event:
  147. <dl>
  148. <dt>Event: connection (connection)</dt>
  149. <dd>A new connection has been successfully opened.</dd>
  150. </dl>
  151. All http requests that don't go under the path selected by `prefix`
  152. will remain unanswered and will be passed to previously registered
  153. handlers. You must install your custom http handlers before calling
  154. `installHandlers`.
  155. ### Connection instance
  156. A `Connection` instance supports
  157. [Node Stream API](http://nodejs.org/docs/v0.5.8/api/streams.html) and
  158. has following methods and properties:
  159. <dl>
  160. <dt>Property: readable (boolean)</dt>
  161. <dd>Is the stream readable?</dd>
  162. <dt>Property: writable (boolean)</dt>
  163. <dd>Is the stream writable?</dd>
  164. <dt>Property: remoteAddress (string)</dt>
  165. <dd>Last known IP address of the client.</dd>
  166. <dt>Property: remotePort (number)</dt>
  167. <dd>Last known port number of the client.</dd>
  168. <dt>Property: address (object)</dt>
  169. <dd>Hash with 'address' and 'port' fields.</dd>
  170. <dt>Property: headers (object)</dt>
  171. <dd>Hash containing various headers copied from last receiving request
  172. on that connection. Exposed headers include: `origin`, `referer`
  173. and `x-forwarded-for` (and friends). We explicitly do not grant
  174. access to `cookie` header, as using it may easily lead to security
  175. issues (for details read the section "Authorisation").</dd>
  176. <dt>Property: url (string)</dt>
  177. <dd><a href="http://nodejs.org/docs/v0.4.10/api/http.html#request.url">Url</a>
  178. property copied from last request.</dd>
  179. <dt>Property: pathname (string)</dt>
  180. <dd>`pathname` from parsed url, for convenience.</dd>
  181. <dt>Property: prefix (string)</dt>
  182. <dd>Prefix of the url on which the request was handled.</dd>
  183. <dt>Property: protocol (string)</dt>
  184. <dd>Protocol used by the connection. Keep in mind that some protocols
  185. are indistinguishable - for example "xhr-polling" and "xdr-polling".</dd>
  186. <dt>Property: readyState (integer)</dt>
  187. <dd>Current state of the connection:
  188. 0-connecting, 1-open, 2-closing, 3-closed.</dd>
  189. <dt>write(message)</dt>
  190. <dd>Sends a message over opened connection. A message must be a
  191. non-empty string. It's illegal to send a message after the connection was
  192. closed (either after 'close' or 'end' method or 'close' event).</dd>
  193. <dt>close([code], [reason])</dt>
  194. <dd>Asks the remote client to disconnect. 'code' and 'reason'
  195. parameters are optional and can be used to share the reason of
  196. disconnection.</dd>
  197. <dt>end()</dt>
  198. <dd>Asks the remote client to disconnect with default 'code' and
  199. 'reason' values.</dd>
  200. </dl>
  201. A `Connection` instance emits the following events:
  202. <dl>
  203. <dt>Event: data (message)</dt>
  204. <dd>A message arrived on the connection. Message is a unicode
  205. string.</dd>
  206. <dt>Event: close ()</dt>
  207. <dd>Connection was closed. This event is triggered exactly once for
  208. every connection.</dd>
  209. </dl>
  210. For example:
  211. ```javascript
  212. sockjs_server.on('connection', function(conn) {
  213. console.log('connection' + conn);
  214. conn.on('close', function() {
  215. console.log('close ' + conn);
  216. });
  217. conn.on('data', function(message) {
  218. console.log('message ' + conn,
  219. message);
  220. });
  221. });
  222. ```
  223. ### Footnote
  224. A fully working echo server does need a bit more boilerplate (to
  225. handle requests unanswered by SockJS), see the
  226. [`echo` example](https://github.com/sockjs/sockjs-node/tree/master/examples/echo)
  227. for a complete code.
  228. ### Examples
  229. If you want to see samples of running code, take a look at:
  230. * [./examples/echo](https://github.com/sockjs/sockjs-node/tree/master/examples/echo)
  231. directory, which contains a full example of a echo server.
  232. * [./examples/test_server](https://github.com/sockjs/sockjs-node/tree/master/examples/test_server) a standard SockJS test server.
  233. Connecting to SockJS-node without the client
  234. --------------------------------------------
  235. Although the main point of SockJS it to enable browser-to-server
  236. connectivity, it is possible to connect to SockJS from an external
  237. application. Any SockJS server complying with 0.3 protocol does
  238. support a raw WebSocket url. The raw WebSocket url for the test server
  239. looks like:
  240. * ws://localhost:8081/echo/websocket
  241. You can connect any WebSocket RFC 6455 compliant WebSocket client to
  242. this url. This can be a command line client, external application,
  243. third party code or even a browser (though I don't know why you would
  244. want to do so).
  245. Note: This endpoint will *not send any heartbeat packets*.
  246. Deployment and load balancing
  247. -----------------------------
  248. There are two issues that need to be considered when planning a
  249. non-trivial SockJS-node deployment: WebSocket-compatible load balancer
  250. and sticky sessions (aka session affinity).
  251. ### WebSocket compatible load balancer
  252. Often WebSockets don't play nicely with proxies and load balancers.
  253. Deploying a SockJS server behind Nginx or Apache could be painful.
  254. Fortunately recent versions of an excellent load balancer
  255. [HAProxy](http://haproxy.1wt.eu/) are able to proxy WebSocket
  256. connections. We propose to put HAProxy as a front line load balancer
  257. and use it to split SockJS traffic from normal HTTP data. Take a look
  258. at the sample
  259. [SockJS HAProxy configuration](https://github.com/sockjs/sockjs-node/blob/master/examples/haproxy.cfg).
  260. The config also shows how to use HAproxy balancing to split traffic
  261. between multiple Node.js servers. You can also do balancing using dns
  262. names.
  263. ### Sticky sessions
  264. If you plan deploying more than one SockJS server, you must make sure
  265. that all HTTP requests for a single session will hit the same server.
  266. SockJS has two mechanisms that can be useful to achieve that:
  267. * Urls are prefixed with server and session id numbers, like:
  268. `/resource/<server_number>/<session_id>/transport`. This is
  269. useful for load balancers that support prefix-based affinity
  270. (HAProxy does).
  271. * `JSESSIONID` cookie is being set by SockJS-node. Many load
  272. balancers turn on sticky sessions if that cookie is set. This
  273. technique is derived from Java applications, where sticky sessions
  274. are often necessary. HAProxy does support this method, as well as
  275. some hosting providers, for example CloudFoundry. In order to
  276. enable this method on the client side, please supply a
  277. `cookie:true` option to SockJS constructor.
  278. Development and testing
  279. -----------------------
  280. If you want to work on SockJS-node source code, you need to clone the
  281. git repo and follow these steps. First you need to install
  282. dependencies:
  283. cd sockjs-node
  284. npm install
  285. npm install --dev
  286. ln -s .. node_modules/sockjs
  287. You're ready to compile CoffeeScript:
  288. make build
  289. If compilation succeeds you may want to test if your changes pass all
  290. the tests. Currently, there are two separate test suites. For both of
  291. them you need to start a SockJS-node test server (by default listening
  292. on port 8081):
  293. make test_server
  294. ### SockJS-protocol Python tests
  295. To run it run something like:
  296. cd sockjs-protocol
  297. make test_deps
  298. ./venv/bin/python sockjs-protocol.py
  299. For details see
  300. [SockJS-protocol README](https://github.com/sockjs/sockjs-protocol#readme).
  301. ### SockJS-client QUnit tests
  302. You need to start a second web server (by default listening on 8080)
  303. that is serving various static html and javascript files:
  304. cd sockjs-client
  305. make test
  306. At that point you should have two web servers running: sockjs-node on
  307. 8081 and sockjs-client on 8080. When you open the browser on
  308. [http://localhost:8080/](http://localhost:8080/) you should be able
  309. run the QUnit tests against your sockjs-node server.
  310. For details see
  311. [SockJS-client README](https://github.com/sockjs/sockjs-client#readme).
  312. Additionally, if you're doing more serious development consider using
  313. `make serve`, which will automatically the server when you modify the
  314. source code.
  315. Various issues and design considerations
  316. ----------------------------------------
  317. ### Authorisation
  318. SockJS-node does not expose cookies to the application. This is done
  319. deliberately as using cookie-based authorisation with SockJS simply
  320. doesn't make sense and will lead to security issues.
  321. Cookies are a contract between a browser and an http server, and are
  322. identified by a domain name. If a browser has a cookie set for
  323. particular domain, it will pass it as a part of all http requests to
  324. the host. But to get various transports working, SockJS uses a middleman
  325. - an iframe hosted from target SockJS domain. That means the server
  326. will receive requests from the iframe, and not from the real
  327. domain. The domain of an iframe is the same as the SockJS domain. The
  328. problem is that any website can embed the iframe and communicate with
  329. it - and request establishing SockJS connection. Using cookies for
  330. authorisation in this scenario will result in granting full access to
  331. SockJS communication with your website from any website. This is a
  332. classic CSRF attack.
  333. Basically - cookies are not suited for SockJS model. If you want to
  334. authorise a session - provide a unique token on a page, send it as a
  335. first thing over SockJS connection and validate it on the server
  336. side. In essence, this is how cookies work.
  337. ### Deploying SockJS on Heroku
  338. Long polling is known to cause problems on Heroku, but
  339. [workaround for SockJS is available](https://github.com/sockjs/sockjs-node/issues/57#issuecomment-5242187).