UNPKG

@hippy/debug-server-next

Version:
149 lines (148 loc) 6.39 kB
"use strict"; /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.SocketServer = void 0; const ws_1 = require("ws"); const enum_1 = require("@debug-server-next/@types/enum"); const debug_targets_1 = require("@debug-server-next/controller/debug-targets"); const pub_sub_manager_1 = require("@debug-server-next/controller/pub-sub-manager"); const chrome_devtools_1 = require("@debug-server-next/controller/chrome-devtools"); const log_1 = require("@debug-server-next/utils/log"); const debug_target_1 = require("@debug-server-next/utils/debug-target"); const url_1 = require("@debug-server-next/utils/url"); const config_1 = require("@debug-server-next/config"); const hmr_1 = require("@debug-server-next/controller/hmr"); const vue_devtools_1 = require("@debug-server-next/controller/vue-devtools"); const react_devtools_1 = require("@debug-server-next/controller/react-devtools"); const vanilla_js_devtools_1 = require("@debug-server-next/controller/vanilla-js-devtools"); const heartbeatInterval = 300000; const log = new log_1.Logger('socket-server', enum_1.WinstonColor.Cyan); /** * Debug WebSocket server, support deploy in multiple node server, use Redis Pub/Sub broadcast message */ class SocketServer { constructor(server) { this.server = server; } start() { const wss = new ws_1.Server({ noServer: true, path: config_1.config.wsPath, }); this.wss = wss; this.server.on('upgrade', this.onUpgrade.bind(this)); wss.on('connection', this.onConnection.bind(this)); wss.on('error', (e) => { log.error('wss error: %s', e === null || e === void 0 ? void 0 : e.stack); }); wss.on('close', () => { clearInterval(this.interval); }); this.interval = setInterval(() => { this.wss.clients.forEach((client) => { if (client.isAlive === false) { client.terminate(); return; } client.isAlive = false; client.ping(() => { }); }); }, heartbeatInterval); } async close() { await (0, pub_sub_manager_1.cleanAllDebugTargets)(); this.wss.close(() => { log.warn('wss closed.'); }); } async onUpgrade(req, socket, head) { log.verbose('onUpgrade, ws url: %s', req.url); const host = (req.headers.host || req.headers.Host) || ''; const wsUrlParams = (0, url_1.parseWsUrl)(req.url); const reason = (0, url_1.getWsInvalidReason)(wsUrlParams); if (reason) { log.warn('onUpgrade error: %s', reason); return socket.destroy(); } const { clientRole } = wsUrlParams; let debugTarget; if (clientRole === enum_1.ClientRole.Devtools) { const params = wsUrlParams; const exist = await this.checkDebugTargetExist(params); if (!exist) { const reason = `debugTarget not exist! ${params.clientId}`; log.warn(reason); return socket.destroy(); } } else if ([enum_1.ClientRole.IOS, enum_1.ClientRole.Android].includes(clientRole)) { debugTarget = (0, debug_target_1.createTargetByWsUrlParams)(wsUrlParams, host); await (0, debug_target_1.patchRefAndSave)(debugTarget); } let upgradeSuccess = false; this.wss.handleUpgrade(req, socket, head, async (ws) => { upgradeSuccess = true; this.wss.emit('connection', ws, req, debugTarget); }); if (!upgradeSuccess) { await (0, debug_target_1.decreaseRefAndSave)(debugTarget.clientId); } } /** * ⚠️: don't do async operation before subscribe, otherwise will miss message */ async onConnection(ws, req, debugTarget) { const wsUrlParams = (0, url_1.parseWsUrl)(req.url); ws.isAlive = true; ws.on('pong', () => { ws.isAlive = true; }); const { clientRole } = wsUrlParams; if ([enum_1.ClientRole.JSRuntime, enum_1.ClientRole.VueDevtools].includes(clientRole)) { return (0, vue_devtools_1.onVueClientConnection)(ws, wsUrlParams); } if ([enum_1.ClientRole.VanillaJSRuntime].includes(clientRole)) { return (0, vanilla_js_devtools_1.onVanillaJSClientConnection)(ws, wsUrlParams); } if ([enum_1.ClientRole.ReactJSRuntime, enum_1.ClientRole.ReactDevtools].includes(clientRole)) { return (0, react_devtools_1.onReactClientConnection)(ws, wsUrlParams); } if (clientRole === enum_1.ClientRole.HMRClient) { return (0, hmr_1.onHMRClientConnection)(ws, wsUrlParams); } if (clientRole === enum_1.ClientRole.HMRServer) { return (0, hmr_1.onHMRServerConnection)(ws, wsUrlParams); } if (clientRole === enum_1.ClientRole.Devtools) { return (0, chrome_devtools_1.onDevtoolsConnection)(ws, wsUrlParams); } if ([enum_1.ClientRole.IOS, enum_1.ClientRole.Android].includes(clientRole)) { return (0, chrome_devtools_1.onAppConnection)(ws, wsUrlParams, debugTarget); } } async checkDebugTargetExist(wsUrlParams) { const { clientId, hash } = wsUrlParams; const debugTarget = await debug_targets_1.DebugTargetManager.findDebugTarget(clientId, hash); log.verbose('checkDebugTargetExist debug target: %j', debugTarget); return Boolean(debugTarget); } } exports.SocketServer = SocketServer;