UNPKG

nxkit

Version:

This is a collection of tools, independent of any other libraries

185 lines (184 loc) 7.4 kB
"use strict"; /* ***** BEGIN LICENSE BLOCK ***** * Distributed under the BSD license: * * Copyright (c) 2015, xuewen.chu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of xuewen.chu nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL xuewen.chu BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ***** END LICENSE BLOCK ***** */ Object.defineProperty(exports, "__esModule", { value: true }); const util_1 = require("../../util"); const request_1 = require("../../request"); const buffer_1 = require("../../buffer"); const errno_1 = require("../../errno"); const http = require("http"); const https = require("https"); const crypto = require("crypto"); const parser_1 = require("../parser"); const conv_1 = require("./conv"); // Node implementation class NodeConversation extends conv_1.WSConversation { constructor() { super(...arguments); this.m_req = null; this.m_socket = null; // web socket connection this.m_count = 0; } async initialize() { util_1.default.assert(!this.m_req, 'No need to repeat open'); if (conv_1.default.USE_GZIP_DATA) this.setGzip(true); // use gzip var self = this; var url = this.m_url; var bind_services = Object.keys(this.m_handles).join(','); url.setParam('bind_services', bind_services); var isSSL = url.protocol == 'wss:'; var port = url.port || (isSSL ? 443 : 80); var lib = isSSL ? https : http; var path = url.path; var origin = '127.0.0.1:' + port; var key = Date.now(); var headers = Object.assign({}, this.getRequestHeaders(), { 'User-Agent': request_1.default.userAgent, 'Connection': 'Upgrade', 'Upgrade': 'websocket', 'Origin': origin, 'Sec-Websocket-Origin': origin, 'Sec-Websocket-Version': 13, 'Sec-Websocket-Key': key, 'Use-Gzip': this.isGzip ? 'on' : 'off', }); if (this.m_signer) { Object.assign(headers, await this.m_signer.sign(path)); } var options = { hostname: url.hostname, port: port, path: path, headers: headers, rejectUnauthorized: false, }; if (isSSL) { options.agent = new https.Agent(options); } var req = this.m_req = lib.request(options); function handshakes(res, key) { var accept = res.headers['sec-websocket-accept']; if (accept) { var shasum = crypto.createHash('sha1'); shasum.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'); var skey = shasum.digest('base64'); return skey == accept; } return false; } req.on('upgrade', function (res, socket, upgradeHead) { console.log('CLI NodeConversation Upgrade', self.m_url.href); if (!self.m_connecting || !handshakes(res, key)) { socket.end(); self.close(); return; } self.m_socket = socket; self.m_token = res.headers['session-token'] || ''; var parser = new parser_1.PacketParser(); socket.setNoDelay(true); socket.setTimeout(0); socket.setKeepAlive(true, conv_1.KEEP_ALIVE_TIME); socket.on('timeout', () => self.close()); socket.on('end', () => self.close()); socket.on('close', () => self.close()); socket.on('data', d => parser.add(buffer_1.default.from(d.buffer))); socket.on('error', e => (self._error(e), self.close())); socket.on('drain', () => (self.m_overflow = false, self.onDrain.trigger({}))); parser.onText.on(e => self.handlePacket(e.data, true)); parser.onData.on(e => self.handlePacket(e.data, false)); parser.onPing.on(e => self.handlePing(e.data)); parser.onPong.on(e => self.handlePong(e.data)); parser.onClose.on(e => self.close()); parser.onError.on(e => (self._error(e.data), self.close())); self._open(); }); req.on('error', function (e) { console.log('CLI NodeConversation error', self.m_url.href); self._error(e); self.close(); }); console.log('CLI NodeConversation init', self.m_url.href, self.m_connecting); req.end(); } /** * @ovrewrite */ close() { var socket = this.m_socket; if (socket) { this.m_socket = null; socket.removeAllListeners('timeout'); socket.removeAllListeners('end'); socket.removeAllListeners('close'); socket.removeAllListeners('error'); socket.removeAllListeners('data'); socket.removeAllListeners('drain'); try { if (socket.writable) socket.end(); } catch (err) { console.error(err); } } else { if (this.m_req) { this.m_req.abort(); } } this.m_req = null; this.m_socket = null; super.close(); } /** * @ovrewrite */ send(data) { util_1.default.assert(this.isOpen, errno_1.default.ERR_CONNECTION_CLOSE_STATUS); return conv_1.WSConversation.write(this, parser_1.sendDataPacket, [this.m_socket, data]); } /** * @overwrite */ ping() { util_1.default.assert(this.isOpen, errno_1.default.ERR_CONNECTION_CLOSE_STATUS); return conv_1.WSConversation.write(this, parser_1.sendPingPacket, [this.m_socket]); } /** * @overwrite */ pong() { util_1.default.assert(this.isOpen, errno_1.default.ERR_CONNECTION_CLOSE_STATUS); return conv_1.WSConversation.write(this, parser_1.sendPongPacket, [this.m_socket]); } } exports.default = NodeConversation;