UNPKG

@n1k1t/mock-server

Version:

The ultimate toolkit to intercept, transform, and simulate HTTP/WS traffic with type-safe expectations

119 lines 5.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WsExecutor = void 0; const rxjs_1 = require("rxjs"); const utils_1 = require("../../../utils"); const logger_1 = require("../../../logger"); const models_1 = require("../../models"); const logger = logger_1.Logger.build('Transports.Ws.Executor'); /** Got from `ws` package */ const checkStatusIsValid = (code) => (code >= 1000 && code <= 1014 && code !== 1004 && code !== 1005 && code !== 1006) || (code >= 3000 && code <= 4999); class WsExecutor extends models_1.Executor { async exec(context, options) { await super.exec(context, options).catch((error) => { if (error instanceof models_1.ExecutorManualError && error.is('ECONNABORTED')) { return context.socket.destroy(); } logger.error('Got unexpected error while execution', error?.stack ?? error); }); return context.complete(); } async match(context) { const expectation = await context.provider.storages.expectations.match(context.snapshot); if (!expectation && context.is(['handling'])) { context.assign({ outgoing: await this.reply(context, { type: 'plain', status: 1011, stream: (0, rxjs_1.from)([models_1.RequestMessage.build('outgoing', 'Expectation was not found')]), headers: {}, raw: {}, }), }); return null; } return expectation; } async forward(context, incoming, schema) { const configuration = await this .compileForwardingConfiguration(context, incoming, schema) .catch((error) => { context.snapshot.assign({ error: { code: 'UNKNOWN', message: error?.message ?? 'Unknown' }, outgoing: { type: 'plain', status: 1011, headers: {}, raw: {} }, }); logger.error('Got error while execution [compileForwardingConfiguration] method', error?.stack ?? error); throw error; }); context.additional.ws = models_1.WebSocketFactory .build(configuration) .compile() .connect(); return { schema, incoming, outgoing: { type: 'plain', status: 0, headers: await context.additional.ws.headers(), stream: context.additional.ws.observable, raw: {}, }, }; } async reply(context, outgoing) { await new Promise((resolve) => { context.provider.server.wss.once('headers', (headers) => { const formatted = (0, utils_1.formatHeaders)(outgoing.headers); Object.entries(formatted).forEach(([key, value]) => headers.some((line) => line.toLowerCase().startsWith(`${key}:`)) ? null : headers.push([key, value].join(': '))); }); context.provider.server.wss.handleUpgrade(context.request, context.socket, context.head, async (socket) => { socket.on('message', (data) => context.streams.incoming.next(models_1.RequestMessage.build('incoming', data))); await outgoing.stream?.forEach((payload) => { const message = payload instanceof models_1.RequestMessage ? payload.redirect('outgoing') : models_1.RequestMessage.build('outgoing', payload); context.streams.outgoing.next(message); socket.send(message.serialize()); }); const status = await context.additional.ws?.status() ?? null; if (outgoing.status === 0) { outgoing.status = status ?? 1000; } if (context.additional.ws && context.snapshot.forwarded?.outgoing) { context.snapshot.forwarded.outgoing.status = status ?? outgoing.status; } if (!checkStatusIsValid(outgoing.status)) { const fallback = status !== null ? checkStatusIsValid(status) ? status : 1011 : 1011; logger.warn(`Outgoing status [${outgoing.status}] is invalid. Fallback to [${fallback}]`); outgoing.status = fallback; } socket.close(outgoing.status); resolve(); }); }); return outgoing; } /** Compiles WS request configuration to forward */ async compileForwardingConfiguration(context, incoming, schema) { const url = new URL(schema.url ?? incoming.path, schema.baseUrl); return { timeout: schema.timeout, headers: (0, utils_1.formatHeaders)(Object.assign(incoming.headers, { ...(schema.options?.overrideHost !== false && { host: url.host }), })), ...(schema.url && { url: schema.url }), ...(schema.baseUrl && { baseURL: schema.baseUrl, url: incoming.path }), query: context.incoming.query, }; } } exports.WsExecutor = WsExecutor; //# sourceMappingURL=executor.js.map