mihawk
Version:
A tiny & simple mock server tool, support json,js,cjs,ts(typescript).
239 lines (238 loc) • 12.6 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const https_1 = __importDefault(require("https"));
const http_1 = __importDefault(require("http"));
const util_1 = require("util");
const color_cc_1 = __importDefault(require("color-cc"));
const WS = __importStar(require("ws"));
const consts_1 = require("../consts");
const parser_1 = require("../utils/parser");
const print_1 = require("../utils/print");
const date_1 = require("../utils/date");
const server_1 = require("../utils/server");
const net_1 = require("../utils/net");
const LOGFLAG_WS = `${color_cc_1.default.cyan('[WS]')}${color_cc_1.default.gray(':')}`;
class WsCtrl {
constructor(options) {
this._clientIndex = 0;
const { host, port, secure = false, server, stomp = false, resolve = _defaultResolveFunc, } = options;
this._useStompMsg = !!stomp;
this._host = host;
this._port = port;
this._secure = secure;
this._wsSvr = null;
this._baseServer = server;
const _resolve = typeof resolve === 'function' ? resolve : _defaultResolveFunc;
this._resolveFunc = function (socket, request, options) {
try {
const funcCtx = socket;
_resolve.call(funcCtx, socket, request, options);
}
catch (error) {
print_1.Printer.error(LOGFLAG_WS, 'Failed to exec socket logic resolveFunc:\n', error);
}
};
}
_autoCreateClientId() {
return `Client-${++this._clientIndex}__${(0, date_1.getTimeNowStr)()}`;
}
start(server, isRestart) {
return __awaiter(this, void 0, void 0, function* () {
if (this._wsSvr) {
print_1.Printer.log(LOGFLAG_WS, 'WS server is already running.');
return;
}
const resolveFunc = this._resolveFunc;
const stomp = this._useStompMsg;
if (server && _isHttpOrHttpsServer(server)) {
this._baseServer = server;
}
const { isSecure = this._secure, host = this._host, port = this._port } = _getBaseServerInfo(this._baseServer);
const protocol = isSecure ? 'wss' : 'ws';
const hostFix = host === '0.0.0.0' ? '127.0.0.1' : host;
this._wsSvr = new WS.WebSocketServer({ server: this._baseServer });
this._wsSvr.on('listening', function listening() {
print_1.Printer.log(`🚀 ${color_cc_1.default.green(`${isRestart ? 'Restart' : 'Start'} mock-socket-server success!`)}`);
!isRestart && print_1.Printer.log(`Mock Socket Server address:`);
print_1.Printer.log(`${color_cc_1.default.gray('-')} ${color_cc_1.default.cyan(`${protocol}://${hostFix}:${port}`)}`);
if ((0, net_1.supportLocalHost)(host)) {
const addr2 = `${protocol}://${(0, net_1.getMyIp)()}:${port}`;
print_1.Printer.log(`${color_cc_1.default.gray('-')} ${color_cc_1.default.cyan(addr2)}`);
}
console.log();
});
this._wsSvr.on('headers', function headers(headers, req) {
headers.push(`X-Powered-By: ${consts_1.PKG_NAME}`);
});
this._wsSvr.on('connection', (socket, request) => {
const { remoteAddress, remotePort } = (request === null || request === void 0 ? void 0 : request.socket) || {};
const clientId = remoteAddress ? `${remoteAddress}:${remotePort || ++this._clientIndex}` : this._autoCreateClientId();
print_1.Printer.log(LOGFLAG_WS, 'Socket client connected!', color_cc_1.default.gray(`clientId=[${clientId}]`), color_cc_1.default.gray(`time=${(0, date_1.getTimeNowStr)()}`));
resolveFunc(socket, request, { clientId, stomp });
setTimeout(() => {
const clientId4Log = color_cc_1.default.gray(`clientId=[${clientId}]`);
const timeNow4Log = color_cc_1.default.gray(`time=${(0, date_1.getTimeNowStr)()}`);
if (this._wsSvr) {
try {
socket === null || socket === void 0 ? void 0 : socket.emit('open', () => {
print_1.Printer.log(LOGFLAG_WS, "Socket client opened! (socket.emit('open') success!)", clientId4Log, timeNow4Log);
});
}
catch (error) {
print_1.Printer.error(LOGFLAG_WS, "Failed to exec socket.emit('open') ! ", clientId4Log, timeNow4Log, '\n', error);
}
}
else {
print_1.Printer.warn(LOGFLAG_WS, "Cancel to exec socket.emit('open'), because connection is closed.", clientId4Log, timeNow4Log);
}
}, 0);
});
this._wsSvr.on('error', function error(err) {
print_1.Printer.error(LOGFLAG_WS, 'WebSocket server error:', err);
});
this._wsSvr.on('close', function close() {
});
});
}
_close(forceClose = false) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
if (!this._wsSvr) {
print_1.Printer.log(LOGFLAG_WS, 'WebSocket server is not running.');
return;
}
const clients = (_a = this._wsSvr) === null || _a === void 0 ? void 0 : _a.clients;
try {
if (clients === null || clients === void 0 ? void 0 : clients.size) {
if (forceClose) {
clients === null || clients === void 0 ? void 0 : clients.forEach(client => client === null || client === void 0 ? void 0 : client.terminate());
}
else {
clients === null || clients === void 0 ? void 0 : clients.forEach(client => client === null || client === void 0 ? void 0 : client.close());
}
}
}
catch (error) {
print_1.Printer.error(LOGFLAG_WS, `Batch ${forceClose ? 'terminate' : 'close'} clients failed!\n`, error);
}
const closeAsync = (0, util_1.promisify)(this._wsSvr.close).bind(this._wsSvr);
try {
yield closeAsync();
this._wsSvr = null;
}
catch (error) {
print_1.Printer.error(LOGFLAG_WS, `${forceClose ? 'Destory' : 'Close'} WebSocket server failed!\n`, error);
}
});
}
close() {
return __awaiter(this, void 0, void 0, function* () {
return yield this._close();
});
}
destory() {
return __awaiter(this, void 0, void 0, function* () {
return yield this._close(true);
});
}
}
exports.default = WsCtrl;
function _defaultResolveFunc(socket, request, options) {
const { clientId: cid, stomp } = options || {};
const clientId = cid || request.socket.remoteAddress;
const clientName = `[${clientId}]`;
const logTail = color_cc_1.default.gray(`from ${clientName}`);
const logName = color_cc_1.default.gray('socket:');
socket.send(JSON.stringify({ success: true, data: `WsServer: Connection established! Hello, client! ${clientName}` }));
socket.on('message', (message, isBinary) => {
const recived = typeof message === 'string' ? message : Buffer.isBuffer(message) ? message === null || message === void 0 ? void 0 : message.toString() : JSON.stringify(message);
let recivedData = recived;
if (stomp) {
recivedData = (0, parser_1.parseStompMsg)(recived);
print_1.Printer.log(LOGFLAG_WS, logName, 'Received stomp message', logTail);
print_1.Printer.log(LOGFLAG_WS, logName, '<= stompData:', recivedData);
}
else {
print_1.Printer.log(LOGFLAG_WS, logName, `Received message <= "${color_cc_1.default.green(recived)}"`, isBinary ? color_cc_1.default.gray('binary') : '', logTail);
}
const recivedDataStr = typeof recivedData === 'string' ? recivedData : JSON.stringify(recivedData);
const msgData = {
success: true,
data: `WsServer: I've recived your message(${recivedDataStr})`,
};
print_1.Printer.log(LOGFLAG_WS, logName, `Send response to ${color_cc_1.default.gray(clientName)} =>`, msgData);
socket.send(JSON.stringify(msgData));
console.log('\n');
});
socket.on('open', () => {
print_1.Printer.log(LOGFLAG_WS, logName, `Client ${color_cc_1.default.gray(clientId)} open.`, logTail);
});
socket.on('upgrade', (request) => {
const clientAddr = request.socket.remoteAddress;
print_1.Printer.log(LOGFLAG_WS, logName, `Client ${clientAddr} upgraded.`, logTail);
});
socket.on('ping', (data) => {
print_1.Printer.log(LOGFLAG_WS, logName, `Received ping => ${(data === null || data === void 0 ? void 0 : data.toString()) || ''}`, logTail);
socket.pong(`Pong: ${data === null || data === void 0 ? void 0 : data.toString()}`);
});
socket.on('pong', (data) => {
print_1.Printer.log(LOGFLAG_WS, logName, `Received pong => ${(data === null || data === void 0 ? void 0 : data.toString()) || ''}`, logTail);
});
socket.on('error', (err) => {
const errMsg = (err === null || err === void 0 ? void 0 : err.message) || (err === null || err === void 0 ? void 0 : err.toString()) || 'unknow error';
print_1.Printer.error(LOGFLAG_WS, logName, `CLient error: ${color_cc_1.default.red(errMsg)}`, logTail);
print_1.Printer.error(LOGFLAG_WS, err, '\n');
});
socket.on('unexpected-response', (request, response) => {
const exceptDetail = `${response.statusCode} ${response.statusMessage}`;
print_1.Printer.error(LOGFLAG_WS, logName, `CLient unexpected-response: ${color_cc_1.default.red(exceptDetail)}`, logTail, '\n');
});
socket.on('close', (code, reason) => {
const closeDetail = `code=${code},reason=${reason.toString() || 'none'}`;
print_1.Printer.log(LOGFLAG_WS, logName, `Client close connection.(${color_cc_1.default.yellow(closeDetail)})`, logTail, '\n');
});
}
function _getBaseServerInfo(server) {
const isSecure = server instanceof https_1.default.Server;
const protocol = isSecure ? 'https' : 'http';
const { port, address } = (0, server_1.getAddrInfoByServer)(server) || {};
return { isSecure, protocol, host: address, port };
}
function _isHttpOrHttpsServer(server) {
return server instanceof http_1.default.Server || server instanceof https_1.default.Server;
}