diff options
author | Florian Dold <florian.dold@gmail.com> | 2016-11-16 01:59:39 +0100 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2016-11-16 02:00:31 +0100 |
commit | bd65bb67e25a79b019d745b7262b2008ce2adb15 (patch) | |
tree | 89e1b032103a63737f1a703e6a943832ef261704 /node_modules/ws/lib/WebSocketServer.js | |
parent | f91466595b651721690133f58ab37f977539e95b (diff) |
incrementally verify denoms
The denominations are not stored in a separate object store.
Diffstat (limited to 'node_modules/ws/lib/WebSocketServer.js')
-rw-r--r-- | node_modules/ws/lib/WebSocketServer.js | 554 |
1 files changed, 0 insertions, 554 deletions
diff --git a/node_modules/ws/lib/WebSocketServer.js b/node_modules/ws/lib/WebSocketServer.js deleted file mode 100644 index 92077cd5a..000000000 --- a/node_modules/ws/lib/WebSocketServer.js +++ /dev/null @@ -1,554 +0,0 @@ -/*! - * ws: a node.js websocket client - * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com> - * MIT Licensed - */ - -var util = require('util') - , events = require('events') - , http = require('http') - , crypto = require('crypto') - , Options = require('options') - , WebSocket = require('./WebSocket') - , Extensions = require('./Extensions') - , PerMessageDeflate = require('./PerMessageDeflate') - , tls = require('tls') - , url = require('url'); - -/** - * WebSocket Server implementation - */ - -function WebSocketServer(options, callback) { - if (this instanceof WebSocketServer === false) { - return new WebSocketServer(options, callback); - } - - events.EventEmitter.call(this); - - options = new Options({ - host: '0.0.0.0', - port: null, - server: null, - verifyClient: null, - handleProtocols: null, - path: null, - noServer: false, - disableHixie: false, - clientTracking: true, - perMessageDeflate: true, - maxPayload: 100 * 1024 * 1024 - }).merge(options); - - if (!options.isDefinedAndNonNull('port') && !options.isDefinedAndNonNull('server') && !options.value.noServer) { - throw new TypeError('`port` or a `server` must be provided'); - } - - var self = this; - - if (options.isDefinedAndNonNull('port')) { - this._server = http.createServer(function (req, res) { - var body = http.STATUS_CODES[426]; - res.writeHead(426, { - 'Content-Length': body.length, - 'Content-Type': 'text/plain' - }); - res.end(body); - }); - this._server.allowHalfOpen = false; - this._server.listen(options.value.port, options.value.host, callback); - this._closeServer = function() { if (self._server) self._server.close(); }; - } - else if (options.value.server) { - this._server = options.value.server; - if (options.value.path) { - // take note of the path, to avoid collisions when multiple websocket servers are - // listening on the same http server - if (this._server._webSocketPaths && options.value.server._webSocketPaths[options.value.path]) { - throw new Error('two instances of WebSocketServer cannot listen on the same http server path'); - } - if (typeof this._server._webSocketPaths !== 'object') { - this._server._webSocketPaths = {}; - } - this._server._webSocketPaths[options.value.path] = 1; - } - } - if (this._server) { - this._onceServerListening = function() { self.emit('listening'); }; - this._server.once('listening', this._onceServerListening); - } - - if (typeof this._server != 'undefined') { - this._onServerError = function(error) { self.emit('error', error) }; - this._server.on('error', this._onServerError); - this._onServerUpgrade = function(req, socket, upgradeHead) { - //copy upgradeHead to avoid retention of large slab buffers used in node core - var head = new Buffer(upgradeHead.length); - upgradeHead.copy(head); - - self.handleUpgrade(req, socket, head, function(client) { - self.emit('connection'+req.url, client); - self.emit('connection', client); - }); - }; - this._server.on('upgrade', this._onServerUpgrade); - } - - this.options = options.value; - this.path = options.value.path; - this.clients = []; -} - -/** - * Inherits from EventEmitter. - */ - -util.inherits(WebSocketServer, events.EventEmitter); - -/** - * Immediately shuts down the connection. - * - * @api public - */ - -WebSocketServer.prototype.close = function(callback) { - // terminate all associated clients - var error = null; - try { - for (var i = 0, l = this.clients.length; i < l; ++i) { - this.clients[i].terminate(); - } - } - catch (e) { - error = e; - } - - // remove path descriptor, if any - if (this.path && this._server._webSocketPaths) { - delete this._server._webSocketPaths[this.path]; - if (Object.keys(this._server._webSocketPaths).length == 0) { - delete this._server._webSocketPaths; - } - } - - // close the http server if it was internally created - try { - if (typeof this._closeServer !== 'undefined') { - this._closeServer(); - } - } - finally { - if (this._server) { - this._server.removeListener('listening', this._onceServerListening); - this._server.removeListener('error', this._onServerError); - this._server.removeListener('upgrade', this._onServerUpgrade); - } - delete this._server; - } - if(callback) - callback(error); - else if(error) - throw error; -} - -/** - * Handle a HTTP Upgrade request. - * - * @api public - */ - -WebSocketServer.prototype.handleUpgrade = function(req, socket, upgradeHead, cb) { - // check for wrong path - if (this.options.path) { - var u = url.parse(req.url); - if (u && u.pathname !== this.options.path) return; - } - - if (typeof req.headers.upgrade === 'undefined' || req.headers.upgrade.toLowerCase() !== 'websocket') { - abortConnection(socket, 400, 'Bad Request'); - return; - } - - if (req.headers['sec-websocket-key1']) handleHixieUpgrade.apply(this, arguments); - else handleHybiUpgrade.apply(this, arguments); -} - -module.exports = WebSocketServer; - -/** - * Entirely private apis, - * which may or may not be bound to a sepcific WebSocket instance. - */ - -function handleHybiUpgrade(req, socket, upgradeHead, cb) { - // handle premature socket errors - var errorHandler = function() { - try { socket.destroy(); } catch (e) {} - } - socket.on('error', errorHandler); - - // verify key presence - if (!req.headers['sec-websocket-key']) { - abortConnection(socket, 400, 'Bad Request'); - return; - } - - // verify version - var version = parseInt(req.headers['sec-websocket-version']); - if ([8, 13].indexOf(version) === -1) { - abortConnection(socket, 400, 'Bad Request'); - return; - } - - // verify protocol - var protocols = req.headers['sec-websocket-protocol']; - - // verify client - var origin = version < 13 ? - req.headers['sec-websocket-origin'] : - req.headers['origin']; - - // handle extensions offer - var extensionsOffer = Extensions.parse(req.headers['sec-websocket-extensions']); - - // handler to call when the connection sequence completes - var self = this; - var completeHybiUpgrade2 = function(protocol) { - - // calc key - var key = req.headers['sec-websocket-key']; - var shasum = crypto.createHash('sha1'); - shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); - key = shasum.digest('base64'); - - var headers = [ - 'HTTP/1.1 101 Switching Protocols' - , 'Upgrade: websocket' - , 'Connection: Upgrade' - , 'Sec-WebSocket-Accept: ' + key - ]; - - if (typeof protocol != 'undefined') { - headers.push('Sec-WebSocket-Protocol: ' + protocol); - } - - var extensions = {}; - try { - extensions = acceptExtensions.call(self, extensionsOffer); - } catch (err) { - abortConnection(socket, 400, 'Bad Request'); - return; - } - - if (Object.keys(extensions).length) { - var serverExtensions = {}; - Object.keys(extensions).forEach(function(token) { - serverExtensions[token] = [extensions[token].params] - }); - headers.push('Sec-WebSocket-Extensions: ' + Extensions.format(serverExtensions)); - } - - // allows external modification/inspection of handshake headers - self.emit('headers', headers); - - socket.setTimeout(0); - socket.setNoDelay(true); - try { - socket.write(headers.concat('', '').join('\r\n')); - } - catch (e) { - // if the upgrade write fails, shut the connection down hard - try { socket.destroy(); } catch (e) {} - return; - } - - var client = new WebSocket([req, socket, upgradeHead], { - protocolVersion: version, - protocol: protocol, - extensions: extensions, - maxPayload: self.options.maxPayload - }); - - if (self.options.clientTracking) { - self.clients.push(client); - client.on('close', function() { - var index = self.clients.indexOf(client); - if (index != -1) { - self.clients.splice(index, 1); - } - }); - } - - // signal upgrade complete - socket.removeListener('error', errorHandler); - cb(client); - } - - // optionally call external protocol selection handler before - // calling completeHybiUpgrade2 - var completeHybiUpgrade1 = function() { - // choose from the sub-protocols - if (typeof self.options.handleProtocols == 'function') { - var protList = (protocols || "").split(/, */); - var callbackCalled = false; - var res = self.options.handleProtocols(protList, function(result, protocol) { - callbackCalled = true; - if (!result) abortConnection(socket, 401, 'Unauthorized'); - else completeHybiUpgrade2(protocol); - }); - if (!callbackCalled) { - // the handleProtocols handler never called our callback - abortConnection(socket, 501, 'Could not process protocols'); - } - return; - } else { - if (typeof protocols !== 'undefined') { - completeHybiUpgrade2(protocols.split(/, */)[0]); - } - else { - completeHybiUpgrade2(); - } - } - } - - // optionally call external client verification handler - if (typeof this.options.verifyClient == 'function') { - var info = { - origin: origin, - secure: typeof req.connection.authorized !== 'undefined' || typeof req.connection.encrypted !== 'undefined', - req: req - }; - if (this.options.verifyClient.length == 2) { - this.options.verifyClient(info, function(result, code, name) { - if (typeof code === 'undefined') code = 401; - if (typeof name === 'undefined') name = http.STATUS_CODES[code]; - - if (!result) abortConnection(socket, code, name); - else completeHybiUpgrade1(); - }); - return; - } - else if (!this.options.verifyClient(info)) { - abortConnection(socket, 401, 'Unauthorized'); - return; - } - } - - completeHybiUpgrade1(); -} - -function handleHixieUpgrade(req, socket, upgradeHead, cb) { - // handle premature socket errors - var errorHandler = function() { - try { socket.destroy(); } catch (e) {} - } - socket.on('error', errorHandler); - - // bail if options prevent hixie - if (this.options.disableHixie) { - abortConnection(socket, 401, 'Hixie support disabled'); - return; - } - - // verify key presence - if (!req.headers['sec-websocket-key2']) { - abortConnection(socket, 400, 'Bad Request'); - return; - } - - var origin = req.headers['origin'] - , self = this; - - // setup handshake completion to run after client has been verified - var onClientVerified = function() { - var wshost; - if (!req.headers['x-forwarded-host']) - wshost = req.headers.host; - else - wshost = req.headers['x-forwarded-host']; - var location = ((req.headers['x-forwarded-proto'] === 'https' || socket.encrypted) ? 'wss' : 'ws') + '://' + wshost + req.url - , protocol = req.headers['sec-websocket-protocol']; - - // build the response header and return a Buffer - var buildResponseHeader = function() { - var headers = [ - 'HTTP/1.1 101 Switching Protocols' - , 'Upgrade: WebSocket' - , 'Connection: Upgrade' - , 'Sec-WebSocket-Location: ' + location - ]; - if (typeof protocol != 'undefined') headers.push('Sec-WebSocket-Protocol: ' + protocol); - if (typeof origin != 'undefined') headers.push('Sec-WebSocket-Origin: ' + origin); - - return new Buffer(headers.concat('', '').join('\r\n')); - }; - - // send handshake response before receiving the nonce - var handshakeResponse = function() { - - socket.setTimeout(0); - socket.setNoDelay(true); - - var headerBuffer = buildResponseHeader(); - - try { - socket.write(headerBuffer, 'binary', function(err) { - // remove listener if there was an error - if (err) socket.removeListener('data', handler); - return; - }); - } catch (e) { - try { socket.destroy(); } catch (e) {} - return; - }; - }; - - // handshake completion code to run once nonce has been successfully retrieved - var completeHandshake = function(nonce, rest, headerBuffer) { - // calculate key - var k1 = req.headers['sec-websocket-key1'] - , k2 = req.headers['sec-websocket-key2'] - , md5 = crypto.createHash('md5'); - - [k1, k2].forEach(function (k) { - var n = parseInt(k.replace(/[^\d]/g, '')) - , spaces = k.replace(/[^ ]/g, '').length; - if (spaces === 0 || n % spaces !== 0){ - abortConnection(socket, 400, 'Bad Request'); - return; - } - n /= spaces; - md5.update(String.fromCharCode( - n >> 24 & 0xFF, - n >> 16 & 0xFF, - n >> 8 & 0xFF, - n & 0xFF)); - }); - md5.update(nonce.toString('binary')); - - socket.setTimeout(0); - socket.setNoDelay(true); - - try { - var hashBuffer = new Buffer(md5.digest('binary'), 'binary'); - var handshakeBuffer = new Buffer(headerBuffer.length + hashBuffer.length); - headerBuffer.copy(handshakeBuffer, 0); - hashBuffer.copy(handshakeBuffer, headerBuffer.length); - - // do a single write, which - upon success - causes a new client websocket to be setup - socket.write(handshakeBuffer, 'binary', function(err) { - if (err) return; // do not create client if an error happens - var client = new WebSocket([req, socket, rest], { - protocolVersion: 'hixie-76', - protocol: protocol - }); - if (self.options.clientTracking) { - self.clients.push(client); - client.on('close', function() { - var index = self.clients.indexOf(client); - if (index != -1) { - self.clients.splice(index, 1); - } - }); - } - - // signal upgrade complete - socket.removeListener('error', errorHandler); - cb(client); - }); - } - catch (e) { - try { socket.destroy(); } catch (e) {} - return; - } - } - - // retrieve nonce - var nonceLength = 8; - if (upgradeHead && upgradeHead.length >= nonceLength) { - var nonce = upgradeHead.slice(0, nonceLength); - var rest = upgradeHead.length > nonceLength ? upgradeHead.slice(nonceLength) : null; - completeHandshake.call(self, nonce, rest, buildResponseHeader()); - } - else { - // nonce not present in upgradeHead - var nonce = new Buffer(nonceLength); - upgradeHead.copy(nonce, 0); - var received = upgradeHead.length; - var rest = null; - var handler = function (data) { - var toRead = Math.min(data.length, nonceLength - received); - if (toRead === 0) return; - data.copy(nonce, received, 0, toRead); - received += toRead; - if (received == nonceLength) { - socket.removeListener('data', handler); - if (toRead < data.length) rest = data.slice(toRead); - - // complete the handshake but send empty buffer for headers since they have already been sent - completeHandshake.call(self, nonce, rest, new Buffer(0)); - } - } - - // handle additional data as we receive it - socket.on('data', handler); - - // send header response before we have the nonce to fix haproxy buffering - handshakeResponse(); - } - } - - // verify client - if (typeof this.options.verifyClient == 'function') { - var info = { - origin: origin, - secure: typeof req.connection.authorized !== 'undefined' || typeof req.connection.encrypted !== 'undefined', - req: req - }; - if (this.options.verifyClient.length == 2) { - var self = this; - this.options.verifyClient(info, function(result, code, name) { - if (typeof code === 'undefined') code = 401; - if (typeof name === 'undefined') name = http.STATUS_CODES[code]; - - if (!result) abortConnection(socket, code, name); - else onClientVerified.apply(self); - }); - return; - } - else if (!this.options.verifyClient(info)) { - abortConnection(socket, 401, 'Unauthorized'); - return; - } - } - - // no client verification required - onClientVerified(); -} - -function acceptExtensions(offer) { - var extensions = {}; - var options = this.options.perMessageDeflate; - var maxPayload = this.options.maxPayload; - if (options && offer[PerMessageDeflate.extensionName]) { - var perMessageDeflate = new PerMessageDeflate(options !== true ? options : {}, true, maxPayload); - perMessageDeflate.accept(offer[PerMessageDeflate.extensionName]); - extensions[PerMessageDeflate.extensionName] = perMessageDeflate; - } - return extensions; -} - -function abortConnection(socket, code, name) { - try { - var response = [ - 'HTTP/1.1 ' + code + ' ' + name, - 'Content-type: text/html' - ]; - socket.write(response.concat('', '').join('\r\n')); - } - catch (e) { /* ignore errors - we've aborted this connection */ } - finally { - // ensure that an early aborted connection is shut down completely - try { socket.destroy(); } catch (e) {} - } -} |