UNPKG

websocket13

Version:

Simple WebSocket protocol 13 client with no native or heavy dependencies

180 lines 17.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const crypto_1 = require("crypto"); const permessage_deflate_1 = __importDefault(require("permessage-deflate")); const tiny_typed_emitter_1 = require("tiny-typed-emitter"); const url_1 = require("url"); const websocket_extensions_1 = __importDefault(require("websocket-extensions")); const WebSocketServerConnection_1 = __importDefault(require("./WebSocketServerConnection")); const HTTPStatusCodes_1 = __importDefault(require("./enums/HTTPStatusCodes")); const HTTP_VERSION = 1.1; const WEBSOCKET_VERSION = 13; // eslint-disable-next-line const PACKAGE_VERSION = require('../package.json').version; class WebSocketServer extends tiny_typed_emitter_1.TypedEmitter { constructor(options) { super(); this.options = { pingInterval: 10000, pingTimeout: 10000, pingFailures: 3, permessageDeflate: true }; options = options || {}; Object.assign(this.options, options); this.protocols = this.options.protocols || []; } http(server) { server.on('upgrade', (req, socket, head) => { if (!req.headers.upgrade || req.headers.upgrade.toLowerCase() != 'websocket') { bail('Invalid upgrade type. Supported: websocket'); return; } if (!req.headers.connection || !req.headers.connection.toLowerCase().split(',').map(i => i.trim()).includes('upgrade')) { bail('Invalid upgrade request.'); return; } let httpV = req.httpVersion.split('.'); if (parseInt(httpV[0]) < 1 || parseInt(httpV[1]) < 1) { bail('Invalid HTTP version for websocket upgrade.'); return; } if (req.method.toUpperCase() != 'GET') { bail('Bad HTTP method. Required: GET'); return; } if (!req.headers['sec-websocket-key'] || Buffer.from(req.headers['sec-websocket-key'], 'base64').length != 16) { bail('Missing or invalid Sec-WebSocket-Key.'); return; } if (req.headers['sec-websocket-version'] != WEBSOCKET_VERSION.toString()) { bail(`Sec-WebSocket-Version must be ${WEBSOCKET_VERSION}.`); return; } if (!socket.remoteAddress) { bail('Unable to determine IP address.'); return; } let selectedProtocol = null; let protocols = []; if (req.headers['sec-websocket-protocol']) { protocols = req.headers['sec-websocket-protocol'].split(',').map(protocol => protocol.trim()); // Do any of these match? for (let i = 0; i < protocols.length; i++) { if (this.protocols.indexOf(protocols[i]) != -1) { selectedProtocol = protocols[i]; break; } } } let uri = (0, url_1.parse)(req.url, true); let extensions = new websocket_extensions_1.default(); if (this.options.permessageDeflate) { extensions.add(permessage_deflate_1.default); } let selectedExtensions = extensions.generateResponse(req.headers['sec-websocket-extensions']); let handshakeData = { path: uri.pathname, query: uri.query, headers: req.headers, httpVersion: req.httpVersion, origin: req.headers.origin || null, protocols: protocols || [], selectedProtocol: selectedProtocol || null, auth: null, cookies: {}, remoteAddress: socket.remoteAddress.replace(/^::ffff:/, ''), socket }; // Does it have HTTP authorization? if (req.headers.authorization) { let match = req.headers.authorization.match(/basic ((?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4}))/i); if (match) { handshakeData.auth = Buffer.from(match[1], 'base64').toString('utf8'); } } // Does it have cookies? if (req.headers.cookie) { req.headers.cookie.split(';').map(cookie => cookie.trim().split('=')).forEach(cookie => { handshakeData.cookies[cookie[0].trim()] = decodeURIComponent(cookie.slice(1).join('=').trim()); }); } // Everything looks okay so far, make sure we'd like to accept this. this.emit('handshake', handshakeData, (statusCode, body, headers) => { // REJECT req.statusCode = statusCode || 403; headers = headers || {}; socket.end(buildResponse(statusCode || 403, headers, body)); }, (response) => { // ACCEPT response = response || {}; let headers = response.headers || {}; let options = { pingInterval: this.options.pingInterval, pingTimeout: this.options.pingTimeout, pingFailures: this.options.pingFailures, }; headers.Upgrade = 'websocket'; headers.Connection = 'Upgrade'; headers['Sec-WebSocket-Accept'] = (0, crypto_1.createHash)('sha1').update(req.headers['sec-websocket-key'] + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11').digest('base64'); // Check if the accept method overrode our selected subprotocol if (typeof response.protocol !== 'undefined') { handshakeData.selectedProtocol = response.protocol || null; } if (typeof response.permessageDeflate != 'undefined') { extensions = new websocket_extensions_1.default(); if (response.permessageDeflate) { extensions.add(permessage_deflate_1.default); } selectedExtensions = extensions.generateResponse(req.headers['sec-websocket-extensions']); } if (selectedExtensions) { headers['Sec-WebSocket-Extensions'] = selectedExtensions; } if (handshakeData.selectedProtocol) { headers['Sec-WebSocket-Protocol'] = handshakeData.selectedProtocol; } socket.write(buildResponse(101, headers)); response.options = response.options || {}; Object.assign(options, response.options); let websocket = new WebSocketServerConnection_1.default(socket, options, handshakeData, head, extensions); this.emit('connection', websocket); return websocket; }); function bail(err) { if (server.listenerCount('upgrade') != 1) { // Something else could pick this up return; } socket.end(buildResponse(400, null, err)); } }); } } exports.default = WebSocketServer; function buildResponse(code, headers, body) { let response = `HTTP/${HTTP_VERSION} ${code} ${HTTPStatusCodes_1.default[code] || 'Unknown Response'}\r\n`; headers = headers || {}; headers.Server = `node-websocket13/${PACKAGE_VERSION}`; headers.Date = new Date().toUTCString(); if (typeof body === 'object') { body = JSON.stringify(body); headers['Content-Type'] = 'application/json'; } if (body) { headers['Content-Length'] = Buffer.byteLength(body); } else if (code != 204 && code != 101) { headers['Content-Length'] = 0; } for (let i in headers) { response += `${i}: ${headers[i]}\r\n`; } response += '\r\n' + (typeof body !== 'undefined' ? body : ''); return response; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiV2ViU29ja2V0U2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL1dlYlNvY2tldFNlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLG1DQUFrQztBQUdsQyw0RUFBbUQ7QUFDbkQsMkRBQWdEO0FBQ2hELDZCQUFzQztBQUN0QyxnRkFBdUQ7QUFHdkQsNEZBQW9FO0FBQ3BFLDhFQUFzRDtBQUd0RCxNQUFNLFlBQVksR0FBRyxHQUFHLENBQUM7QUFDekIsTUFBTSxpQkFBaUIsR0FBRyxFQUFFLENBQUM7QUFFN0IsMkJBQTJCO0FBQzNCLE1BQU0sZUFBZSxHQUFHLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLE9BQU8sQ0FBQztBQUUzRCxNQUFxQixlQUFnQixTQUFRLGlDQUFtQztJQUkvRSxZQUFZLE9BQWdDO1FBQzNDLEtBQUssRUFBRSxDQUFDO1FBRVIsSUFBSSxDQUFDLE9BQU8sR0FBRztZQUNkLFlBQVksRUFBRSxLQUFLO1lBQ25CLFdBQVcsRUFBRSxLQUFLO1lBQ2xCLFlBQVksRUFBRSxDQUFDO1lBQ2YsaUJBQWlCLEVBQUUsSUFBSTtTQUN2QixDQUFDO1FBRUYsT0FBTyxHQUFHLE9BQU8sSUFBSSxFQUFFLENBQUM7UUFDeEIsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBRXJDLElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLElBQUksRUFBRSxDQUFDO0lBQy9DLENBQUM7SUFFRCxJQUFJLENBQUMsTUFBa0I7UUFDdEIsTUFBTSxDQUFDLEVBQUUsQ0FBQyxTQUFTLEVBQUUsQ0FBQyxHQUFvQixFQUFFLE1BQWMsRUFBRSxJQUFZLEVBQUUsRUFBRTtZQUMzRSxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxPQUFPLElBQUksR0FBRyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsV0FBVyxFQUFFLElBQUks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