@n1k1t/mock-server
Version:
The ultimate toolkit to intercept, transform, and simulate HTTP/WS traffic with type-safe expectations
119 lines • 5.4 kB
JavaScript
;
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