Comparing version 2.3.1 to 3.0.0
@@ -32,9 +32,7 @@ 'use strict'; | ||
* @param {(String|Buffer|ArrayBuffer|Buffer[])} data The received data | ||
* @param {Boolean} isBinary Specifies if `data` is binary | ||
* @param {WebSocket} target A reference to the target to which the event was dispatched | ||
*/ | ||
constructor (data, isBinary, target) { | ||
constructor (data, target) { | ||
super('message', target); | ||
this.binary = isBinary; // non-standard. | ||
this.data = data; | ||
@@ -103,4 +101,4 @@ } | ||
function onMessage (data, flags) { | ||
listener.call(this, new MessageEvent(data, !!flags.binary, this)); | ||
function onMessage (data) { | ||
listener.call(this, new MessageEvent(data, this)); | ||
} | ||
@@ -107,0 +105,0 @@ |
@@ -21,9 +21,12 @@ 'use strict'; | ||
constructor (options, isServer, maxPayload) { | ||
this._maxPayload = maxPayload | 0; | ||
this._options = options || {}; | ||
this._threshold = this._options.threshold !== undefined | ||
? this._options.threshold | ||
: 1024; | ||
this._isServer = !!isServer; | ||
this._deflate = null; | ||
this._inflate = null; | ||
this._deflate = null; | ||
this.params = null; | ||
this._maxPayload = maxPayload || 0; | ||
this.threshold = this._options.threshold === undefined ? 1024 : this._options.threshold; | ||
} | ||
@@ -30,0 +33,0 @@ |
@@ -38,25 +38,25 @@ /*! | ||
constructor (extensions, maxPayload, binaryType) { | ||
this.binaryType = binaryType || constants.BINARY_TYPES[0]; | ||
this.extensions = extensions || {}; | ||
this.maxPayload = maxPayload | 0; | ||
this._binaryType = binaryType || constants.BINARY_TYPES[0]; | ||
this._extensions = extensions || {}; | ||
this._maxPayload = maxPayload | 0; | ||
this.bufferedBytes = 0; | ||
this.buffers = []; | ||
this._bufferedBytes = 0; | ||
this._buffers = []; | ||
this.compressed = false; | ||
this.payloadLength = 0; | ||
this.fragmented = 0; | ||
this.masked = false; | ||
this.fin = false; | ||
this.mask = null; | ||
this.opcode = 0; | ||
this._compressed = false; | ||
this._payloadLength = 0; | ||
this._fragmented = 0; | ||
this._masked = false; | ||
this._fin = false; | ||
this._mask = null; | ||
this._opcode = 0; | ||
this.totalPayloadLength = 0; | ||
this.messageLength = 0; | ||
this.fragments = []; | ||
this._totalPayloadLength = 0; | ||
this._messageLength = 0; | ||
this._fragments = []; | ||
this.cleanupCallback = null; | ||
this.hadError = false; | ||
this.dead = false; | ||
this.loop = false; | ||
this._cleanupCallback = null; | ||
this._hadError = false; | ||
this._dead = false; | ||
this._loop = false; | ||
@@ -69,3 +69,3 @@ this.onmessage = null; | ||
this.state = GET_INFO; | ||
this._state = GET_INFO; | ||
} | ||
@@ -85,9 +85,9 @@ | ||
this.bufferedBytes -= bytes; | ||
this._bufferedBytes -= bytes; | ||
if (bytes === this.buffers[0].length) return this.buffers.shift(); | ||
if (bytes === this._buffers[0].length) return this._buffers.shift(); | ||
if (bytes < this.buffers[0].length) { | ||
dst = this.buffers[0].slice(0, bytes); | ||
this.buffers[0] = this.buffers[0].slice(bytes); | ||
if (bytes < this._buffers[0].length) { | ||
dst = this._buffers[0].slice(0, bytes); | ||
this._buffers[0] = this._buffers[0].slice(bytes); | ||
return dst; | ||
@@ -99,11 +99,11 @@ } | ||
while (bytes > 0) { | ||
l = this.buffers[0].length; | ||
l = this._buffers[0].length; | ||
if (bytes >= l) { | ||
this.buffers[0].copy(dst, offset); | ||
this._buffers[0].copy(dst, offset); | ||
offset += l; | ||
this.buffers.shift(); | ||
this._buffers.shift(); | ||
} else { | ||
this.buffers[0].copy(dst, offset, 0, bytes); | ||
this.buffers[0] = this.buffers[0].slice(bytes); | ||
this._buffers[0].copy(dst, offset, 0, bytes); | ||
this._buffers[0] = this._buffers[0].slice(bytes); | ||
} | ||
@@ -126,6 +126,6 @@ | ||
hasBufferedBytes (n) { | ||
if (this.bufferedBytes >= n) return true; | ||
if (this._bufferedBytes >= n) return true; | ||
this.loop = false; | ||
if (this.dead) this.cleanup(this.cleanupCallback); | ||
this._loop = false; | ||
if (this._dead) this.cleanup(this._cleanupCallback); | ||
return false; | ||
@@ -140,6 +140,6 @@ } | ||
add (data) { | ||
if (this.dead) return; | ||
if (this._dead) return; | ||
this.bufferedBytes += data.length; | ||
this.buffers.push(data); | ||
this._bufferedBytes += data.length; | ||
this._buffers.push(data); | ||
this.startLoop(); | ||
@@ -154,6 +154,6 @@ } | ||
startLoop () { | ||
this.loop = true; | ||
this._loop = true; | ||
while (this.loop) { | ||
switch (this.state) { | ||
while (this._loop) { | ||
switch (this._state) { | ||
case GET_INFO: | ||
@@ -175,3 +175,3 @@ this.getInfo(); | ||
default: // `INFLATING` | ||
this.loop = false; | ||
this._loop = false; | ||
} | ||
@@ -198,3 +198,3 @@ } | ||
if (compressed && !this.extensions[PerMessageDeflate.extensionName]) { | ||
if (compressed && !this._extensions[PerMessageDeflate.extensionName]) { | ||
this.error(new Error('RSV1 must be clear'), 1002); | ||
@@ -204,7 +204,7 @@ return; | ||
this.fin = (buf[0] & 0x80) === 0x80; | ||
this.opcode = buf[0] & 0x0f; | ||
this.payloadLength = buf[1] & 0x7f; | ||
this._fin = (buf[0] & 0x80) === 0x80; | ||
this._opcode = buf[0] & 0x0f; | ||
this._payloadLength = buf[1] & 0x7f; | ||
if (this.opcode === 0x00) { | ||
if (this._opcode === 0x00) { | ||
if (compressed) { | ||
@@ -215,17 +215,17 @@ this.error(new Error('RSV1 must be clear'), 1002); | ||
if (!this.fragmented) { | ||
this.error(new Error(`invalid opcode: ${this.opcode}`), 1002); | ||
if (!this._fragmented) { | ||
this.error(new Error(`invalid opcode: ${this._opcode}`), 1002); | ||
return; | ||
} else { | ||
this.opcode = this.fragmented; | ||
this._opcode = this._fragmented; | ||
} | ||
} else if (this.opcode === 0x01 || this.opcode === 0x02) { | ||
if (this.fragmented) { | ||
this.error(new Error(`invalid opcode: ${this.opcode}`), 1002); | ||
} else if (this._opcode === 0x01 || this._opcode === 0x02) { | ||
if (this._fragmented) { | ||
this.error(new Error(`invalid opcode: ${this._opcode}`), 1002); | ||
return; | ||
} | ||
this.compressed = compressed; | ||
} else if (this.opcode > 0x07 && this.opcode < 0x0b) { | ||
if (!this.fin) { | ||
this._compressed = compressed; | ||
} else if (this._opcode > 0x07 && this._opcode < 0x0b) { | ||
if (!this._fin) { | ||
this.error(new Error('FIN must be set'), 1002); | ||
@@ -240,3 +240,3 @@ return; | ||
if (this.payloadLength > 0x7d) { | ||
if (this._payloadLength > 0x7d) { | ||
this.error(new Error('invalid payload length'), 1002); | ||
@@ -246,12 +246,12 @@ return; | ||
} else { | ||
this.error(new Error(`invalid opcode: ${this.opcode}`), 1002); | ||
this.error(new Error(`invalid opcode: ${this._opcode}`), 1002); | ||
return; | ||
} | ||
if (!this.fin && !this.fragmented) this.fragmented = this.opcode; | ||
if (!this._fin && !this._fragmented) this._fragmented = this._opcode; | ||
this.masked = (buf[1] & 0x80) === 0x80; | ||
this._masked = (buf[1] & 0x80) === 0x80; | ||
if (this.payloadLength === 126) this.state = GET_PAYLOAD_LENGTH_16; | ||
else if (this.payloadLength === 127) this.state = GET_PAYLOAD_LENGTH_64; | ||
if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16; | ||
else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64; | ||
else this.haveLength(); | ||
@@ -268,3 +268,3 @@ } | ||
this.payloadLength = this.readBuffer(2).readUInt16BE(0, true); | ||
this._payloadLength = this.readBuffer(2).readUInt16BE(0, true); | ||
this.haveLength(); | ||
@@ -293,3 +293,3 @@ } | ||
this.payloadLength = (num * Math.pow(2, 32)) + buf.readUInt32BE(4, true); | ||
this._payloadLength = (num * Math.pow(2, 32)) + buf.readUInt32BE(4, true); | ||
this.haveLength(); | ||
@@ -304,8 +304,8 @@ } | ||
haveLength () { | ||
if (this.opcode < 0x08 && this.maxPayloadExceeded(this.payloadLength)) { | ||
if (this._opcode < 0x08 && this.maxPayloadExceeded(this._payloadLength)) { | ||
return; | ||
} | ||
if (this.masked) this.state = GET_MASK; | ||
else this.state = GET_DATA; | ||
if (this._masked) this._state = GET_MASK; | ||
else this._state = GET_DATA; | ||
} | ||
@@ -321,4 +321,4 @@ | ||
this.mask = this.readBuffer(4); | ||
this.state = GET_DATA; | ||
this._mask = this.readBuffer(4); | ||
this._state = GET_DATA; | ||
} | ||
@@ -334,13 +334,13 @@ | ||
if (this.payloadLength) { | ||
if (!this.hasBufferedBytes(this.payloadLength)) return; | ||
if (this._payloadLength) { | ||
if (!this.hasBufferedBytes(this._payloadLength)) return; | ||
data = this.readBuffer(this.payloadLength); | ||
if (this.masked) bufferUtil.unmask(data, this.mask); | ||
data = this.readBuffer(this._payloadLength); | ||
if (this._masked) bufferUtil.unmask(data, this._mask); | ||
} | ||
if (this.opcode > 0x07) { | ||
if (this._opcode > 0x07) { | ||
this.controlMessage(data); | ||
} else if (this.compressed) { | ||
this.state = INFLATING; | ||
} else if (this._compressed) { | ||
this._state = INFLATING; | ||
this.decompress(data); | ||
@@ -359,5 +359,5 @@ } else if (this.pushFragment(data)) { | ||
decompress (data) { | ||
const extension = this.extensions[PerMessageDeflate.extensionName]; | ||
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; | ||
extension.decompress(data, this.fin, (err, buf) => { | ||
perMessageDeflate.decompress(data, this._fin, (err, buf) => { | ||
if (err) { | ||
@@ -379,17 +379,17 @@ this.error(err, err.closeCode === 1009 ? 1009 : 1007); | ||
dataMessage () { | ||
if (this.fin) { | ||
const messageLength = this.messageLength; | ||
const fragments = this.fragments; | ||
if (this._fin) { | ||
const messageLength = this._messageLength; | ||
const fragments = this._fragments; | ||
this.totalPayloadLength = 0; | ||
this.messageLength = 0; | ||
this.fragmented = 0; | ||
this.fragments = []; | ||
this._totalPayloadLength = 0; | ||
this._messageLength = 0; | ||
this._fragmented = 0; | ||
this._fragments = []; | ||
if (this.opcode === 2) { | ||
if (this._opcode === 2) { | ||
var data; | ||
if (this.binaryType === 'nodebuffer') { | ||
if (this._binaryType === 'nodebuffer') { | ||
data = toBuffer(fragments, messageLength); | ||
} else if (this.binaryType === 'arraybuffer') { | ||
} else if (this._binaryType === 'arraybuffer') { | ||
data = toArrayBuffer(toBuffer(fragments, messageLength)); | ||
@@ -400,3 +400,3 @@ } else { | ||
this.onmessage(data, { masked: this.masked, binary: true }); | ||
this.onmessage(data); | ||
} else { | ||
@@ -410,7 +410,7 @@ const buf = toBuffer(fragments, messageLength); | ||
this.onmessage(buf.toString(), { masked: this.masked }); | ||
this.onmessage(buf.toString()); | ||
} | ||
} | ||
this.state = GET_INFO; | ||
this._state = GET_INFO; | ||
} | ||
@@ -425,7 +425,7 @@ | ||
controlMessage (data) { | ||
if (this.opcode === 0x08) { | ||
if (this._opcode === 0x08) { | ||
if (data.length === 0) { | ||
this.onclose(1000, '', { masked: this.masked }); | ||
this.loop = false; | ||
this.cleanup(this.cleanupCallback); | ||
this.onclose(1000, ''); | ||
this._loop = false; | ||
this.cleanup(this._cleanupCallback); | ||
} else if (data.length === 1) { | ||
@@ -448,5 +448,5 @@ this.error(new Error('invalid payload length'), 1002); | ||
this.onclose(code, buf.toString(), { masked: this.masked }); | ||
this.loop = false; | ||
this.cleanup(this.cleanupCallback); | ||
this.onclose(code, buf.toString()); | ||
this._loop = false; | ||
this.cleanup(this._cleanupCallback); | ||
} | ||
@@ -457,8 +457,6 @@ | ||
const flags = { masked: this.masked, binary: true }; | ||
if (this._opcode === 0x09) this.onping(data); | ||
else this.onpong(data); | ||
if (this.opcode === 0x09) this.onping(data, flags); | ||
else this.onpong(data, flags); | ||
this.state = GET_INFO; | ||
this._state = GET_INFO; | ||
} | ||
@@ -475,5 +473,5 @@ | ||
this.onerror(err, code); | ||
this.hadError = true; | ||
this.loop = false; | ||
this.cleanup(this.cleanupCallback); | ||
this._hadError = true; | ||
this._loop = false; | ||
this.cleanup(this._cleanupCallback); | ||
} | ||
@@ -488,8 +486,8 @@ | ||
maxPayloadExceeded (length) { | ||
if (length === 0 || this.maxPayload < 1) return false; | ||
if (length === 0 || this._maxPayload < 1) return false; | ||
const fullLength = this.totalPayloadLength + length; | ||
const fullLength = this._totalPayloadLength + length; | ||
if (fullLength <= this.maxPayload) { | ||
this.totalPayloadLength = fullLength; | ||
if (fullLength <= this._maxPayload) { | ||
this._totalPayloadLength = fullLength; | ||
return false; | ||
@@ -513,7 +511,7 @@ } | ||
const totalLength = this.messageLength + fragment.length; | ||
const totalLength = this._messageLength + fragment.length; | ||
if (this.maxPayload < 1 || totalLength <= this.maxPayload) { | ||
this.messageLength = totalLength; | ||
this.fragments.push(fragment); | ||
if (this._maxPayload < 1 || totalLength <= this._maxPayload) { | ||
this._messageLength = totalLength; | ||
this._fragments.push(fragment); | ||
return true; | ||
@@ -533,13 +531,13 @@ } | ||
cleanup (cb) { | ||
this.dead = true; | ||
this._dead = true; | ||
if (!this.hadError && (this.loop || this.state === INFLATING)) { | ||
this.cleanupCallback = cb; | ||
if (!this._hadError && (this._loop || this._state === INFLATING)) { | ||
this._cleanupCallback = cb; | ||
} else { | ||
this.extensions = null; | ||
this.fragments = null; | ||
this.buffers = null; | ||
this.mask = null; | ||
this._extensions = null; | ||
this._fragments = null; | ||
this._buffers = null; | ||
this._mask = null; | ||
this.cleanupCallback = null; | ||
this._cleanupCallback = null; | ||
this.onmessage = null; | ||
@@ -546,0 +544,0 @@ this.onclose = null; |
@@ -29,11 +29,11 @@ /*! | ||
constructor (socket, extensions) { | ||
this.perMessageDeflate = (extensions || {})[PerMessageDeflate.extensionName]; | ||
this._extensions = extensions || {}; | ||
this._socket = socket; | ||
this.firstFragment = true; | ||
this.compress = false; | ||
this._firstFragment = true; | ||
this._compress = false; | ||
this.bufferedBytes = 0; | ||
this.deflating = false; | ||
this.queue = []; | ||
this._bufferedBytes = 0; | ||
this._deflating = false; | ||
this._queue = []; | ||
@@ -127,3 +127,3 @@ this.onerror = null; | ||
if (this.deflating) { | ||
if (this._deflating) { | ||
this.enqueue([this.doClose, buf, mask, cb]); | ||
@@ -174,3 +174,3 @@ } else { | ||
if (this.deflating) { | ||
if (this._deflating) { | ||
this.enqueue([this.doPing, data, mask, readOnly]); | ||
@@ -221,3 +221,3 @@ } else { | ||
if (this.deflating) { | ||
if (this._deflating) { | ||
this.enqueue([this.doPong, data, mask, readOnly]); | ||
@@ -275,8 +275,10 @@ } else { | ||
if (this.firstFragment) { | ||
this.firstFragment = false; | ||
if (rsv1 && this.perMessageDeflate) { | ||
rsv1 = data.length >= this.perMessageDeflate.threshold; | ||
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; | ||
if (this._firstFragment) { | ||
this._firstFragment = false; | ||
if (rsv1 && perMessageDeflate) { | ||
rsv1 = data.length >= perMessageDeflate._threshold; | ||
} | ||
this.compress = rsv1; | ||
this._compress = rsv1; | ||
} else { | ||
@@ -287,5 +289,5 @@ rsv1 = false; | ||
if (options.fin) this.firstFragment = true; | ||
if (options.fin) this._firstFragment = true; | ||
if (this.perMessageDeflate) { | ||
if (perMessageDeflate) { | ||
const opts = { | ||
@@ -299,6 +301,6 @@ fin: options.fin, | ||
if (this.deflating) { | ||
this.enqueue([this.dispatch, data, this.compress, opts, cb]); | ||
if (this._deflating) { | ||
this.enqueue([this.dispatch, data, this._compress, opts, cb]); | ||
} else { | ||
this.dispatch(data, this.compress, opts, cb); | ||
this.dispatch(data, this._compress, opts, cb); | ||
} | ||
@@ -336,4 +338,6 @@ } else { | ||
this.deflating = true; | ||
this.perMessageDeflate.compress(data, options.fin, (err, buf) => { | ||
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; | ||
this._deflating = true; | ||
perMessageDeflate.compress(data, options.fin, (err, buf) => { | ||
if (err) { | ||
@@ -347,3 +351,3 @@ if (cb) cb(err); | ||
this.sendFrame(Sender.frame(buf, options), cb); | ||
this.deflating = false; | ||
this._deflating = false; | ||
this.dequeue(); | ||
@@ -359,6 +363,6 @@ }); | ||
dequeue () { | ||
while (!this.deflating && this.queue.length) { | ||
const params = this.queue.shift(); | ||
while (!this._deflating && this._queue.length) { | ||
const params = this._queue.shift(); | ||
this.bufferedBytes -= params[1].length; | ||
this._bufferedBytes -= params[1].length; | ||
params[0].apply(this, params.slice(1)); | ||
@@ -375,4 +379,4 @@ } | ||
enqueue (params) { | ||
this.bufferedBytes += params[1].length; | ||
this.queue.push(params); | ||
this._bufferedBytes += params[1].length; | ||
this._queue.push(params); | ||
} | ||
@@ -379,0 +383,0 @@ |
@@ -68,3 +68,3 @@ /*! | ||
if (Array.isArray(address)) { | ||
initAsServerClient.call(this, address[0], address[1], address[2], options); | ||
initAsServerClient.call(this, address[0], address[1], options); | ||
} else { | ||
@@ -87,3 +87,3 @@ initAsClient.call(this, address, protocols, options); | ||
if (this._socket) { | ||
amount = this._socket.bufferSize + this._sender.bufferedBytes; | ||
amount = this._socket.bufferSize + this._sender._bufferedBytes; | ||
} | ||
@@ -111,3 +111,3 @@ return amount; | ||
// | ||
if (this._receiver) this._receiver.binaryType = type; | ||
if (this._receiver) this._receiver._binaryType = type; | ||
} | ||
@@ -126,3 +126,3 @@ | ||
this._receiver = new Receiver(this.extensions, this.maxPayload, this.binaryType); | ||
this._receiver = new Receiver(this.extensions, this._maxPayload, this.binaryType); | ||
this._sender = new Sender(socket, this.extensions); | ||
@@ -150,8 +150,8 @@ this._ultron = new Ultron(socket); | ||
// receiver event handlers | ||
this._receiver.onmessage = (data, flags) => this.emit('message', data, flags); | ||
this._receiver.onping = (data, flags) => { | ||
this._receiver.onmessage = (data) => this.emit('message', data); | ||
this._receiver.onping = (data) => { | ||
this.pong(data, !this._isServer, true); | ||
this.emit('ping', data, flags); | ||
this.emit('ping', data); | ||
}; | ||
this._receiver.onpong = (data, flags) => this.emit('pong', data, flags); | ||
this._receiver.onpong = (data) => this.emit('pong', data); | ||
this._receiver.onclose = (code, reason) => { | ||
@@ -463,9 +463,8 @@ this._closeMessage = reason; | ||
*/ | ||
function initAsServerClient (req, socket, head, options) { | ||
function initAsServerClient (socket, head, options) { | ||
this.protocolVersion = options.protocolVersion; | ||
this._maxPayload = options.maxPayload; | ||
this.extensions = options.extensions; | ||
this.maxPayload = options.maxPayload; | ||
this.protocol = options.protocol; | ||
this.upgradeReq = req; | ||
this._isServer = true; | ||
@@ -472,0 +471,0 @@ |
@@ -50,3 +50,3 @@ /*! | ||
maxPayload: 100 * 1024 * 1024, | ||
perMessageDeflate: true, | ||
perMessageDeflate: false, | ||
handleProtocols: null, | ||
@@ -89,4 +89,3 @@ clientTracking: true, | ||
this.handleUpgrade(req, socket, head, (client) => { | ||
this.emit(`connection${req.url}`, client); | ||
this.emit('connection', client); | ||
this.emit('connection', client, req); | ||
}); | ||
@@ -98,3 +97,2 @@ }); | ||
this.options = options; | ||
this.path = options.path; | ||
} | ||
@@ -262,3 +260,3 @@ | ||
const client = new WebSocket([req, socket, head], null, { | ||
const client = new WebSocket([socket, head], null, { | ||
maxPayload: this.options.maxPayload, | ||
@@ -265,0 +263,0 @@ protocolVersion: version, |
{ | ||
"name": "ws", | ||
"version": "2.3.1", | ||
"version": "3.0.0", | ||
"description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js", | ||
@@ -41,6 +41,6 @@ "keywords": [ | ||
"eslint-plugin-standard": "~3.0.0", | ||
"mocha": "~3.2.0", | ||
"nyc": "~10.2.0", | ||
"mocha": "~3.4.1", | ||
"nyc": "~10.3.0", | ||
"utf-8-validate": "~3.0.0" | ||
} | ||
} |
129
README.md
@@ -19,2 +19,24 @@ # ws: a Node.js WebSocket library | ||
## Table of Contents | ||
* [Protocol support](#protocol-support) | ||
* [Installing](#installing) | ||
+ [Opt-in for performance and spec compliance](#opt-in-for-performance-and-spec-compliance) | ||
* [API docs](#api-docs) | ||
* [WebSocket compression](#websocket-compression) | ||
* [Usage examples](#usage-examples) | ||
+ [Sending and receiving text data](#sending-and-receiving-text-data) | ||
+ [Sending binary data](#sending-binary-data) | ||
+ [Server example](#server-example) | ||
+ [Broadcast example](#broadcast-example) | ||
+ [ExpressJS example](#expressjs-example) | ||
+ [echo.websocket.org demo](#echowebsocketorg-demo) | ||
+ [Other examples](#other-examples) | ||
* [Error handling best practices](#error-handling-best-practices) | ||
* [FAQ](#faq) | ||
+ [How to get the IP address of the client?](#how-to-get-the-ip-address-of-the-client) | ||
+ [How to detect and close broken connections?](#how-to-detect-and-close-broken-connections) | ||
* [Changelog](#changelog) | ||
* [License](#license) | ||
## Protocol support | ||
@@ -44,6 +66,5 @@ | ||
## API Docs | ||
## API docs | ||
See [`/doc/ws.md`](https://github.com/websockets/ws/blob/master/doc/ws.md) | ||
for Node.js-like docs for the ws classes. | ||
See [`/doc/ws.md`](./doc/ws.md) for Node.js-like docs for the ws classes. | ||
@@ -57,8 +78,9 @@ ## WebSocket compression | ||
The extension is enabled by default but adds a significant overhead in terms of | ||
performance and memory comsumption. We suggest to use WebSocket compression | ||
only if it is really needed. | ||
The extension is disabled by default on the server and enabled by default on | ||
the client. It adds a significant overhead in terms of performance and memory | ||
comsumption so we suggest to enable it only if it is really needed. | ||
To disable the extension you can set the `perMessageDeflate` option to `false`. | ||
On the server: | ||
The client will only use the extension if it is supported and enabled on the | ||
server. To always disable the extension on the client set the | ||
`perMessageDeflate` option to `false`. | ||
@@ -68,13 +90,2 @@ ```js | ||
const wss = new WebSocket.Server({ | ||
perMessageDeflate: false, | ||
port: 8080 | ||
}); | ||
``` | ||
On the client: | ||
```js | ||
const WebSocket = require('ws'); | ||
const ws = new WebSocket('ws://www.host.com/path', { | ||
@@ -98,5 +109,4 @@ perMessageDeflate: false | ||
ws.on('message', function incoming(data, flags) { | ||
// flags.binary will be set if a binary data is received. | ||
// flags.masked will be set if the data was masked. | ||
ws.on('message', function incoming(data) { | ||
console.log(data); | ||
}); | ||
@@ -184,6 +194,6 @@ ``` | ||
wss.on('connection', function connection(ws) { | ||
const location = url.parse(ws.upgradeReq.url, true); | ||
wss.on('connection', function connection(ws, req) { | ||
const location = url.parse(req.url, true); | ||
// You might use location.query.access_token to authenticate or share sessions | ||
// or ws.upgradeReq.headers.cookie (see http://stackoverflow.com/a/16395220/151312) | ||
// or req.headers.cookie (see http://stackoverflow.com/a/16395220/151312) | ||
@@ -220,4 +230,4 @@ ws.on('message', function incoming(message) { | ||
ws.on('message', function incoming(data, flags) { | ||
console.log(`Roundtrip time: ${Date.now() - data} ms`, flags); | ||
ws.on('message', function incoming(data) { | ||
console.log(`Roundtrip time: ${Date.now() - data} ms`); | ||
@@ -258,6 +268,66 @@ setTimeout(function timeout() { | ||
## FAQ | ||
### How to get the IP address of the client? | ||
The remote IP address can be obtained from the raw socket. | ||
```js | ||
const WebSocket = require('ws'); | ||
const wss = new WebSocket.Server({ port: 8080 }); | ||
wss.on('connection', function connection(ws, req) { | ||
const ip = req.connection.remoteAddress; | ||
}); | ||
``` | ||
When the server runs behing a proxy like NGINX, the de-facto standard is to use | ||
the `X-Forwarded-For` header. | ||
```js | ||
wss.on('connection', function connection(ws, req) { | ||
const ip = req.headers['x-forwarded-for']; | ||
}); | ||
``` | ||
### How to detect and close broken connections? | ||
Sometimes the link between the server and the client can be interrupted in a | ||
way that keeps both the server and the client unware of the broken state of the | ||
connection (e.g. when pulling the cord). | ||
In these cases ping messages can be used as a means to verify that the remote | ||
endpoint is still responsive. | ||
```js | ||
const WebSocket = require('ws'); | ||
const wss = new WebSocket.Server({ port: 8080 }); | ||
function heartbeat() { | ||
this.isAlive = true; | ||
} | ||
wss.on('connection', function connection(ws) { | ||
ws.isAlive = true; | ||
ws.on('pong', heartbeat); | ||
}); | ||
const interval = setInterval(function ping() { | ||
wss.clients.forEach(function each(ws) { | ||
if (ws.isAlive === false) return ws.terminate(); | ||
ws.isAlive = false; | ||
ws.ping('', false, true); | ||
}); | ||
}, 30000); | ||
``` | ||
Pong messages are automatically sent in reponse to ping messages as required | ||
by the spec. | ||
## Changelog | ||
We're using the GitHub [`releases`](https://github.com/websockets/ws/releases) | ||
for changelog entries. | ||
We're using the GitHub [releases][changelog] for changelog entries. | ||
@@ -269,1 +339,2 @@ ## License | ||
[permessage-deflate]: https://tools.ietf.org/html/rfc7692 | ||
[changelog]: https://github.com/websockets/ws/releases |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
85113
332
2371