11cb0ef41Sopenharmony_ci'use strict'; 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ciconst { 41cb0ef41Sopenharmony_ci ArrayPrototypePush, 51cb0ef41Sopenharmony_ci ErrorCaptureStackTrace, 61cb0ef41Sopenharmony_ci FunctionPrototypeBind, 71cb0ef41Sopenharmony_ci JSONParse, 81cb0ef41Sopenharmony_ci JSONStringify, 91cb0ef41Sopenharmony_ci ObjectKeys, 101cb0ef41Sopenharmony_ci Promise, 111cb0ef41Sopenharmony_ci} = primordials; 121cb0ef41Sopenharmony_ci 131cb0ef41Sopenharmony_ciconst Buffer = require('buffer').Buffer; 141cb0ef41Sopenharmony_ciconst crypto = require('crypto'); 151cb0ef41Sopenharmony_ciconst { ERR_DEBUGGER_ERROR } = require('internal/errors').codes; 161cb0ef41Sopenharmony_ciconst { EventEmitter } = require('events'); 171cb0ef41Sopenharmony_ciconst http = require('http'); 181cb0ef41Sopenharmony_ciconst URL = require('url'); 191cb0ef41Sopenharmony_ci 201cb0ef41Sopenharmony_ciconst debuglog = require('internal/util/debuglog').debuglog('inspect'); 211cb0ef41Sopenharmony_ci 221cb0ef41Sopenharmony_ciconst kOpCodeText = 0x1; 231cb0ef41Sopenharmony_ciconst kOpCodeClose = 0x8; 241cb0ef41Sopenharmony_ci 251cb0ef41Sopenharmony_ciconst kFinalBit = 0x80; 261cb0ef41Sopenharmony_ciconst kReserved1Bit = 0x40; 271cb0ef41Sopenharmony_ciconst kReserved2Bit = 0x20; 281cb0ef41Sopenharmony_ciconst kReserved3Bit = 0x10; 291cb0ef41Sopenharmony_ciconst kOpCodeMask = 0xF; 301cb0ef41Sopenharmony_ciconst kMaskBit = 0x80; 311cb0ef41Sopenharmony_ciconst kPayloadLengthMask = 0x7F; 321cb0ef41Sopenharmony_ci 331cb0ef41Sopenharmony_ciconst kMaxSingleBytePayloadLength = 125; 341cb0ef41Sopenharmony_ciconst kMaxTwoBytePayloadLength = 0xFFFF; 351cb0ef41Sopenharmony_ciconst kTwoBytePayloadLengthField = 126; 361cb0ef41Sopenharmony_ciconst kEightBytePayloadLengthField = 127; 371cb0ef41Sopenharmony_ciconst kMaskingKeyWidthInBytes = 4; 381cb0ef41Sopenharmony_ci 391cb0ef41Sopenharmony_ci// This guid is defined in the Websocket Protocol RFC 401cb0ef41Sopenharmony_ci// https://tools.ietf.org/html/rfc6455#section-1.3 411cb0ef41Sopenharmony_ciconst WEBSOCKET_HANDSHAKE_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; 421cb0ef41Sopenharmony_ci 431cb0ef41Sopenharmony_cifunction unpackError({ code, message }) { 441cb0ef41Sopenharmony_ci const err = new ERR_DEBUGGER_ERROR(`${message}`); 451cb0ef41Sopenharmony_ci err.code = code; 461cb0ef41Sopenharmony_ci ErrorCaptureStackTrace(err, unpackError); 471cb0ef41Sopenharmony_ci return err; 481cb0ef41Sopenharmony_ci} 491cb0ef41Sopenharmony_ci 501cb0ef41Sopenharmony_cifunction validateHandshake(requestKey, responseKey) { 511cb0ef41Sopenharmony_ci const expectedResponseKeyBase = requestKey + WEBSOCKET_HANDSHAKE_GUID; 521cb0ef41Sopenharmony_ci const shasum = crypto.createHash('sha1'); 531cb0ef41Sopenharmony_ci shasum.update(expectedResponseKeyBase); 541cb0ef41Sopenharmony_ci const shabuf = shasum.digest(); 551cb0ef41Sopenharmony_ci 561cb0ef41Sopenharmony_ci if (shabuf.toString('base64') !== responseKey) { 571cb0ef41Sopenharmony_ci throw new ERR_DEBUGGER_ERROR( 581cb0ef41Sopenharmony_ci `WebSocket secret mismatch: ${requestKey} did not match ${responseKey}`, 591cb0ef41Sopenharmony_ci ); 601cb0ef41Sopenharmony_ci } 611cb0ef41Sopenharmony_ci} 621cb0ef41Sopenharmony_ci 631cb0ef41Sopenharmony_cifunction encodeFrameHybi17(payload) { 641cb0ef41Sopenharmony_ci const dataLength = payload.length; 651cb0ef41Sopenharmony_ci 661cb0ef41Sopenharmony_ci let singleByteLength; 671cb0ef41Sopenharmony_ci let additionalLength; 681cb0ef41Sopenharmony_ci if (dataLength > kMaxTwoBytePayloadLength) { 691cb0ef41Sopenharmony_ci singleByteLength = kEightBytePayloadLengthField; 701cb0ef41Sopenharmony_ci additionalLength = Buffer.alloc(8); 711cb0ef41Sopenharmony_ci let remaining = dataLength; 721cb0ef41Sopenharmony_ci for (let i = 0; i < 8; ++i) { 731cb0ef41Sopenharmony_ci additionalLength[7 - i] = remaining & 0xFF; 741cb0ef41Sopenharmony_ci remaining >>= 8; 751cb0ef41Sopenharmony_ci } 761cb0ef41Sopenharmony_ci } else if (dataLength > kMaxSingleBytePayloadLength) { 771cb0ef41Sopenharmony_ci singleByteLength = kTwoBytePayloadLengthField; 781cb0ef41Sopenharmony_ci additionalLength = Buffer.alloc(2); 791cb0ef41Sopenharmony_ci additionalLength[0] = (dataLength & 0xFF00) >> 8; 801cb0ef41Sopenharmony_ci additionalLength[1] = dataLength & 0xFF; 811cb0ef41Sopenharmony_ci } else { 821cb0ef41Sopenharmony_ci additionalLength = Buffer.alloc(0); 831cb0ef41Sopenharmony_ci singleByteLength = dataLength; 841cb0ef41Sopenharmony_ci } 851cb0ef41Sopenharmony_ci 861cb0ef41Sopenharmony_ci const header = Buffer.from([ 871cb0ef41Sopenharmony_ci kFinalBit | kOpCodeText, 881cb0ef41Sopenharmony_ci kMaskBit | singleByteLength, 891cb0ef41Sopenharmony_ci ]); 901cb0ef41Sopenharmony_ci 911cb0ef41Sopenharmony_ci const mask = Buffer.alloc(4); 921cb0ef41Sopenharmony_ci const masked = Buffer.alloc(dataLength); 931cb0ef41Sopenharmony_ci for (let i = 0; i < dataLength; ++i) { 941cb0ef41Sopenharmony_ci masked[i] = payload[i] ^ mask[i % kMaskingKeyWidthInBytes]; 951cb0ef41Sopenharmony_ci } 961cb0ef41Sopenharmony_ci 971cb0ef41Sopenharmony_ci return Buffer.concat([header, additionalLength, mask, masked]); 981cb0ef41Sopenharmony_ci} 991cb0ef41Sopenharmony_ci 1001cb0ef41Sopenharmony_cifunction decodeFrameHybi17(data) { 1011cb0ef41Sopenharmony_ci const dataAvailable = data.length; 1021cb0ef41Sopenharmony_ci const notComplete = { closed: false, payload: null, rest: data }; 1031cb0ef41Sopenharmony_ci let payloadOffset = 2; 1041cb0ef41Sopenharmony_ci if ((dataAvailable - payloadOffset) < 0) return notComplete; 1051cb0ef41Sopenharmony_ci 1061cb0ef41Sopenharmony_ci const firstByte = data[0]; 1071cb0ef41Sopenharmony_ci const secondByte = data[1]; 1081cb0ef41Sopenharmony_ci 1091cb0ef41Sopenharmony_ci const final = (firstByte & kFinalBit) !== 0; 1101cb0ef41Sopenharmony_ci const reserved1 = (firstByte & kReserved1Bit) !== 0; 1111cb0ef41Sopenharmony_ci const reserved2 = (firstByte & kReserved2Bit) !== 0; 1121cb0ef41Sopenharmony_ci const reserved3 = (firstByte & kReserved3Bit) !== 0; 1131cb0ef41Sopenharmony_ci const opCode = firstByte & kOpCodeMask; 1141cb0ef41Sopenharmony_ci const masked = (secondByte & kMaskBit) !== 0; 1151cb0ef41Sopenharmony_ci const compressed = reserved1; 1161cb0ef41Sopenharmony_ci if (compressed) { 1171cb0ef41Sopenharmony_ci throw new ERR_DEBUGGER_ERROR('Compressed frames not supported'); 1181cb0ef41Sopenharmony_ci } 1191cb0ef41Sopenharmony_ci if (!final || reserved2 || reserved3) { 1201cb0ef41Sopenharmony_ci throw new ERR_DEBUGGER_ERROR('Only compression extension is supported'); 1211cb0ef41Sopenharmony_ci } 1221cb0ef41Sopenharmony_ci 1231cb0ef41Sopenharmony_ci if (masked) { 1241cb0ef41Sopenharmony_ci throw new ERR_DEBUGGER_ERROR('Masked server frame - not supported'); 1251cb0ef41Sopenharmony_ci } 1261cb0ef41Sopenharmony_ci 1271cb0ef41Sopenharmony_ci let closed = false; 1281cb0ef41Sopenharmony_ci switch (opCode) { 1291cb0ef41Sopenharmony_ci case kOpCodeClose: 1301cb0ef41Sopenharmony_ci closed = true; 1311cb0ef41Sopenharmony_ci break; 1321cb0ef41Sopenharmony_ci case kOpCodeText: 1331cb0ef41Sopenharmony_ci break; 1341cb0ef41Sopenharmony_ci default: 1351cb0ef41Sopenharmony_ci throw new ERR_DEBUGGER_ERROR(`Unsupported op code ${opCode}`); 1361cb0ef41Sopenharmony_ci } 1371cb0ef41Sopenharmony_ci 1381cb0ef41Sopenharmony_ci let payloadLength = secondByte & kPayloadLengthMask; 1391cb0ef41Sopenharmony_ci switch (payloadLength) { 1401cb0ef41Sopenharmony_ci case kTwoBytePayloadLengthField: 1411cb0ef41Sopenharmony_ci payloadOffset += 2; 1421cb0ef41Sopenharmony_ci payloadLength = (data[2] << 8) + data[3]; 1431cb0ef41Sopenharmony_ci break; 1441cb0ef41Sopenharmony_ci 1451cb0ef41Sopenharmony_ci case kEightBytePayloadLengthField: 1461cb0ef41Sopenharmony_ci payloadOffset += 8; 1471cb0ef41Sopenharmony_ci payloadLength = 0; 1481cb0ef41Sopenharmony_ci for (let i = 0; i < 8; ++i) { 1491cb0ef41Sopenharmony_ci payloadLength <<= 8; 1501cb0ef41Sopenharmony_ci payloadLength |= data[2 + i]; 1511cb0ef41Sopenharmony_ci } 1521cb0ef41Sopenharmony_ci break; 1531cb0ef41Sopenharmony_ci 1541cb0ef41Sopenharmony_ci default: 1551cb0ef41Sopenharmony_ci // Nothing. We already have the right size. 1561cb0ef41Sopenharmony_ci } 1571cb0ef41Sopenharmony_ci if ((dataAvailable - payloadOffset - payloadLength) < 0) return notComplete; 1581cb0ef41Sopenharmony_ci 1591cb0ef41Sopenharmony_ci const payloadEnd = payloadOffset + payloadLength; 1601cb0ef41Sopenharmony_ci return { 1611cb0ef41Sopenharmony_ci payload: data.slice(payloadOffset, payloadEnd), 1621cb0ef41Sopenharmony_ci rest: data.slice(payloadEnd), 1631cb0ef41Sopenharmony_ci closed, 1641cb0ef41Sopenharmony_ci }; 1651cb0ef41Sopenharmony_ci} 1661cb0ef41Sopenharmony_ci 1671cb0ef41Sopenharmony_ciclass Client extends EventEmitter { 1681cb0ef41Sopenharmony_ci constructor() { 1691cb0ef41Sopenharmony_ci super(); 1701cb0ef41Sopenharmony_ci this.handleChunk = FunctionPrototypeBind(this._handleChunk, this); 1711cb0ef41Sopenharmony_ci 1721cb0ef41Sopenharmony_ci this._port = undefined; 1731cb0ef41Sopenharmony_ci this._host = undefined; 1741cb0ef41Sopenharmony_ci 1751cb0ef41Sopenharmony_ci this.reset(); 1761cb0ef41Sopenharmony_ci } 1771cb0ef41Sopenharmony_ci 1781cb0ef41Sopenharmony_ci _handleChunk(chunk) { 1791cb0ef41Sopenharmony_ci this._unprocessed = Buffer.concat([this._unprocessed, chunk]); 1801cb0ef41Sopenharmony_ci 1811cb0ef41Sopenharmony_ci while (this._unprocessed.length > 2) { 1821cb0ef41Sopenharmony_ci const { 1831cb0ef41Sopenharmony_ci closed, 1841cb0ef41Sopenharmony_ci payload: payloadBuffer, 1851cb0ef41Sopenharmony_ci rest, 1861cb0ef41Sopenharmony_ci } = decodeFrameHybi17(this._unprocessed); 1871cb0ef41Sopenharmony_ci this._unprocessed = rest; 1881cb0ef41Sopenharmony_ci 1891cb0ef41Sopenharmony_ci if (closed) { 1901cb0ef41Sopenharmony_ci this.reset(); 1911cb0ef41Sopenharmony_ci return; 1921cb0ef41Sopenharmony_ci } 1931cb0ef41Sopenharmony_ci if (payloadBuffer === null || payloadBuffer.length === 0) break; 1941cb0ef41Sopenharmony_ci 1951cb0ef41Sopenharmony_ci const payloadStr = payloadBuffer.toString(); 1961cb0ef41Sopenharmony_ci debuglog('< %s', payloadStr); 1971cb0ef41Sopenharmony_ci const lastChar = payloadStr[payloadStr.length - 1]; 1981cb0ef41Sopenharmony_ci if (payloadStr[0] !== '{' || lastChar !== '}') { 1991cb0ef41Sopenharmony_ci throw new ERR_DEBUGGER_ERROR(`Payload does not look like JSON: ${payloadStr}`); 2001cb0ef41Sopenharmony_ci } 2011cb0ef41Sopenharmony_ci let payload; 2021cb0ef41Sopenharmony_ci try { 2031cb0ef41Sopenharmony_ci payload = JSONParse(payloadStr); 2041cb0ef41Sopenharmony_ci } catch (parseError) { 2051cb0ef41Sopenharmony_ci parseError.string = payloadStr; 2061cb0ef41Sopenharmony_ci throw parseError; 2071cb0ef41Sopenharmony_ci } 2081cb0ef41Sopenharmony_ci 2091cb0ef41Sopenharmony_ci const { id, method, params, result, error } = payload; 2101cb0ef41Sopenharmony_ci if (id) { 2111cb0ef41Sopenharmony_ci const handler = this._pending[id]; 2121cb0ef41Sopenharmony_ci if (handler) { 2131cb0ef41Sopenharmony_ci delete this._pending[id]; 2141cb0ef41Sopenharmony_ci handler(error, result); 2151cb0ef41Sopenharmony_ci } 2161cb0ef41Sopenharmony_ci } else if (method) { 2171cb0ef41Sopenharmony_ci this.emit('debugEvent', method, params); 2181cb0ef41Sopenharmony_ci this.emit(method, params); 2191cb0ef41Sopenharmony_ci } else { 2201cb0ef41Sopenharmony_ci throw new ERR_DEBUGGER_ERROR(`Unsupported response: ${payloadStr}`); 2211cb0ef41Sopenharmony_ci } 2221cb0ef41Sopenharmony_ci } 2231cb0ef41Sopenharmony_ci } 2241cb0ef41Sopenharmony_ci 2251cb0ef41Sopenharmony_ci reset() { 2261cb0ef41Sopenharmony_ci if (this._http) { 2271cb0ef41Sopenharmony_ci this._http.destroy(); 2281cb0ef41Sopenharmony_ci } 2291cb0ef41Sopenharmony_ci if (this._socket) { 2301cb0ef41Sopenharmony_ci this._socket.destroy(); 2311cb0ef41Sopenharmony_ci } 2321cb0ef41Sopenharmony_ci this._http = null; 2331cb0ef41Sopenharmony_ci this._lastId = 0; 2341cb0ef41Sopenharmony_ci this._socket = null; 2351cb0ef41Sopenharmony_ci this._pending = {}; 2361cb0ef41Sopenharmony_ci this._unprocessed = Buffer.alloc(0); 2371cb0ef41Sopenharmony_ci } 2381cb0ef41Sopenharmony_ci 2391cb0ef41Sopenharmony_ci callMethod(method, params) { 2401cb0ef41Sopenharmony_ci return new Promise((resolve, reject) => { 2411cb0ef41Sopenharmony_ci if (!this._socket) { 2421cb0ef41Sopenharmony_ci reject(new ERR_DEBUGGER_ERROR('Use `run` to start the app again.')); 2431cb0ef41Sopenharmony_ci return; 2441cb0ef41Sopenharmony_ci } 2451cb0ef41Sopenharmony_ci const data = { id: ++this._lastId, method, params }; 2461cb0ef41Sopenharmony_ci this._pending[data.id] = (error, result) => { 2471cb0ef41Sopenharmony_ci if (error) reject(unpackError(error)); 2481cb0ef41Sopenharmony_ci else resolve(ObjectKeys(result).length ? result : undefined); 2491cb0ef41Sopenharmony_ci }; 2501cb0ef41Sopenharmony_ci const json = JSONStringify(data); 2511cb0ef41Sopenharmony_ci debuglog('> %s', json); 2521cb0ef41Sopenharmony_ci this._socket.write(encodeFrameHybi17(Buffer.from(json))); 2531cb0ef41Sopenharmony_ci }); 2541cb0ef41Sopenharmony_ci } 2551cb0ef41Sopenharmony_ci 2561cb0ef41Sopenharmony_ci _fetchJSON(urlPath) { 2571cb0ef41Sopenharmony_ci return new Promise((resolve, reject) => { 2581cb0ef41Sopenharmony_ci const httpReq = http.get({ 2591cb0ef41Sopenharmony_ci host: this._host, 2601cb0ef41Sopenharmony_ci port: this._port, 2611cb0ef41Sopenharmony_ci path: urlPath, 2621cb0ef41Sopenharmony_ci }); 2631cb0ef41Sopenharmony_ci 2641cb0ef41Sopenharmony_ci const chunks = []; 2651cb0ef41Sopenharmony_ci 2661cb0ef41Sopenharmony_ci function onResponse(httpRes) { 2671cb0ef41Sopenharmony_ci function parseChunks() { 2681cb0ef41Sopenharmony_ci const resBody = Buffer.concat(chunks).toString(); 2691cb0ef41Sopenharmony_ci if (httpRes.statusCode !== 200) { 2701cb0ef41Sopenharmony_ci reject(new ERR_DEBUGGER_ERROR(`Unexpected ${httpRes.statusCode}: ${resBody}`)); 2711cb0ef41Sopenharmony_ci return; 2721cb0ef41Sopenharmony_ci } 2731cb0ef41Sopenharmony_ci try { 2741cb0ef41Sopenharmony_ci resolve(JSONParse(resBody)); 2751cb0ef41Sopenharmony_ci } catch { 2761cb0ef41Sopenharmony_ci reject(new ERR_DEBUGGER_ERROR(`Response didn't contain JSON: ${resBody}`)); 2771cb0ef41Sopenharmony_ci 2781cb0ef41Sopenharmony_ci } 2791cb0ef41Sopenharmony_ci } 2801cb0ef41Sopenharmony_ci 2811cb0ef41Sopenharmony_ci httpRes.on('error', reject); 2821cb0ef41Sopenharmony_ci httpRes.on('data', (chunk) => ArrayPrototypePush(chunks, chunk)); 2831cb0ef41Sopenharmony_ci httpRes.on('end', parseChunks); 2841cb0ef41Sopenharmony_ci } 2851cb0ef41Sopenharmony_ci 2861cb0ef41Sopenharmony_ci httpReq.on('error', reject); 2871cb0ef41Sopenharmony_ci httpReq.on('response', onResponse); 2881cb0ef41Sopenharmony_ci }); 2891cb0ef41Sopenharmony_ci } 2901cb0ef41Sopenharmony_ci 2911cb0ef41Sopenharmony_ci async connect(port, host) { 2921cb0ef41Sopenharmony_ci this._port = port; 2931cb0ef41Sopenharmony_ci this._host = host; 2941cb0ef41Sopenharmony_ci const urlPath = await this._discoverWebsocketPath(); 2951cb0ef41Sopenharmony_ci return this._connectWebsocket(urlPath); 2961cb0ef41Sopenharmony_ci } 2971cb0ef41Sopenharmony_ci 2981cb0ef41Sopenharmony_ci async _discoverWebsocketPath() { 2991cb0ef41Sopenharmony_ci const { 0: { webSocketDebuggerUrl } } = await this._fetchJSON('/json'); 3001cb0ef41Sopenharmony_ci return URL.parse(webSocketDebuggerUrl).path; 3011cb0ef41Sopenharmony_ci } 3021cb0ef41Sopenharmony_ci 3031cb0ef41Sopenharmony_ci _connectWebsocket(urlPath) { 3041cb0ef41Sopenharmony_ci this.reset(); 3051cb0ef41Sopenharmony_ci 3061cb0ef41Sopenharmony_ci const requestKey = crypto.randomBytes(16).toString('base64'); 3071cb0ef41Sopenharmony_ci debuglog('request WebSocket', requestKey); 3081cb0ef41Sopenharmony_ci 3091cb0ef41Sopenharmony_ci const httpReq = this._http = http.request({ 3101cb0ef41Sopenharmony_ci host: this._host, 3111cb0ef41Sopenharmony_ci port: this._port, 3121cb0ef41Sopenharmony_ci path: urlPath, 3131cb0ef41Sopenharmony_ci headers: { 3141cb0ef41Sopenharmony_ci 'Connection': 'Upgrade', 3151cb0ef41Sopenharmony_ci 'Upgrade': 'websocket', 3161cb0ef41Sopenharmony_ci 'Sec-WebSocket-Key': requestKey, 3171cb0ef41Sopenharmony_ci 'Sec-WebSocket-Version': '13', 3181cb0ef41Sopenharmony_ci }, 3191cb0ef41Sopenharmony_ci }); 3201cb0ef41Sopenharmony_ci httpReq.on('error', (e) => { 3211cb0ef41Sopenharmony_ci this.emit('error', e); 3221cb0ef41Sopenharmony_ci }); 3231cb0ef41Sopenharmony_ci httpReq.on('response', (httpRes) => { 3241cb0ef41Sopenharmony_ci if (httpRes.statusCode >= 400) { 3251cb0ef41Sopenharmony_ci process.stderr.write(`Unexpected HTTP code: ${httpRes.statusCode}\n`); 3261cb0ef41Sopenharmony_ci httpRes.pipe(process.stderr); 3271cb0ef41Sopenharmony_ci } else { 3281cb0ef41Sopenharmony_ci httpRes.pipe(process.stderr); 3291cb0ef41Sopenharmony_ci } 3301cb0ef41Sopenharmony_ci }); 3311cb0ef41Sopenharmony_ci 3321cb0ef41Sopenharmony_ci const handshakeListener = (res, socket) => { 3331cb0ef41Sopenharmony_ci validateHandshake(requestKey, res.headers['sec-websocket-accept']); 3341cb0ef41Sopenharmony_ci debuglog('websocket upgrade'); 3351cb0ef41Sopenharmony_ci 3361cb0ef41Sopenharmony_ci this._socket = socket; 3371cb0ef41Sopenharmony_ci socket.on('data', this.handleChunk); 3381cb0ef41Sopenharmony_ci socket.on('close', () => { 3391cb0ef41Sopenharmony_ci this.emit('close'); 3401cb0ef41Sopenharmony_ci }); 3411cb0ef41Sopenharmony_ci 3421cb0ef41Sopenharmony_ci this.emit('ready'); 3431cb0ef41Sopenharmony_ci }; 3441cb0ef41Sopenharmony_ci 3451cb0ef41Sopenharmony_ci return new Promise((resolve, reject) => { 3461cb0ef41Sopenharmony_ci this.once('error', reject); 3471cb0ef41Sopenharmony_ci this.once('ready', resolve); 3481cb0ef41Sopenharmony_ci 3491cb0ef41Sopenharmony_ci httpReq.on('upgrade', handshakeListener); 3501cb0ef41Sopenharmony_ci httpReq.end(); 3511cb0ef41Sopenharmony_ci }); 3521cb0ef41Sopenharmony_ci } 3531cb0ef41Sopenharmony_ci} 3541cb0ef41Sopenharmony_ci 3551cb0ef41Sopenharmony_cimodule.exports = Client; 356