@zimic/interceptor 0.17.0-canary.1 → 0.17.0-canary.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-3SKHNQLL.mjs → chunk-L75WKVZO.mjs} +508 -60
- package/dist/chunk-L75WKVZO.mjs.map +1 -0
- package/dist/{chunk-TYHJPU5G.js → chunk-PURXNE6R.js} +519 -61
- package/dist/chunk-PURXNE6R.js.map +1 -0
- package/dist/cli.js +141 -17
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +137 -13
- package/dist/cli.mjs.map +1 -1
- package/dist/http.d.ts +14 -0
- package/dist/http.js +449 -269
- package/dist/http.js.map +1 -1
- package/dist/http.mjs +449 -269
- package/dist/http.mjs.map +1 -1
- package/dist/scripts/postinstall.js +6 -6
- package/dist/scripts/postinstall.js.map +1 -1
- package/dist/scripts/postinstall.mjs +5 -5
- package/dist/scripts/postinstall.mjs.map +1 -1
- package/dist/server.d.ts +16 -0
- package/dist/server.js +6 -6
- package/dist/server.mjs +1 -1
- package/package.json +11 -10
- package/src/cli/browser/init.ts +5 -6
- package/src/cli/cli.ts +140 -55
- package/src/cli/server/start.ts +22 -7
- package/src/cli/server/token/create.ts +33 -0
- package/src/cli/server/token/list.ts +23 -0
- package/src/cli/server/token/remove.ts +22 -0
- package/src/http/interceptor/HttpInterceptorClient.ts +49 -27
- package/src/http/interceptor/HttpInterceptorStore.ts +25 -9
- package/src/http/interceptor/LocalHttpInterceptor.ts +6 -3
- package/src/http/interceptor/RemoteHttpInterceptor.ts +9 -4
- package/src/http/interceptor/types/options.ts +15 -0
- package/src/http/interceptorWorker/HttpInterceptorWorker.ts +14 -16
- package/src/http/interceptorWorker/RemoteHttpInterceptorWorker.ts +17 -12
- package/src/http/interceptorWorker/types/options.ts +1 -0
- package/src/http/requestHandler/errors/TimesCheckError.ts +1 -1
- package/src/server/InterceptorServer.ts +52 -8
- package/src/server/constants.ts +1 -1
- package/src/server/errors/InvalidInterceptorTokenError.ts +13 -0
- package/src/server/errors/InvalidInterceptorTokenFileError.ts +13 -0
- package/src/server/errors/InvalidInterceptorTokenValueError.ts +13 -0
- package/src/server/types/options.ts +9 -0
- package/src/server/types/public.ts +9 -0
- package/src/server/types/schema.ts +4 -4
- package/src/server/utils/auth.ts +301 -0
- package/src/server/utils/fetch.ts +3 -1
- package/src/utils/data.ts +13 -0
- package/src/utils/files.ts +14 -0
- package/src/utils/{console.ts → logging.ts} +5 -7
- package/src/utils/webSocket.ts +57 -11
- package/src/webSocket/WebSocketClient.ts +11 -5
- package/src/webSocket/WebSocketHandler.ts +72 -51
- package/src/webSocket/WebSocketServer.ts +25 -4
- package/src/webSocket/constants.ts +2 -0
- package/src/webSocket/errors/UnauthorizedWebSocketConnectionError.ts +11 -0
- package/src/webSocket/types.ts +49 -52
- package/dist/chunk-3SKHNQLL.mjs.map +0 -1
- package/dist/chunk-TYHJPU5G.js.map +0 -1
|
@@ -2,8 +2,14 @@ import { __name } from './chunk-CGILA3WO.mjs';
|
|
|
2
2
|
import { HTTP_METHODS, HttpHeaders, HttpSearchParams, HttpFormData, HTTP_METHODS_WITH_RESPONSE_BODY } from '@zimic/http';
|
|
3
3
|
import { normalizeNodeRequest, sendNodeResponse } from '@whatwg-node/server';
|
|
4
4
|
import { createServer } from 'http';
|
|
5
|
-
import
|
|
5
|
+
import color3 from 'picocolors';
|
|
6
6
|
import ClientSocket from 'isomorphic-ws';
|
|
7
|
+
import crypto from 'crypto';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import os from 'os';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import util from 'util';
|
|
12
|
+
import { z } from 'zod';
|
|
7
13
|
|
|
8
14
|
// src/server/errors/RunningInterceptorServerError.ts
|
|
9
15
|
var RunningInterceptorServerError = class extends Error {
|
|
@@ -112,6 +118,19 @@ function methodCanHaveResponseBody(method) {
|
|
|
112
118
|
}
|
|
113
119
|
__name(methodCanHaveResponseBody, "methodCanHaveResponseBody");
|
|
114
120
|
|
|
121
|
+
// src/webSocket/errors/UnauthorizedWebSocketConnectionError.ts
|
|
122
|
+
var UnauthorizedWebSocketConnectionError = class extends Error {
|
|
123
|
+
constructor(event) {
|
|
124
|
+
super(`${event.reason} (code ${event.code})`);
|
|
125
|
+
this.event = event;
|
|
126
|
+
this.name = "UnauthorizedWebSocketConnectionError";
|
|
127
|
+
}
|
|
128
|
+
static {
|
|
129
|
+
__name(this, "UnauthorizedWebSocketConnectionError");
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
var UnauthorizedWebSocketConnectionError_default = UnauthorizedWebSocketConnectionError;
|
|
133
|
+
|
|
115
134
|
// src/utils/webSocket.ts
|
|
116
135
|
var WebSocketTimeoutError = class extends Error {
|
|
117
136
|
static {
|
|
@@ -157,29 +176,58 @@ var WebSocketCloseTimeoutError = class extends WebSocketTimeoutError {
|
|
|
157
176
|
var DEFAULT_WEB_SOCKET_LIFECYCLE_TIMEOUT = 60 * 1e3;
|
|
158
177
|
var DEFAULT_WEB_SOCKET_MESSAGE_TIMEOUT = 3 * 60 * 1e3;
|
|
159
178
|
async function waitForOpenClientSocket(socket, options = {}) {
|
|
160
|
-
const { timeout: timeoutDuration = DEFAULT_WEB_SOCKET_LIFECYCLE_TIMEOUT } = options;
|
|
179
|
+
const { timeout: timeoutDuration = DEFAULT_WEB_SOCKET_LIFECYCLE_TIMEOUT, waitForAuthentication = false } = options;
|
|
161
180
|
const isAlreadyOpen = socket.readyState === socket.OPEN;
|
|
162
181
|
if (isAlreadyOpen) {
|
|
163
182
|
return;
|
|
164
183
|
}
|
|
165
184
|
await new Promise((resolve, reject) => {
|
|
166
|
-
function
|
|
185
|
+
function removeAllSocketListeners() {
|
|
186
|
+
socket.removeEventListener("message", handleSocketMessage);
|
|
167
187
|
socket.removeEventListener("open", handleOpenSuccess);
|
|
188
|
+
socket.removeEventListener("error", handleOpenError);
|
|
189
|
+
socket.removeEventListener("close", handleClose);
|
|
190
|
+
}
|
|
191
|
+
__name(removeAllSocketListeners, "removeAllSocketListeners");
|
|
192
|
+
function handleOpenError(error) {
|
|
193
|
+
removeAllSocketListeners();
|
|
168
194
|
reject(error);
|
|
169
195
|
}
|
|
170
196
|
__name(handleOpenError, "handleOpenError");
|
|
197
|
+
function handleClose(event) {
|
|
198
|
+
const isUnauthorized = event.code === 1008;
|
|
199
|
+
if (isUnauthorized) {
|
|
200
|
+
const unauthorizedError = new UnauthorizedWebSocketConnectionError_default(event);
|
|
201
|
+
handleOpenError(unauthorizedError);
|
|
202
|
+
} else {
|
|
203
|
+
handleOpenError(event);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
__name(handleClose, "handleClose");
|
|
171
207
|
const openTimeout = setTimeout(() => {
|
|
172
208
|
const timeoutError = new WebSocketOpenTimeoutError(timeoutDuration);
|
|
173
209
|
handleOpenError(timeoutError);
|
|
174
210
|
}, timeoutDuration);
|
|
175
211
|
function handleOpenSuccess() {
|
|
176
|
-
|
|
212
|
+
removeAllSocketListeners();
|
|
177
213
|
clearTimeout(openTimeout);
|
|
178
214
|
resolve();
|
|
179
215
|
}
|
|
180
216
|
__name(handleOpenSuccess, "handleOpenSuccess");
|
|
181
|
-
|
|
217
|
+
function handleSocketMessage(message) {
|
|
218
|
+
const hasValidAuth = message.data === "socket:auth:valid";
|
|
219
|
+
if (hasValidAuth) {
|
|
220
|
+
handleOpenSuccess();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
__name(handleSocketMessage, "handleSocketMessage");
|
|
224
|
+
if (waitForAuthentication) {
|
|
225
|
+
socket.addEventListener("message", handleSocketMessage);
|
|
226
|
+
} else {
|
|
227
|
+
socket.addEventListener("open", handleOpenSuccess);
|
|
228
|
+
}
|
|
182
229
|
socket.addEventListener("error", handleOpenError);
|
|
230
|
+
socket.addEventListener("close", handleClose);
|
|
183
231
|
});
|
|
184
232
|
}
|
|
185
233
|
__name(waitForOpenClientSocket, "waitForOpenClientSocket");
|
|
@@ -190,23 +238,28 @@ async function closeClientSocket(socket, options = {}) {
|
|
|
190
238
|
return;
|
|
191
239
|
}
|
|
192
240
|
await new Promise((resolve, reject) => {
|
|
193
|
-
function
|
|
194
|
-
socket.removeEventListener("
|
|
241
|
+
function removeAllSocketListeners() {
|
|
242
|
+
socket.removeEventListener("error", handleError);
|
|
243
|
+
socket.removeEventListener("close", handleClose);
|
|
244
|
+
}
|
|
245
|
+
__name(removeAllSocketListeners, "removeAllSocketListeners");
|
|
246
|
+
function handleError(error) {
|
|
247
|
+
removeAllSocketListeners();
|
|
195
248
|
reject(error);
|
|
196
249
|
}
|
|
197
|
-
__name(
|
|
250
|
+
__name(handleError, "handleError");
|
|
198
251
|
const closeTimeout = setTimeout(() => {
|
|
199
252
|
const timeoutError = new WebSocketCloseTimeoutError(timeoutDuration);
|
|
200
|
-
|
|
253
|
+
handleError(timeoutError);
|
|
201
254
|
}, timeoutDuration);
|
|
202
|
-
function
|
|
203
|
-
|
|
255
|
+
function handleClose() {
|
|
256
|
+
removeAllSocketListeners();
|
|
204
257
|
clearTimeout(closeTimeout);
|
|
205
258
|
resolve();
|
|
206
259
|
}
|
|
207
|
-
__name(
|
|
208
|
-
socket.addEventListener("error",
|
|
209
|
-
socket.addEventListener("close",
|
|
260
|
+
__name(handleClose, "handleClose");
|
|
261
|
+
socket.addEventListener("error", handleError);
|
|
262
|
+
socket.addEventListener("close", handleClose);
|
|
210
263
|
socket.close();
|
|
211
264
|
});
|
|
212
265
|
}
|
|
@@ -295,6 +348,12 @@ function removeArrayElement(array, element) {
|
|
|
295
348
|
}
|
|
296
349
|
__name(removeArrayElement, "removeArrayElement");
|
|
297
350
|
|
|
351
|
+
// src/utils/environment.ts
|
|
352
|
+
function isClientSide() {
|
|
353
|
+
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
354
|
+
}
|
|
355
|
+
__name(isClientSide, "isClientSide");
|
|
356
|
+
|
|
298
357
|
// ../zimic-utils/dist/import/createCachedDynamicImport.mjs
|
|
299
358
|
function createCachedDynamicImport(importModuleDynamically) {
|
|
300
359
|
let cachedImportResult;
|
|
@@ -307,11 +366,87 @@ __name(createCachedDynamicImport, "createCachedDynamicImport");
|
|
|
307
366
|
__name2(createCachedDynamicImport, "createCachedDynamicImport");
|
|
308
367
|
var createCachedDynamicImport_default = createCachedDynamicImport;
|
|
309
368
|
|
|
310
|
-
//
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
369
|
+
// ../zimic-utils/dist/logging/Logger.mjs
|
|
370
|
+
var Logger = class _Logger {
|
|
371
|
+
static {
|
|
372
|
+
__name(this, "_Logger");
|
|
373
|
+
}
|
|
374
|
+
static {
|
|
375
|
+
__name2(this, "Logger");
|
|
376
|
+
}
|
|
377
|
+
prefix;
|
|
378
|
+
raw;
|
|
379
|
+
constructor(options = {}) {
|
|
380
|
+
const { prefix } = options;
|
|
381
|
+
this.prefix = prefix;
|
|
382
|
+
this.raw = prefix ? new _Logger({ ...options, prefix: void 0 }) : this;
|
|
383
|
+
}
|
|
384
|
+
logWithLevel(level, ...messages) {
|
|
385
|
+
if (this.prefix) {
|
|
386
|
+
console[level](this.prefix, ...messages);
|
|
387
|
+
} else {
|
|
388
|
+
console[level](...messages);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
info(...messages) {
|
|
392
|
+
this.logWithLevel("log", ...messages);
|
|
393
|
+
}
|
|
394
|
+
warn(...messages) {
|
|
395
|
+
this.logWithLevel("warn", ...messages);
|
|
396
|
+
}
|
|
397
|
+
error(...messages) {
|
|
398
|
+
this.logWithLevel("error", ...messages);
|
|
399
|
+
}
|
|
400
|
+
table(headers, rows) {
|
|
401
|
+
const columnLengths = headers.map((header) => {
|
|
402
|
+
let maxValueLength = header.title.length;
|
|
403
|
+
for (const row of rows) {
|
|
404
|
+
const value = row[header.property];
|
|
405
|
+
if (value.length > maxValueLength) {
|
|
406
|
+
maxValueLength = value.length;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return maxValueLength;
|
|
410
|
+
});
|
|
411
|
+
const formattedRows = [];
|
|
412
|
+
const horizontalLine = columnLengths.map((length) => "\u2500".repeat(length));
|
|
413
|
+
formattedRows.push(horizontalLine, []);
|
|
414
|
+
for (let headerIndex = 0; headerIndex < headers.length; headerIndex++) {
|
|
415
|
+
const header = headers[headerIndex];
|
|
416
|
+
const columnLength = columnLengths[headerIndex];
|
|
417
|
+
const value = header.title;
|
|
418
|
+
formattedRows.at(-1)?.push(value.padEnd(columnLength, " "));
|
|
419
|
+
}
|
|
420
|
+
formattedRows.push(horizontalLine);
|
|
421
|
+
for (const row of rows) {
|
|
422
|
+
formattedRows.push([]);
|
|
423
|
+
for (let headerIndex = 0; headerIndex < headers.length; headerIndex++) {
|
|
424
|
+
const header = headers[headerIndex];
|
|
425
|
+
const columnLength = columnLengths[headerIndex];
|
|
426
|
+
const value = row[header.property];
|
|
427
|
+
formattedRows.at(-1)?.push(value.padEnd(columnLength, " "));
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
formattedRows.push(horizontalLine);
|
|
431
|
+
const formattedTable = formattedRows.map((row, index) => {
|
|
432
|
+
const isFirstLine = index === 0;
|
|
433
|
+
if (isFirstLine) {
|
|
434
|
+
return `\u250C\u2500${row.join("\u2500\u252C\u2500")}\u2500\u2510`;
|
|
435
|
+
}
|
|
436
|
+
const isLineAfterHeaders = index === 2;
|
|
437
|
+
if (isLineAfterHeaders) {
|
|
438
|
+
return `\u251C\u2500${row.join("\u2500\u253C\u2500")}\u2500\u2524`;
|
|
439
|
+
}
|
|
440
|
+
const isLastLine = index === formattedRows.length - 1;
|
|
441
|
+
if (isLastLine) {
|
|
442
|
+
return `\u2514\u2500${row.join("\u2500\u2534\u2500")}\u2500\u2518`;
|
|
443
|
+
}
|
|
444
|
+
return `\u2502 ${row.join(" \u2502 ")} \u2502`;
|
|
445
|
+
}).join("\n");
|
|
446
|
+
this.logWithLevel("log", formattedTable);
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
var Logger_default = Logger;
|
|
315
450
|
|
|
316
451
|
// src/utils/files.ts
|
|
317
452
|
var importFile = createCachedDynamicImport_default(
|
|
@@ -320,16 +455,30 @@ var importFile = createCachedDynamicImport_default(
|
|
|
320
455
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
321
456
|
async () => globalThis.File ?? (await import('buffer')).File
|
|
322
457
|
);
|
|
458
|
+
var importFilesystem = createCachedDynamicImport_default(() => import('fs'));
|
|
459
|
+
async function pathExists(path2) {
|
|
460
|
+
const fs2 = await importFilesystem();
|
|
461
|
+
try {
|
|
462
|
+
await fs2.promises.access(path2);
|
|
463
|
+
return true;
|
|
464
|
+
} catch {
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
__name(pathExists, "pathExists");
|
|
323
469
|
|
|
324
|
-
// src/utils/
|
|
470
|
+
// src/utils/logging.ts
|
|
471
|
+
var logger = new Logger_default({
|
|
472
|
+
prefix: color3.cyan("[@zimic/interceptor]")
|
|
473
|
+
});
|
|
325
474
|
var importUtil = createCachedDynamicImport_default(() => import('util'));
|
|
326
475
|
async function formatValueToLog(value, options = {}) {
|
|
327
476
|
if (isClientSide()) {
|
|
328
477
|
return value;
|
|
329
478
|
}
|
|
330
479
|
const { colors = true } = options;
|
|
331
|
-
const
|
|
332
|
-
return
|
|
480
|
+
const util2 = await importUtil();
|
|
481
|
+
return util2.inspect(value, {
|
|
333
482
|
colors,
|
|
334
483
|
compact: true,
|
|
335
484
|
depth: Infinity,
|
|
@@ -340,12 +489,6 @@ async function formatValueToLog(value, options = {}) {
|
|
|
340
489
|
});
|
|
341
490
|
}
|
|
342
491
|
__name(formatValueToLog, "formatValueToLog");
|
|
343
|
-
function logWithPrefix(messageOrMessages, options = {}) {
|
|
344
|
-
const { method = "log" } = options;
|
|
345
|
-
const messages = Array.isArray(messageOrMessages) ? messageOrMessages : [messageOrMessages];
|
|
346
|
-
console[method](color2.cyan("[@zimic/interceptor]"), ...messages);
|
|
347
|
-
}
|
|
348
|
-
__name(logWithPrefix, "logWithPrefix");
|
|
349
492
|
|
|
350
493
|
// src/http/requestHandler/types/requests.ts
|
|
351
494
|
var HTTP_INTERCEPTOR_REQUEST_HIDDEN_PROPERTIES = Object.freeze(
|
|
@@ -705,21 +848,18 @@ var HttpInterceptorWorker = class _HttpInterceptorWorker {
|
|
|
705
848
|
formatValueToLog(request.searchParams.toObject()),
|
|
706
849
|
formatValueToLog(request.body)
|
|
707
850
|
]);
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
`${action === "bypass" ? "Warning:" : "Error:"} Request was not handled and was ${action === "bypass" ? color2.yellow("bypassed") : color2.red("rejected")}.
|
|
851
|
+
logger[action === "bypass" ? "warn" : "error"](
|
|
852
|
+
`${action === "bypass" ? "Warning:" : "Error:"} Request was not handled and was ${action === "bypass" ? color3.yellow("bypassed") : color3.red("rejected")}.
|
|
711
853
|
|
|
712
854
|
`,
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
],
|
|
722
|
-
{ method: action === "bypass" ? "warn" : "error" }
|
|
855
|
+
`${request.method} ${request.url}`,
|
|
856
|
+
"\n Headers:",
|
|
857
|
+
formattedHeaders,
|
|
858
|
+
"\n Search params:",
|
|
859
|
+
formattedSearchParams,
|
|
860
|
+
"\n Body:",
|
|
861
|
+
formattedBody,
|
|
862
|
+
"\n\nLearn more: https://github.com/zimicjs/zimic/wiki/api\u2010zimic\u2010interceptor\u2010http#unhandled-requests"
|
|
723
863
|
);
|
|
724
864
|
}
|
|
725
865
|
};
|
|
@@ -751,6 +891,17 @@ function convertBase64ToArrayBuffer(base64Value) {
|
|
|
751
891
|
}
|
|
752
892
|
}
|
|
753
893
|
__name(convertBase64ToArrayBuffer, "convertBase64ToArrayBuffer");
|
|
894
|
+
var HEX_REGEX = /^[a-z0-9]+$/;
|
|
895
|
+
function convertHexLengthToByteLength(hexLength) {
|
|
896
|
+
return Math.ceil(hexLength / 2);
|
|
897
|
+
}
|
|
898
|
+
__name(convertHexLengthToByteLength, "convertHexLengthToByteLength");
|
|
899
|
+
var BASE64URL_REGEX = /^[a-zA-Z0-9-_]+$/;
|
|
900
|
+
function convertHexLengthToBase64urlLength(hexLength) {
|
|
901
|
+
const byteLength = convertHexLengthToByteLength(hexLength);
|
|
902
|
+
return Math.ceil(byteLength * 4 / 3);
|
|
903
|
+
}
|
|
904
|
+
__name(convertHexLengthToBase64urlLength, "convertHexLengthToBase64urlLength");
|
|
754
905
|
|
|
755
906
|
// src/utils/fetch.ts
|
|
756
907
|
async function serializeRequest(request) {
|
|
@@ -788,6 +939,9 @@ var importCrypto = createCachedDynamicImport_default(async () => {
|
|
|
788
939
|
return globalCrypto ?? await import('crypto');
|
|
789
940
|
});
|
|
790
941
|
|
|
942
|
+
// src/webSocket/constants.ts
|
|
943
|
+
var WEB_SOCKET_CONTROL_MESSAGES = Object.freeze(["socket:auth:valid"]);
|
|
944
|
+
|
|
791
945
|
// src/webSocket/errors/InvalidWebSocketMessage.ts
|
|
792
946
|
var InvalidWebSocketMessage = class extends Error {
|
|
793
947
|
static {
|
|
@@ -828,8 +982,11 @@ var WebSocketHandler = class {
|
|
|
828
982
|
this.socketTimeout = options.socketTimeout ?? DEFAULT_WEB_SOCKET_LIFECYCLE_TIMEOUT;
|
|
829
983
|
this.messageTimeout = options.messageTimeout ?? DEFAULT_WEB_SOCKET_MESSAGE_TIMEOUT;
|
|
830
984
|
}
|
|
831
|
-
async registerSocket(socket) {
|
|
832
|
-
const openPromise = waitForOpenClientSocket(socket, {
|
|
985
|
+
async registerSocket(socket, options = {}) {
|
|
986
|
+
const openPromise = waitForOpenClientSocket(socket, {
|
|
987
|
+
timeout: this.socketTimeout,
|
|
988
|
+
waitForAuthentication: options.waitForAuthentication
|
|
989
|
+
});
|
|
833
990
|
const handleSocketMessage = /* @__PURE__ */ __name(async (rawMessage) => {
|
|
834
991
|
await this.handleSocketMessage(socket, rawMessage);
|
|
835
992
|
}, "handleSocketMessage");
|
|
@@ -842,8 +999,8 @@ var WebSocketHandler = class {
|
|
|
842
999
|
socket.addEventListener("error", handleSocketError);
|
|
843
1000
|
const handleSocketClose = /* @__PURE__ */ __name(() => {
|
|
844
1001
|
socket.removeEventListener("message", handleSocketMessage);
|
|
845
|
-
socket.removeEventListener("error", handleSocketError);
|
|
846
1002
|
socket.removeEventListener("close", handleSocketClose);
|
|
1003
|
+
socket.removeEventListener("error", handleSocketError);
|
|
847
1004
|
this.removeSocket(socket);
|
|
848
1005
|
}, "handleSocketClose");
|
|
849
1006
|
socket.addEventListener("close", handleSocketClose);
|
|
@@ -851,6 +1008,9 @@ var WebSocketHandler = class {
|
|
|
851
1008
|
}
|
|
852
1009
|
handleSocketMessage = /* @__PURE__ */ __name(async (socket, rawMessage) => {
|
|
853
1010
|
try {
|
|
1011
|
+
if (this.isControlMessageData(rawMessage.data)) {
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
854
1014
|
const stringifiedMessageData = this.readRawMessageData(rawMessage.data);
|
|
855
1015
|
const parsedMessageData = this.parseMessage(stringifiedMessageData);
|
|
856
1016
|
await this.notifyListeners(parsedMessageData, socket);
|
|
@@ -858,6 +1018,9 @@ var WebSocketHandler = class {
|
|
|
858
1018
|
console.error(error);
|
|
859
1019
|
}
|
|
860
1020
|
}, "handleSocketMessage");
|
|
1021
|
+
isControlMessageData(messageData) {
|
|
1022
|
+
return typeof messageData === "string" && WEB_SOCKET_CONTROL_MESSAGES.includes(messageData);
|
|
1023
|
+
}
|
|
861
1024
|
readRawMessageData(data) {
|
|
862
1025
|
if (typeof data === "string") {
|
|
863
1026
|
return data;
|
|
@@ -872,7 +1035,7 @@ var WebSocketHandler = class {
|
|
|
872
1035
|
} catch {
|
|
873
1036
|
throw new InvalidWebSocketMessage_default(stringifiedMessage);
|
|
874
1037
|
}
|
|
875
|
-
if (!this.
|
|
1038
|
+
if (!this.isMessage(parsedMessage)) {
|
|
876
1039
|
throw new InvalidWebSocketMessage_default(stringifiedMessage);
|
|
877
1040
|
}
|
|
878
1041
|
if (this.isReplyMessage(parsedMessage)) {
|
|
@@ -889,7 +1052,7 @@ var WebSocketHandler = class {
|
|
|
889
1052
|
data: parsedMessage.data
|
|
890
1053
|
};
|
|
891
1054
|
}
|
|
892
|
-
|
|
1055
|
+
isMessage(message) {
|
|
893
1056
|
return typeof message === "object" && message !== null && "id" in message && typeof message.id === "string" && "channel" in message && typeof message.channel === "string" && (!("requestId" in message) || typeof message.requestId === "string");
|
|
894
1057
|
}
|
|
895
1058
|
async notifyListeners(message, socket) {
|
|
@@ -925,9 +1088,9 @@ var WebSocketHandler = class {
|
|
|
925
1088
|
this.sockets.delete(socket);
|
|
926
1089
|
}
|
|
927
1090
|
async createEventMessage(channel, eventData) {
|
|
928
|
-
const
|
|
1091
|
+
const crypto2 = await importCrypto();
|
|
929
1092
|
const eventMessage = {
|
|
930
|
-
id:
|
|
1093
|
+
id: crypto2.randomUUID(),
|
|
931
1094
|
channel,
|
|
932
1095
|
data: eventData
|
|
933
1096
|
};
|
|
@@ -977,9 +1140,9 @@ var WebSocketHandler = class {
|
|
|
977
1140
|
}
|
|
978
1141
|
}
|
|
979
1142
|
async createReplyMessage(request, replyData) {
|
|
980
|
-
const
|
|
1143
|
+
const crypto2 = await importCrypto();
|
|
981
1144
|
const replyMessage = {
|
|
982
|
-
id:
|
|
1145
|
+
id: crypto2.randomUUID(),
|
|
983
1146
|
channel: request.channel,
|
|
984
1147
|
requestId: request.id,
|
|
985
1148
|
data: replyData
|
|
@@ -1060,12 +1223,14 @@ var WebSocketServer = class extends WebSocketHandler_default {
|
|
|
1060
1223
|
}
|
|
1061
1224
|
webSocketServer;
|
|
1062
1225
|
httpServer;
|
|
1226
|
+
authenticate;
|
|
1063
1227
|
constructor(options) {
|
|
1064
1228
|
super({
|
|
1065
1229
|
socketTimeout: options.socketTimeout,
|
|
1066
1230
|
messageTimeout: options.messageTimeout
|
|
1067
1231
|
});
|
|
1068
1232
|
this.httpServer = options.httpServer;
|
|
1233
|
+
this.authenticate = options.authenticate;
|
|
1069
1234
|
}
|
|
1070
1235
|
get isRunning() {
|
|
1071
1236
|
return this.webSocketServer !== void 0;
|
|
@@ -1078,9 +1243,17 @@ var WebSocketServer = class extends WebSocketHandler_default {
|
|
|
1078
1243
|
webSocketServer.on("error", (error) => {
|
|
1079
1244
|
console.error(error);
|
|
1080
1245
|
});
|
|
1081
|
-
webSocketServer.on("connection", async (socket) => {
|
|
1246
|
+
webSocketServer.on("connection", async (socket, request) => {
|
|
1247
|
+
if (this.authenticate) {
|
|
1248
|
+
const result = await this.authenticate(socket, request);
|
|
1249
|
+
if (!result.isValid) {
|
|
1250
|
+
socket.close(1008, result.message);
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1082
1254
|
try {
|
|
1083
1255
|
await super.registerSocket(socket);
|
|
1256
|
+
socket.send("socket:auth:valid");
|
|
1084
1257
|
} catch (error) {
|
|
1085
1258
|
webSocketServer.emit("error", error);
|
|
1086
1259
|
}
|
|
@@ -1101,8 +1274,242 @@ var WebSocketServer = class extends WebSocketHandler_default {
|
|
|
1101
1274
|
};
|
|
1102
1275
|
var WebSocketServer_default = WebSocketServer;
|
|
1103
1276
|
|
|
1277
|
+
// src/server/errors/InvalidInterceptorTokenError.ts
|
|
1278
|
+
var InvalidInterceptorTokenError = class extends Error {
|
|
1279
|
+
static {
|
|
1280
|
+
__name(this, "InvalidInterceptorTokenError");
|
|
1281
|
+
}
|
|
1282
|
+
constructor(tokenId) {
|
|
1283
|
+
super(`Invalid interceptor token: ${tokenId}`);
|
|
1284
|
+
this.name = "InvalidInterceptorTokenError";
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
var InvalidInterceptorTokenError_default = InvalidInterceptorTokenError;
|
|
1288
|
+
|
|
1289
|
+
// src/server/errors/InvalidInterceptorTokenFileError.ts
|
|
1290
|
+
var InvalidInterceptorTokenFileError = class extends Error {
|
|
1291
|
+
static {
|
|
1292
|
+
__name(this, "InvalidInterceptorTokenFileError");
|
|
1293
|
+
}
|
|
1294
|
+
constructor(tokenFilePath, validationErrorMessage) {
|
|
1295
|
+
super(`Invalid interceptor token file ${tokenFilePath}: ${validationErrorMessage}`);
|
|
1296
|
+
this.name = "InvalidInterceptorTokenFileError";
|
|
1297
|
+
}
|
|
1298
|
+
};
|
|
1299
|
+
var InvalidInterceptorTokenFileError_default = InvalidInterceptorTokenFileError;
|
|
1300
|
+
|
|
1301
|
+
// src/server/errors/InvalidInterceptorTokenValueError.ts
|
|
1302
|
+
var InvalidInterceptorTokenValueError = class extends Error {
|
|
1303
|
+
static {
|
|
1304
|
+
__name(this, "InvalidInterceptorTokenValueError");
|
|
1305
|
+
}
|
|
1306
|
+
constructor(tokenValue) {
|
|
1307
|
+
super(`Invalid interceptor token value: ${tokenValue}`);
|
|
1308
|
+
this.name = "InvalidInterceptorTokenValueError";
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1311
|
+
var InvalidInterceptorTokenValueError_default = InvalidInterceptorTokenValueError;
|
|
1312
|
+
|
|
1313
|
+
// src/server/utils/auth.ts
|
|
1314
|
+
var DEFAULT_INTERCEPTOR_TOKENS_DIRECTORY = path.join(
|
|
1315
|
+
".zimic",
|
|
1316
|
+
"interceptor",
|
|
1317
|
+
"server",
|
|
1318
|
+
`tokens${""}`
|
|
1319
|
+
);
|
|
1320
|
+
var INTERCEPTOR_TOKEN_ID_HEX_LENGTH = 32;
|
|
1321
|
+
var INTERCEPTOR_TOKEN_SECRET_HEX_LENGTH = 64;
|
|
1322
|
+
var INTERCEPTOR_TOKEN_VALUE_HEX_LENGTH = INTERCEPTOR_TOKEN_ID_HEX_LENGTH + INTERCEPTOR_TOKEN_SECRET_HEX_LENGTH;
|
|
1323
|
+
var INTERCEPTOR_TOKEN_VALUE_BASE64URL_LENGTH = convertHexLengthToBase64urlLength(
|
|
1324
|
+
INTERCEPTOR_TOKEN_VALUE_HEX_LENGTH
|
|
1325
|
+
);
|
|
1326
|
+
var INTERCEPTOR_TOKEN_SALT_HEX_LENGTH = 64;
|
|
1327
|
+
var INTERCEPTOR_TOKEN_HASH_ITERATIONS = Number("1000000");
|
|
1328
|
+
var INTERCEPTOR_TOKEN_HASH_HEX_LENGTH = 128;
|
|
1329
|
+
var INTERCEPTOR_TOKEN_HASH_ALGORITHM = "sha512";
|
|
1330
|
+
var pbkdf2 = util.promisify(crypto.pbkdf2);
|
|
1331
|
+
async function hashInterceptorToken(plainToken, salt) {
|
|
1332
|
+
const hashBuffer = await pbkdf2(
|
|
1333
|
+
plainToken,
|
|
1334
|
+
salt,
|
|
1335
|
+
INTERCEPTOR_TOKEN_HASH_ITERATIONS,
|
|
1336
|
+
convertHexLengthToByteLength(INTERCEPTOR_TOKEN_HASH_HEX_LENGTH),
|
|
1337
|
+
INTERCEPTOR_TOKEN_HASH_ALGORITHM
|
|
1338
|
+
);
|
|
1339
|
+
const hash = hashBuffer.toString("hex");
|
|
1340
|
+
return hash;
|
|
1341
|
+
}
|
|
1342
|
+
__name(hashInterceptorToken, "hashInterceptorToken");
|
|
1343
|
+
function createInterceptorTokenId() {
|
|
1344
|
+
return crypto.randomUUID().replace(/[^a-z0-9]/g, "");
|
|
1345
|
+
}
|
|
1346
|
+
__name(createInterceptorTokenId, "createInterceptorTokenId");
|
|
1347
|
+
function isValidInterceptorTokenId(tokenId) {
|
|
1348
|
+
return tokenId.length === INTERCEPTOR_TOKEN_ID_HEX_LENGTH && HEX_REGEX.test(tokenId);
|
|
1349
|
+
}
|
|
1350
|
+
__name(isValidInterceptorTokenId, "isValidInterceptorTokenId");
|
|
1351
|
+
function isValidInterceptorTokenValue(tokenValue) {
|
|
1352
|
+
return tokenValue.length === INTERCEPTOR_TOKEN_VALUE_BASE64URL_LENGTH && BASE64URL_REGEX.test(tokenValue);
|
|
1353
|
+
}
|
|
1354
|
+
__name(isValidInterceptorTokenValue, "isValidInterceptorTokenValue");
|
|
1355
|
+
async function createInterceptorTokensDirectory(tokensDirectory) {
|
|
1356
|
+
try {
|
|
1357
|
+
const parentTokensDirectory = path.dirname(tokensDirectory);
|
|
1358
|
+
await fs.promises.mkdir(parentTokensDirectory, { recursive: true });
|
|
1359
|
+
await fs.promises.mkdir(tokensDirectory, { mode: 448, recursive: true });
|
|
1360
|
+
await fs.promises.appendFile(path.join(tokensDirectory, ".gitignore"), `*${os.EOL}`, { encoding: "utf-8" });
|
|
1361
|
+
} catch (error) {
|
|
1362
|
+
logger.error(
|
|
1363
|
+
`${color3.red(color3.bold("\u2716"))} Failed to create the tokens directory: ${color3.magenta(tokensDirectory)}`
|
|
1364
|
+
);
|
|
1365
|
+
throw error;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
__name(createInterceptorTokensDirectory, "createInterceptorTokensDirectory");
|
|
1369
|
+
var interceptorTokenFileContentSchema = z.object({
|
|
1370
|
+
version: z.literal(1),
|
|
1371
|
+
token: z.object({
|
|
1372
|
+
id: z.string().length(INTERCEPTOR_TOKEN_ID_HEX_LENGTH).regex(HEX_REGEX),
|
|
1373
|
+
name: z.string().optional(),
|
|
1374
|
+
secret: z.object({
|
|
1375
|
+
hash: z.string().length(INTERCEPTOR_TOKEN_HASH_HEX_LENGTH).regex(HEX_REGEX),
|
|
1376
|
+
salt: z.string().length(INTERCEPTOR_TOKEN_SALT_HEX_LENGTH).regex(HEX_REGEX)
|
|
1377
|
+
}),
|
|
1378
|
+
createdAt: z.string().datetime().transform((value) => new Date(value))
|
|
1379
|
+
})
|
|
1380
|
+
});
|
|
1381
|
+
async function saveInterceptorTokenToFile(tokensDirectory, token) {
|
|
1382
|
+
const tokeFilePath = path.join(tokensDirectory, token.id);
|
|
1383
|
+
const persistedToken = {
|
|
1384
|
+
id: token.id,
|
|
1385
|
+
name: token.name,
|
|
1386
|
+
secret: {
|
|
1387
|
+
hash: token.secret.hash,
|
|
1388
|
+
salt: token.secret.salt
|
|
1389
|
+
},
|
|
1390
|
+
createdAt: token.createdAt.toISOString()
|
|
1391
|
+
};
|
|
1392
|
+
const tokenFileContent = interceptorTokenFileContentSchema.parse({
|
|
1393
|
+
version: 1,
|
|
1394
|
+
token: persistedToken
|
|
1395
|
+
});
|
|
1396
|
+
await fs.promises.writeFile(tokeFilePath, JSON.stringify(tokenFileContent, null, 2), {
|
|
1397
|
+
mode: 384,
|
|
1398
|
+
encoding: "utf-8"
|
|
1399
|
+
});
|
|
1400
|
+
return tokeFilePath;
|
|
1401
|
+
}
|
|
1402
|
+
__name(saveInterceptorTokenToFile, "saveInterceptorTokenToFile");
|
|
1403
|
+
async function readInterceptorTokenFromFile(tokenId, options) {
|
|
1404
|
+
if (!isValidInterceptorTokenId(tokenId)) {
|
|
1405
|
+
throw new InvalidInterceptorTokenError_default(tokenId);
|
|
1406
|
+
}
|
|
1407
|
+
const tokenFilePath = path.join(options.tokensDirectory, tokenId);
|
|
1408
|
+
const tokenFileExists = await pathExists(tokenFilePath);
|
|
1409
|
+
if (!tokenFileExists) {
|
|
1410
|
+
return null;
|
|
1411
|
+
}
|
|
1412
|
+
const tokenFileContentAsString = await fs.promises.readFile(tokenFilePath, { encoding: "utf-8" });
|
|
1413
|
+
const validation = interceptorTokenFileContentSchema.safeParse(JSON.parse(tokenFileContentAsString));
|
|
1414
|
+
if (!validation.success) {
|
|
1415
|
+
throw new InvalidInterceptorTokenFileError_default(tokenFilePath, validation.error.message);
|
|
1416
|
+
}
|
|
1417
|
+
return validation.data.token;
|
|
1418
|
+
}
|
|
1419
|
+
__name(readInterceptorTokenFromFile, "readInterceptorTokenFromFile");
|
|
1420
|
+
async function createInterceptorToken(options) {
|
|
1421
|
+
const { name, tokensDirectory } = options;
|
|
1422
|
+
const tokensDirectoryExists = await pathExists(tokensDirectory);
|
|
1423
|
+
if (!tokensDirectoryExists) {
|
|
1424
|
+
await createInterceptorTokensDirectory(tokensDirectory);
|
|
1425
|
+
}
|
|
1426
|
+
const tokenId = createInterceptorTokenId();
|
|
1427
|
+
if (!isValidInterceptorTokenId(tokenId)) {
|
|
1428
|
+
throw new InvalidInterceptorTokenError_default(tokenId);
|
|
1429
|
+
}
|
|
1430
|
+
const tokenSecretSizeInBytes = convertHexLengthToByteLength(INTERCEPTOR_TOKEN_SECRET_HEX_LENGTH);
|
|
1431
|
+
const tokenSecret = crypto.randomBytes(tokenSecretSizeInBytes).toString("hex");
|
|
1432
|
+
const tokenSecretSaltSizeInBytes = convertHexLengthToByteLength(INTERCEPTOR_TOKEN_SALT_HEX_LENGTH);
|
|
1433
|
+
const tokenSecretSalt = crypto.randomBytes(tokenSecretSaltSizeInBytes).toString("hex");
|
|
1434
|
+
const tokenSecretHash = await hashInterceptorToken(tokenSecret, tokenSecretSalt);
|
|
1435
|
+
const tokenValue = Buffer.from(`${tokenId}${tokenSecret}`, "hex").toString("base64url");
|
|
1436
|
+
if (!isValidInterceptorTokenValue(tokenValue)) {
|
|
1437
|
+
throw new InvalidInterceptorTokenValueError_default(tokenValue);
|
|
1438
|
+
}
|
|
1439
|
+
const token = {
|
|
1440
|
+
id: tokenId,
|
|
1441
|
+
name,
|
|
1442
|
+
secret: {
|
|
1443
|
+
hash: tokenSecretHash,
|
|
1444
|
+
salt: tokenSecretSalt,
|
|
1445
|
+
value: tokenSecret
|
|
1446
|
+
},
|
|
1447
|
+
value: tokenValue,
|
|
1448
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
1449
|
+
};
|
|
1450
|
+
await saveInterceptorTokenToFile(tokensDirectory, token);
|
|
1451
|
+
return token;
|
|
1452
|
+
}
|
|
1453
|
+
__name(createInterceptorToken, "createInterceptorToken");
|
|
1454
|
+
async function listInterceptorTokens(options) {
|
|
1455
|
+
const tokensDirectoryExists = await pathExists(options.tokensDirectory);
|
|
1456
|
+
if (!tokensDirectoryExists) {
|
|
1457
|
+
return [];
|
|
1458
|
+
}
|
|
1459
|
+
const files = await fs.promises.readdir(options.tokensDirectory);
|
|
1460
|
+
const tokenReadPromises = files.map(async (file) => {
|
|
1461
|
+
if (!isValidInterceptorTokenId(file)) {
|
|
1462
|
+
return null;
|
|
1463
|
+
}
|
|
1464
|
+
const tokenId = file;
|
|
1465
|
+
const token = await readInterceptorTokenFromFile(tokenId, options);
|
|
1466
|
+
return token;
|
|
1467
|
+
});
|
|
1468
|
+
const tokenCandidates = await Promise.allSettled(tokenReadPromises);
|
|
1469
|
+
const tokens = [];
|
|
1470
|
+
for (const tokenCandidate of tokenCandidates) {
|
|
1471
|
+
if (tokenCandidate.status === "rejected") {
|
|
1472
|
+
console.error(tokenCandidate.reason);
|
|
1473
|
+
} else if (tokenCandidate.value !== null) {
|
|
1474
|
+
tokens.push(tokenCandidate.value);
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
tokens.sort((token, otherToken) => token.createdAt.getTime() - otherToken.createdAt.getTime());
|
|
1478
|
+
return tokens;
|
|
1479
|
+
}
|
|
1480
|
+
__name(listInterceptorTokens, "listInterceptorTokens");
|
|
1481
|
+
async function validateInterceptorToken(tokenValue, options) {
|
|
1482
|
+
if (!isValidInterceptorTokenValue(tokenValue)) {
|
|
1483
|
+
throw new InvalidInterceptorTokenValueError_default(tokenValue);
|
|
1484
|
+
}
|
|
1485
|
+
const decodedTokenValue = Buffer.from(tokenValue, "base64url").toString("hex");
|
|
1486
|
+
const tokenId = decodedTokenValue.slice(0, INTERCEPTOR_TOKEN_ID_HEX_LENGTH);
|
|
1487
|
+
const tokenSecret = decodedTokenValue.slice(
|
|
1488
|
+
INTERCEPTOR_TOKEN_ID_HEX_LENGTH,
|
|
1489
|
+
INTERCEPTOR_TOKEN_ID_HEX_LENGTH + INTERCEPTOR_TOKEN_VALUE_HEX_LENGTH
|
|
1490
|
+
);
|
|
1491
|
+
const tokenFromFile = await readInterceptorTokenFromFile(tokenId, options);
|
|
1492
|
+
if (!tokenFromFile) {
|
|
1493
|
+
throw new InvalidInterceptorTokenValueError_default(tokenValue);
|
|
1494
|
+
}
|
|
1495
|
+
const tokenSecretHash = await hashInterceptorToken(tokenSecret, tokenFromFile.secret.salt);
|
|
1496
|
+
if (tokenSecretHash !== tokenFromFile.secret.hash) {
|
|
1497
|
+
throw new InvalidInterceptorTokenValueError_default(tokenValue);
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
__name(validateInterceptorToken, "validateInterceptorToken");
|
|
1501
|
+
async function removeInterceptorToken(tokenId, options) {
|
|
1502
|
+
if (!isValidInterceptorTokenId(tokenId)) {
|
|
1503
|
+
throw new InvalidInterceptorTokenError_default(tokenId);
|
|
1504
|
+
}
|
|
1505
|
+
const tokenFilePath = path.join(options.tokensDirectory, tokenId);
|
|
1506
|
+
await fs.promises.rm(tokenFilePath, { force: true });
|
|
1507
|
+
}
|
|
1508
|
+
__name(removeInterceptorToken, "removeInterceptorToken");
|
|
1509
|
+
|
|
1104
1510
|
// src/server/utils/fetch.ts
|
|
1105
1511
|
async function getFetchAPI() {
|
|
1512
|
+
const File2 = await importFile();
|
|
1106
1513
|
return {
|
|
1107
1514
|
fetch,
|
|
1108
1515
|
Request,
|
|
@@ -1117,7 +1524,7 @@ async function getFetchAPI() {
|
|
|
1117
1524
|
TextDecoderStream,
|
|
1118
1525
|
TextEncoderStream,
|
|
1119
1526
|
Blob,
|
|
1120
|
-
File:
|
|
1527
|
+
File: File2,
|
|
1121
1528
|
crypto: globalThis.crypto,
|
|
1122
1529
|
btoa,
|
|
1123
1530
|
TextEncoder,
|
|
@@ -1139,6 +1546,7 @@ var InterceptorServer = class {
|
|
|
1139
1546
|
_hostname;
|
|
1140
1547
|
_port;
|
|
1141
1548
|
logUnhandledRequests;
|
|
1549
|
+
tokensDirectory;
|
|
1142
1550
|
httpHandlerGroups = {
|
|
1143
1551
|
GET: [],
|
|
1144
1552
|
POST: [],
|
|
@@ -1153,6 +1561,7 @@ var InterceptorServer = class {
|
|
|
1153
1561
|
this._hostname = options.hostname ?? DEFAULT_HOSTNAME;
|
|
1154
1562
|
this._port = options.port;
|
|
1155
1563
|
this.logUnhandledRequests = options.logUnhandledRequests ?? DEFAULT_LOG_UNHANDLED_REQUESTS;
|
|
1564
|
+
this.tokensDirectory = options.tokensDirectory;
|
|
1156
1565
|
}
|
|
1157
1566
|
get hostname() {
|
|
1158
1567
|
return this._hostname;
|
|
@@ -1197,10 +1606,39 @@ var InterceptorServer = class {
|
|
|
1197
1606
|
});
|
|
1198
1607
|
await this.startHttpServer();
|
|
1199
1608
|
this.webSocketServer = new WebSocketServer_default({
|
|
1200
|
-
httpServer: this.httpServer
|
|
1609
|
+
httpServer: this.httpServer,
|
|
1610
|
+
authenticate: this.authenticateWebSocketConnection
|
|
1201
1611
|
});
|
|
1202
1612
|
this.startWebSocketServer();
|
|
1203
1613
|
}
|
|
1614
|
+
authenticateWebSocketConnection = /* @__PURE__ */ __name(async (_socket, request) => {
|
|
1615
|
+
if (!this.tokensDirectory) {
|
|
1616
|
+
return { isValid: true };
|
|
1617
|
+
}
|
|
1618
|
+
const tokenValue = this.getWebSocketRequestTokenValue(request);
|
|
1619
|
+
if (!tokenValue) {
|
|
1620
|
+
return { isValid: false, message: "An interceptor token is required, but none was provided." };
|
|
1621
|
+
}
|
|
1622
|
+
try {
|
|
1623
|
+
await validateInterceptorToken(tokenValue, { tokensDirectory: this.tokensDirectory });
|
|
1624
|
+
return { isValid: true };
|
|
1625
|
+
} catch (error) {
|
|
1626
|
+
console.error(error);
|
|
1627
|
+
return { isValid: false, message: "The interceptor token is not valid." };
|
|
1628
|
+
}
|
|
1629
|
+
}, "authenticateWebSocketConnection");
|
|
1630
|
+
getWebSocketRequestTokenValue(request) {
|
|
1631
|
+
const protocols = request.headers["sec-websocket-protocol"] ?? "";
|
|
1632
|
+
const parametersAsString = decodeURIComponent(protocols).split(", ");
|
|
1633
|
+
for (const parameterAsString of parametersAsString) {
|
|
1634
|
+
const tokenValueMatch = /^token=(?<tokenValue>.+?)$/.exec(parameterAsString);
|
|
1635
|
+
const tokenValue = tokenValueMatch?.groups?.tokenValue;
|
|
1636
|
+
if (tokenValue) {
|
|
1637
|
+
return tokenValue;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
return void 0;
|
|
1641
|
+
}
|
|
1204
1642
|
async startHttpServer() {
|
|
1205
1643
|
await startHttpServer(this.httpServerOrThrow, {
|
|
1206
1644
|
hostname: this.hostname,
|
|
@@ -1211,8 +1649,8 @@ var InterceptorServer = class {
|
|
|
1211
1649
|
}
|
|
1212
1650
|
startWebSocketServer() {
|
|
1213
1651
|
this.webSocketServerOrThrow.start();
|
|
1214
|
-
this.webSocketServerOrThrow.onEvent("interceptors/workers/
|
|
1215
|
-
this.webSocketServerOrThrow.onEvent("interceptors/workers/
|
|
1652
|
+
this.webSocketServerOrThrow.onEvent("interceptors/workers/commit", this.commitWorker);
|
|
1653
|
+
this.webSocketServerOrThrow.onEvent("interceptors/workers/reset", this.resetWorker);
|
|
1216
1654
|
}
|
|
1217
1655
|
commitWorker = /* @__PURE__ */ __name((message, socket) => {
|
|
1218
1656
|
const commit = message.data;
|
|
@@ -1276,8 +1714,8 @@ var InterceptorServer = class {
|
|
|
1276
1714
|
this.httpServer = void 0;
|
|
1277
1715
|
}
|
|
1278
1716
|
async stopWebSocketServer() {
|
|
1279
|
-
this.webSocketServerOrThrow.offEvent("interceptors/workers/
|
|
1280
|
-
this.webSocketServerOrThrow.offEvent("interceptors/workers/
|
|
1717
|
+
this.webSocketServerOrThrow.offEvent("interceptors/workers/commit", this.commitWorker);
|
|
1718
|
+
this.webSocketServerOrThrow.offEvent("interceptors/workers/reset", this.resetWorker);
|
|
1281
1719
|
await this.webSocketServerOrThrow.stop();
|
|
1282
1720
|
this.webSocketServer = void 0;
|
|
1283
1721
|
}
|
|
@@ -1389,6 +1827,10 @@ __name(createInterceptorServer, "createInterceptorServer");
|
|
|
1389
1827
|
* This is expected not to happen since the servers are not stopped unless they are running. */
|
|
1390
1828
|
/* istanbul ignore if -- @preserve
|
|
1391
1829
|
* The address is expected to be an object because the server does not listen on a pipe or Unix domain socket. */
|
|
1830
|
+
/* istanbul ignore else -- @preserve
|
|
1831
|
+
* An unauthorized close event is the only one we expect to happen here. */
|
|
1832
|
+
/* istanbul ignore else -- @preserve
|
|
1833
|
+
* We currently only support the 'socket:auth:valid' message and it is the only possible control message here. */
|
|
1392
1834
|
/* istanbul ignore if -- @preserve
|
|
1393
1835
|
* This is not expected since the server is not stopped unless it is running. */
|
|
1394
1836
|
/* istanbul ignore next -- @preserve
|
|
@@ -1400,6 +1842,12 @@ __name(createInterceptorServer, "createInterceptorServer");
|
|
|
1400
1842
|
/* istanbul ignore next -- @preserve
|
|
1401
1843
|
* Reply listeners are always present when notified in normal conditions. If they were not present, the request
|
|
1402
1844
|
* would reach a timeout and not be responded. The empty set serves as a fallback. */
|
|
1845
|
+
/* istanbul ignore if -- @preserve
|
|
1846
|
+
* This should never happen, but let's check that the token identifier is valid after generated. */
|
|
1847
|
+
/* istanbul ignore if -- @preserve
|
|
1848
|
+
* This should never happen, but let's check that the token value is valid after generated. */
|
|
1849
|
+
/* istanbul ignore if -- @preserve
|
|
1850
|
+
* At this point, we should have a valid tokenId. This is just a sanity check. */
|
|
1403
1851
|
/* istanbul ignore if -- @preserve
|
|
1404
1852
|
* The HTTP server is initialized before using this method in normal conditions. */
|
|
1405
1853
|
/* istanbul ignore if -- @preserve
|
|
@@ -1410,6 +1858,6 @@ __name(createInterceptorServer, "createInterceptorServer");
|
|
|
1410
1858
|
* This try..catch is for the case when the remote interceptor web socket client is closed before responding.
|
|
1411
1859
|
* Since simulating this scenario is difficult, we are ignoring this branch fow now. */
|
|
1412
1860
|
|
|
1413
|
-
export { DEFAULT_ACCESS_CONTROL_HEADERS, DEFAULT_PREFLIGHT_STATUS_CODE, NotRunningInterceptorServerError_default, RunningInterceptorServerError_default, createCachedDynamicImport_default, createInterceptorServer,
|
|
1414
|
-
//# sourceMappingURL=chunk-
|
|
1415
|
-
//# sourceMappingURL=chunk-
|
|
1861
|
+
export { DEFAULT_ACCESS_CONTROL_HEADERS, DEFAULT_INTERCEPTOR_TOKENS_DIRECTORY, DEFAULT_PREFLIGHT_STATUS_CODE, NotRunningInterceptorServerError_default, RunningInterceptorServerError_default, createCachedDynamicImport_default, createInterceptorServer, createInterceptorToken, listInterceptorTokens, logger, readInterceptorTokenFromFile, removeInterceptorToken };
|
|
1862
|
+
//# sourceMappingURL=chunk-L75WKVZO.mjs.map
|
|
1863
|
+
//# sourceMappingURL=chunk-L75WKVZO.mjs.map
|