@hippy/debug-server-next
Version:
Debug server for hippy.
149 lines (148 loc) • 6.39 kB
JavaScript
;
/*
* 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;