1 |
3a515b92
|
cagy
|
# faye-websocket
|
2 |
|
|
|
3 |
|
|
* Travis CI build: [](http://travis-ci.org/faye/faye-websocket-node)
|
5 |
|
|
* Autobahn tests: [server](http://faye.jcoglan.com/autobahn/servers/),
|
6 |
|
|
[client](http://faye.jcoglan.com/autobahn/clients/)
|
7 |
|
|
|
8 |
|
|
This is a general-purpose WebSocket implementation extracted from the
|
9 |
|
|
[Faye](http://faye.jcoglan.com) project. It provides classes for easily building
|
10 |
|
|
WebSocket servers and clients in Node. It does not provide a server itself, but
|
11 |
|
|
rather makes it easy to handle WebSocket connections within an existing
|
12 |
|
|
[Node](http://nodejs.org/) application. It does not provide any abstraction
|
13 |
|
|
other than the standard [WebSocket API](http://dev.w3.org/html5/websockets/).
|
14 |
|
|
|
15 |
|
|
It also provides an abstraction for handling
|
16 |
|
|
[EventSource](http://dev.w3.org/html5/eventsource/) connections, which are
|
17 |
|
|
one-way connections that allow the server to push data to the client. They are
|
18 |
|
|
based on streaming HTTP responses and can be easier to access via proxies than
|
19 |
|
|
WebSockets.
|
20 |
|
|
|
21 |
|
|
|
22 |
|
|
## Installation
|
23 |
|
|
|
24 |
|
|
```
|
25 |
|
|
$ npm install faye-websocket
|
26 |
|
|
```
|
27 |
|
|
|
28 |
|
|
|
29 |
|
|
## Handling WebSocket connections in Node
|
30 |
|
|
|
31 |
|
|
You can handle WebSockets on the server side by listening for HTTP Upgrade
|
32 |
|
|
requests, and creating a new socket for the request. This socket object exposes
|
33 |
|
|
the usual WebSocket methods for receiving and sending messages. For example this
|
34 |
|
|
is how you'd implement an echo server:
|
35 |
|
|
|
36 |
|
|
```js
|
37 |
|
|
var WebSocket = require('faye-websocket'),
|
38 |
|
|
http = require('http');
|
39 |
|
|
|
40 |
|
|
var server = http.createServer();
|
41 |
|
|
|
42 |
|
|
server.on('upgrade', function(request, socket, body) {
|
43 |
|
|
if (WebSocket.isWebSocket(request)) {
|
44 |
|
|
var ws = new WebSocket(request, socket, body);
|
45 |
|
|
|
46 |
|
|
ws.on('message', function(event) {
|
47 |
|
|
ws.send(event.data);
|
48 |
|
|
});
|
49 |
|
|
|
50 |
|
|
ws.on('close', function(event) {
|
51 |
|
|
console.log('close', event.code, event.reason);
|
52 |
|
|
ws = null;
|
53 |
|
|
});
|
54 |
|
|
}
|
55 |
|
|
});
|
56 |
|
|
|
57 |
|
|
server.listen(8000);
|
58 |
|
|
```
|
59 |
|
|
|
60 |
|
|
`WebSocket` objects are also duplex streams, so you could replace the
|
61 |
|
|
`ws.on('message', ...)` line with:
|
62 |
|
|
|
63 |
|
|
```js
|
64 |
|
|
ws.pipe(ws);
|
65 |
|
|
```
|
66 |
|
|
|
67 |
|
|
Note that under certain circumstances (notably a draft-76 client connecting
|
68 |
|
|
through an HTTP proxy), the WebSocket handshake will not be complete after you
|
69 |
|
|
call `new WebSocket()` because the server will not have received the entire
|
70 |
|
|
handshake from the client yet. In this case, calls to `ws.send()` will buffer
|
71 |
|
|
the message in memory until the handshake is complete, at which point any
|
72 |
|
|
buffered messages will be sent to the client.
|
73 |
|
|
|
74 |
|
|
If you need to detect when the WebSocket handshake is complete, you can use the
|
75 |
|
|
`onopen` event.
|
76 |
|
|
|
77 |
|
|
If the connection's protocol version supports it, you can call `ws.ping()` to
|
78 |
|
|
send a ping message and wait for the client's response. This method takes a
|
79 |
|
|
message string, and an optional callback that fires when a matching pong message
|
80 |
|
|
is received. It returns `true` if and only if a ping message was sent. If the
|
81 |
|
|
client does not support ping/pong, this method sends no data and returns
|
82 |
|
|
`false`.
|
83 |
|
|
|
84 |
|
|
```js
|
85 |
|
|
ws.ping('Mic check, one, two', function() {
|
86 |
|
|
// fires when pong is received
|
87 |
|
|
});
|
88 |
|
|
```
|
89 |
|
|
|
90 |
|
|
|
91 |
|
|
## Using the WebSocket client
|
92 |
|
|
|
93 |
|
|
The client supports both the plain-text `ws` protocol and the encrypted `wss`
|
94 |
|
|
protocol, and has exactly the same interface as a socket you would use in a web
|
95 |
|
|
browser. On the wire it identifies itself as `hybi-13`.
|
96 |
|
|
|
97 |
|
|
```js
|
98 |
|
|
var WebSocket = require('faye-websocket'),
|
99 |
|
|
ws = new WebSocket.Client('ws://www.example.com/');
|
100 |
|
|
|
101 |
|
|
ws.on('open', function(event) {
|
102 |
|
|
console.log('open');
|
103 |
|
|
ws.send('Hello, world!');
|
104 |
|
|
});
|
105 |
|
|
|
106 |
|
|
ws.on('message', function(event) {
|
107 |
|
|
console.log('message', event.data);
|
108 |
|
|
});
|
109 |
|
|
|
110 |
|
|
ws.on('close', function(event) {
|
111 |
|
|
console.log('close', event.code, event.reason);
|
112 |
|
|
ws = null;
|
113 |
|
|
});
|
114 |
|
|
```
|
115 |
|
|
|
116 |
|
|
The WebSocket client also lets you inspect the status and headers of the
|
117 |
|
|
handshake response via its `statusCode` and `headers` properties.
|
118 |
|
|
|
119 |
|
|
To connect via a proxy, set the `proxy` option to the HTTP origin of the proxy,
|
120 |
|
|
including any authorization information, custom headers and TLS config you
|
121 |
|
|
require. Only the `origin` setting is required.
|
122 |
|
|
|
123 |
|
|
```js
|
124 |
|
|
var ws = new WebSocket.Client('ws://www.example.com/', [], {
|
125 |
|
|
proxy: {
|
126 |
|
|
origin: 'http://username:password@proxy.example.com',
|
127 |
|
|
headers: {'User-Agent': 'node'},
|
128 |
|
|
tls: {cert: fs.readFileSync('client.crt')}
|
129 |
|
|
}
|
130 |
|
|
});
|
131 |
|
|
```
|
132 |
|
|
|
133 |
|
|
The `tls` value is a Node 'TLS options' object that will be passed to
|
134 |
|
|
[`tls.connect()`](http://nodejs.org/api/tls.html#tls_tls_connect_options_callback).
|
135 |
|
|
|
136 |
|
|
|
137 |
|
|
## Subprotocol negotiation
|
138 |
|
|
|
139 |
|
|
The WebSocket protocol allows peers to select and identify the application
|
140 |
|
|
protocol to use over the connection. On the client side, you can set which
|
141 |
|
|
protocols the client accepts by passing a list of protocol names when you
|
142 |
|
|
construct the socket:
|
143 |
|
|
|
144 |
|
|
```js
|
145 |
|
|
var ws = new WebSocket.Client('ws://www.example.com/', ['irc', 'amqp']);
|
146 |
|
|
```
|
147 |
|
|
|
148 |
|
|
On the server side, you can likewise pass in the list of protocols the server
|
149 |
|
|
supports after the other constructor arguments:
|
150 |
|
|
|
151 |
|
|
```js
|
152 |
|
|
var ws = new WebSocket(request, socket, body, ['irc', 'amqp']);
|
153 |
|
|
```
|
154 |
|
|
|
155 |
|
|
If the client and server agree on a protocol, both the client- and server-side
|
156 |
|
|
socket objects expose the selected protocol through the `ws.protocol` property.
|
157 |
|
|
|
158 |
|
|
|
159 |
|
|
## Protocol extensions
|
160 |
|
|
|
161 |
|
|
faye-websocket is based on the
|
162 |
|
|
[websocket-extensions](https://github.com/faye/websocket-extensions-node)
|
163 |
|
|
framework that allows extensions to be negotiated via the
|
164 |
|
|
`Sec-WebSocket-Extensions` header. To add extensions to a connection, pass an
|
165 |
|
|
array of extensions to the `:extensions` option. For example, to add
|
166 |
|
|
[permessage-deflate](https://github.com/faye/permessage-deflate-node):
|
167 |
|
|
|
168 |
|
|
```js
|
169 |
|
|
var deflate = require('permessage-deflate');
|
170 |
|
|
|
171 |
|
|
var ws = new WebSocket(request, socket, body, [], {extensions: [deflate]});
|
172 |
|
|
```
|
173 |
|
|
|
174 |
|
|
|
175 |
|
|
## Initialization options
|
176 |
|
|
|
177 |
|
|
Both the server- and client-side classes allow an options object to be passed in
|
178 |
|
|
at initialization time, for example:
|
179 |
|
|
|
180 |
|
|
```js
|
181 |
|
|
var ws = new WebSocket(request, socket, body, protocols, options);
|
182 |
|
|
var ws = new WebSocket.Client(url, protocols, options);
|
183 |
|
|
```
|
184 |
|
|
|
185 |
|
|
`protocols` is an array of subprotocols as described above, or `null`.
|
186 |
|
|
`options` is an optional object containing any of these fields:
|
187 |
|
|
|
188 |
|
|
* `extensions` - an array of
|
189 |
|
|
[websocket-extensions](https://github.com/faye/websocket-extensions-node)
|
190 |
|
|
compatible extensions, as described above
|
191 |
|
|
* `headers` - an object containing key-value pairs representing HTTP headers to
|
192 |
|
|
be sent during the handshake process
|
193 |
|
|
* `maxLength` - the maximum allowed size of incoming message frames, in bytes.
|
194 |
|
|
The default value is `2^26 - 1`, or 1 byte short of 64 MiB.
|
195 |
|
|
* `ping` - an integer that sets how often the WebSocket should send ping frames,
|
196 |
|
|
measured in seconds
|
197 |
|
|
|
198 |
|
|
The client accepts some additional options:
|
199 |
|
|
|
200 |
|
|
* `proxy` - settings for a proxy as described above
|
201 |
|
|
* `tls` - a Node 'TLS options' object containing TLS settings for the origin
|
202 |
|
|
server, this will be passed to
|
203 |
|
|
[`tls.connect()`](http://nodejs.org/api/tls.html#tls_tls_connect_options_callback)
|
204 |
|
|
* `ca` - (legacy) a shorthand for passing `{tls: {ca: value}}`
|
205 |
|
|
|
206 |
|
|
|
207 |
|
|
## WebSocket API
|
208 |
|
|
|
209 |
|
|
Both server- and client-side `WebSocket` objects support the following API.
|
210 |
|
|
|
211 |
|
|
* <b>`on('open', function(event) {})`</b> fires when the socket connection is
|
212 |
|
|
established. Event has no attributes.
|
213 |
|
|
* <b>`on('message', function(event) {})`</b> fires when the socket receives a
|
214 |
|
|
message. Event has one attribute, <b>`data`</b>, which is either a `String`
|
215 |
|
|
(for text frames) or a `Buffer` (for binary frames).
|
216 |
|
|
* <b>`on('error', function(event) {})`</b> fires when there is a protocol error
|
217 |
|
|
due to bad data sent by the other peer. This event is purely informational,
|
218 |
|
|
you do not need to implement error recover.
|
219 |
|
|
* <b>`on('close', function(event) {})`</b> fires when either the client or the
|
220 |
|
|
server closes the connection. Event has two optional attributes, <b>`code`</b>
|
221 |
|
|
and <b>`reason`</b>, that expose the status code and message sent by the peer
|
222 |
|
|
that closed the connection.
|
223 |
|
|
* <b>`send(message)`</b> accepts either a `String` or a `Buffer` and sends a
|
224 |
|
|
text or binary message over the connection to the other peer.
|
225 |
|
|
* <b>`ping(message, function() {})`</b> sends a ping frame with an optional
|
226 |
|
|
message and fires the callback when a matching pong is received.
|
227 |
|
|
* <b>`close(code, reason)`</b> closes the connection, sending the given status
|
228 |
|
|
code and reason text, both of which are optional.
|
229 |
|
|
* <b>`version`</b> is a string containing the version of the `WebSocket`
|
230 |
|
|
protocol the connection is using.
|
231 |
|
|
* <b>`protocol`</b> is a string (which may be empty) identifying the subprotocol
|
232 |
|
|
the socket is using.
|
233 |
|
|
|
234 |
|
|
|
235 |
|
|
## Handling EventSource connections in Node
|
236 |
|
|
|
237 |
|
|
EventSource connections provide a very similar interface, although because they
|
238 |
|
|
only allow the server to send data to the client, there is no `onmessage` API.
|
239 |
|
|
EventSource allows the server to push text messages to the client, where each
|
240 |
|
|
message has an optional event-type and ID.
|
241 |
|
|
|
242 |
|
|
```js
|
243 |
|
|
var WebSocket = require('faye-websocket'),
|
244 |
|
|
EventSource = WebSocket.EventSource,
|
245 |
|
|
http = require('http');
|
246 |
|
|
|
247 |
|
|
var server = http.createServer();
|
248 |
|
|
|
249 |
|
|
server.on('request', function(request, response) {
|
250 |
|
|
if (EventSource.isEventSource(request)) {
|
251 |
|
|
var es = new EventSource(request, response);
|
252 |
|
|
console.log('open', es.url, es.lastEventId);
|
253 |
|
|
|
254 |
|
|
// Periodically send messages
|
255 |
|
|
var loop = setInterval(function() { es.send('Hello') }, 1000);
|
256 |
|
|
|
257 |
|
|
es.on('close', function() {
|
258 |
|
|
clearInterval(loop);
|
259 |
|
|
es = null;
|
260 |
|
|
});
|
261 |
|
|
|
262 |
|
|
} else {
|
263 |
|
|
// Normal HTTP request
|
264 |
|
|
response.writeHead(200, {'Content-Type': 'text/plain'});
|
265 |
|
|
response.end('Hello');
|
266 |
|
|
}
|
267 |
|
|
});
|
268 |
|
|
|
269 |
|
|
server.listen(8000);
|
270 |
|
|
```
|
271 |
|
|
|
272 |
|
|
The `send` method takes two optional parameters, `event` and `id`. The default
|
273 |
|
|
event-type is `'message'` with no ID. For example, to send a `notification`
|
274 |
|
|
event with ID `99`:
|
275 |
|
|
|
276 |
|
|
```js
|
277 |
|
|
es.send('Breaking News!', {event: 'notification', id: '99'});
|
278 |
|
|
```
|
279 |
|
|
|
280 |
|
|
The `EventSource` object exposes the following properties:
|
281 |
|
|
|
282 |
|
|
* <b>`url`</b> is a string containing the URL the client used to create the
|
283 |
|
|
EventSource.
|
284 |
|
|
* <b>`lastEventId`</b> is a string containing the last event ID received by the
|
285 |
|
|
client. You can use this when the client reconnects after a dropped connection
|
286 |
|
|
to determine which messages need resending.
|
287 |
|
|
|
288 |
|
|
When you initialize an EventSource with ` new EventSource()`, you can pass
|
289 |
|
|
configuration options after the `response` parameter. Available options are:
|
290 |
|
|
|
291 |
|
|
* <b>`headers`</b> is an object containing custom headers to be set on the
|
292 |
|
|
EventSource response.
|
293 |
|
|
* <b>`retry`</b> is a number that tells the client how long (in seconds) it
|
294 |
|
|
should wait after a dropped connection before attempting to reconnect.
|
295 |
|
|
* <b>`ping`</b> is a number that tells the server how often (in seconds) to send
|
296 |
|
|
'ping' packets to the client to keep the connection open, to defeat timeouts
|
297 |
|
|
set by proxies. The client will ignore these messages.
|
298 |
|
|
|
299 |
|
|
For example, this creates a connection that allows access from any origin, pings
|
300 |
|
|
every 15 seconds and is retryable every 10 seconds if the connection is broken:
|
301 |
|
|
|
302 |
|
|
```js
|
303 |
|
|
var es = new EventSource(request, response, {
|
304 |
|
|
headers: {'Access-Control-Allow-Origin': '*'},
|
305 |
|
|
ping: 15,
|
306 |
|
|
retry: 10
|
307 |
|
|
});
|
308 |
|
|
```
|
309 |
|
|
|
310 |
|
|
You can send a ping message at any time by calling `es.ping()`. Unlike
|
311 |
|
|
WebSocket, the client does not send a response to this; it is merely to send
|
312 |
|
|
some data over the wire to keep the connection alive.
|
313 |
|
|
|
314 |
|
|
|
315 |
|
|
## License
|
316 |
|
|
|
317 |
|
|
(The MIT License)
|
318 |
|
|
|
319 |
|
|
Copyright (c) 2010-2015 James Coglan
|
320 |
|
|
|
321 |
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
322 |
|
|
this software and associated documentation files (the 'Software'), to deal in
|
323 |
|
|
the Software without restriction, including without limitation the rights to
|
324 |
|
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
325 |
|
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
326 |
|
|
subject to the following conditions:
|
327 |
|
|
|
328 |
|
|
The above copyright notice and this permission notice shall be included in all
|
329 |
|
|
copies or substantial portions of the Software.
|
330 |
|
|
|
331 |
|
|
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
332 |
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
333 |
|
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
334 |
|
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
335 |
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
336 |
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|