test-proxy-recorder 0.1.5 → 0.1.7
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/README.md +334 -207
- package/dist/index.cjs +205 -62
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.mjs +205 -62
- package/dist/proxy.js +205 -62
- package/package.json +8 -3
package/dist/index.cjs
CHANGED
|
@@ -138,14 +138,16 @@ var ProxyServer = class {
|
|
|
138
138
|
this.proxy.on("error", this.handleProxyError.bind(this));
|
|
139
139
|
this.proxy.on("proxyRes", this.handleProxyResponse.bind(this));
|
|
140
140
|
}
|
|
141
|
-
handleProxyError(err,
|
|
141
|
+
handleProxyError(err, req, res) {
|
|
142
142
|
console.error("Proxy error:", err);
|
|
143
143
|
if (!(res instanceof http__default.default.ServerResponse)) {
|
|
144
144
|
return;
|
|
145
145
|
}
|
|
146
146
|
if (!res.headersSent) {
|
|
147
|
+
const corsHeaders = this.getCorsHeaders(req);
|
|
147
148
|
res.writeHead(HTTP_STATUS_BAD_GATEWAY, {
|
|
148
|
-
"Content-Type": "application/json"
|
|
149
|
+
"Content-Type": "application/json",
|
|
150
|
+
...corsHeaders
|
|
149
151
|
});
|
|
150
152
|
}
|
|
151
153
|
res.end(JSON.stringify({ error: "Proxy error", message: err.message }));
|
|
@@ -156,24 +158,51 @@ var ProxyServer = class {
|
|
|
156
158
|
this.recordResponse(req, proxyRes);
|
|
157
159
|
}
|
|
158
160
|
}
|
|
159
|
-
|
|
161
|
+
/**
|
|
162
|
+
* Get CORS headers for a given request
|
|
163
|
+
* @param req The incoming HTTP request
|
|
164
|
+
* @returns An object containing CORS headers
|
|
165
|
+
*/
|
|
166
|
+
getCorsHeaders(req) {
|
|
160
167
|
const origin = req.headers.origin;
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
168
|
+
return {
|
|
169
|
+
"access-control-allow-origin": origin || "*",
|
|
170
|
+
"access-control-allow-credentials": "true",
|
|
171
|
+
"access-control-allow-headers": req.headers["access-control-request-headers"] || "Origin, X-Requested-With, Content-Type, Accept, Authorization",
|
|
172
|
+
"access-control-allow-methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
173
|
+
"access-control-expose-headers": "*"
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
addCorsHeaders(proxyRes, req) {
|
|
177
|
+
const corsHeaders = this.getCorsHeaders(req);
|
|
178
|
+
Object.assign(proxyRes.headers, corsHeaders);
|
|
166
179
|
}
|
|
167
180
|
getTarget() {
|
|
168
181
|
const target = this.targets[this.currentTargetIndex];
|
|
169
182
|
this.currentTargetIndex = (this.currentTargetIndex + 1) % this.targets.length;
|
|
170
183
|
return target;
|
|
171
184
|
}
|
|
185
|
+
parseGetParams(req) {
|
|
186
|
+
const url = new URL(req.url || "", `http://${req.headers.host}`);
|
|
187
|
+
const mode = url.searchParams.get("mode");
|
|
188
|
+
const id = url.searchParams.get("id") || void 0;
|
|
189
|
+
const timeoutParam = url.searchParams.get("timeout");
|
|
190
|
+
const timeout = timeoutParam ? Number.parseInt(timeoutParam, 10) : void 0;
|
|
191
|
+
if (!mode) {
|
|
192
|
+
throw new Error("Mode parameter is required");
|
|
193
|
+
}
|
|
194
|
+
return { mode, id, timeout };
|
|
195
|
+
}
|
|
172
196
|
async handleControlRequest(req, res) {
|
|
173
197
|
try {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
198
|
+
let data;
|
|
199
|
+
if (req.method === "GET") {
|
|
200
|
+
data = this.parseGetParams(req);
|
|
201
|
+
} else {
|
|
202
|
+
const body = await readRequestBody(req);
|
|
203
|
+
console.log("MODE CHANGE (POST)", body);
|
|
204
|
+
data = JSON.parse(body);
|
|
205
|
+
}
|
|
177
206
|
const { mode, id, timeout: requestTimeout } = data;
|
|
178
207
|
const timeout = requestTimeout ?? DEFAULT_TIMEOUT_MS;
|
|
179
208
|
this.clearModeTimeout();
|
|
@@ -201,7 +230,7 @@ var ProxyServer = class {
|
|
|
201
230
|
async switchMode(mode, id) {
|
|
202
231
|
if (this.currentSession) {
|
|
203
232
|
console.log("Switching mode, saving current session first");
|
|
204
|
-
await this.saveCurrentSession();
|
|
233
|
+
await this.saveCurrentSession(true);
|
|
205
234
|
console.log("Session saved, continuing with mode switch");
|
|
206
235
|
}
|
|
207
236
|
switch (mode) {
|
|
@@ -256,33 +285,41 @@ var ProxyServer = class {
|
|
|
256
285
|
if (timeout && timeout > 0) {
|
|
257
286
|
this.modeTimeout = setTimeout(async () => {
|
|
258
287
|
console.log("Timeout reached, switching back to transparent mode");
|
|
259
|
-
await this.saveCurrentSession();
|
|
288
|
+
await this.saveCurrentSession(true);
|
|
260
289
|
this.switchToTransparentMode();
|
|
261
290
|
this.modeTimeout = null;
|
|
262
291
|
}, timeout);
|
|
263
292
|
}
|
|
264
293
|
}
|
|
265
|
-
async saveCurrentSession() {
|
|
294
|
+
async saveCurrentSession(filterIncomplete = false) {
|
|
266
295
|
if (!this.currentSession) {
|
|
267
296
|
console.log("No current session to save");
|
|
268
297
|
return;
|
|
269
298
|
}
|
|
270
|
-
if (
|
|
271
|
-
|
|
272
|
-
|
|
299
|
+
if (filterIncomplete) {
|
|
300
|
+
const incompleteCount = this.currentSession.recordings.filter(
|
|
301
|
+
(r) => !r.response
|
|
302
|
+
).length;
|
|
303
|
+
if (incompleteCount > 0) {
|
|
304
|
+
console.log(
|
|
305
|
+
`Removing ${incompleteCount} incomplete recording(s) without responses`
|
|
306
|
+
);
|
|
307
|
+
this.currentSession.recordings = this.currentSession.recordings.filter(
|
|
308
|
+
(r) => r.response
|
|
309
|
+
);
|
|
310
|
+
}
|
|
273
311
|
}
|
|
274
312
|
console.log(
|
|
275
313
|
`Saving session with ${this.currentSession.recordings.length} HTTP and ${this.currentSession.websocketRecordings.length} WebSocket recordings`
|
|
276
314
|
);
|
|
277
315
|
await saveRecordingSession(this.recordingsDir, this.currentSession);
|
|
278
316
|
}
|
|
279
|
-
|
|
317
|
+
saveRequestRecordSync(req, body) {
|
|
280
318
|
if (!this.currentSession) {
|
|
319
|
+
console.log("saveRequestRecordSync: No current session");
|
|
281
320
|
return;
|
|
282
321
|
}
|
|
283
322
|
const key = getReqID(req);
|
|
284
|
-
const currentSequence = this.requestSequenceMap.get(key) || 0;
|
|
285
|
-
this.requestSequenceMap.set(key, currentSequence + 1);
|
|
286
323
|
const record = {
|
|
287
324
|
request: {
|
|
288
325
|
method: req.method,
|
|
@@ -292,9 +329,34 @@ var ProxyServer = class {
|
|
|
292
329
|
},
|
|
293
330
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
294
331
|
key,
|
|
295
|
-
sequence:
|
|
332
|
+
sequence: -1
|
|
333
|
+
// Temporary, will be set when response arrives
|
|
296
334
|
};
|
|
297
335
|
this.currentSession.recordings.push(record);
|
|
336
|
+
console.log(
|
|
337
|
+
// eslint-disable-next-line sonarjs/no-nested-template-literals
|
|
338
|
+
`saveRequestRecordSync: Saved ${req.method} ${req.url} (key: ${key}, body: ${body ? `${body.length} chars` : "null"}, total: ${this.currentSession.recordings.length}, sessionId: ${this.currentSession.id})`
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
updateRequestBodySync(req, body) {
|
|
342
|
+
if (!this.currentSession) {
|
|
343
|
+
console.log("updateRequestBodySync: No current session");
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
const key = getReqID(req);
|
|
347
|
+
const record = this.currentSession.recordings.findLast(
|
|
348
|
+
(r) => r.key === key && !r.response
|
|
349
|
+
);
|
|
350
|
+
if (!record) {
|
|
351
|
+
console.error(
|
|
352
|
+
`updateRequestBodySync: Could not find request record for ${req.method} ${req.url}`
|
|
353
|
+
);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
record.request.body = body || null;
|
|
357
|
+
console.log(
|
|
358
|
+
`updateRequestBodySync: Updated body for ${req.method} ${req.url} (${body.length} chars)`
|
|
359
|
+
);
|
|
298
360
|
}
|
|
299
361
|
async recordResponse(req, proxyRes) {
|
|
300
362
|
if (!this.currentSession) {
|
|
@@ -319,59 +381,115 @@ var ProxyServer = class {
|
|
|
319
381
|
headers: proxyRes.headers,
|
|
320
382
|
body: body || null
|
|
321
383
|
};
|
|
322
|
-
await this.saveCurrentSession();
|
|
323
384
|
console.log(`Recorded: ${req.method} ${req.url}`);
|
|
324
385
|
});
|
|
325
386
|
}
|
|
387
|
+
async recordResponseData(req, proxyRes, body) {
|
|
388
|
+
if (!this.currentSession) {
|
|
389
|
+
console.log("recordResponseData: No current session");
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
const key = getReqID(req);
|
|
393
|
+
const record = this.currentSession.recordings.findLast(
|
|
394
|
+
(r) => r.key === key && !r.response
|
|
395
|
+
);
|
|
396
|
+
if (!record) {
|
|
397
|
+
const host = req.headers.host || "unknown";
|
|
398
|
+
const recordsWithKey = this.currentSession.recordings.filter(
|
|
399
|
+
(r) => r.key === key
|
|
400
|
+
);
|
|
401
|
+
console.error(
|
|
402
|
+
`Request record not found for response: ${key} at ${req.method} ${host}${req.url}`
|
|
403
|
+
);
|
|
404
|
+
console.error(
|
|
405
|
+
` Total recordings: ${this.currentSession.recordings.length}, with this key: ${recordsWithKey.length}`
|
|
406
|
+
);
|
|
407
|
+
console.error(
|
|
408
|
+
` Records with key:`,
|
|
409
|
+
recordsWithKey.map((r) => ({
|
|
410
|
+
seq: r.sequence,
|
|
411
|
+
hasResponse: !!r.response
|
|
412
|
+
}))
|
|
413
|
+
);
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
record.response = {
|
|
417
|
+
statusCode: proxyRes.statusCode,
|
|
418
|
+
headers: proxyRes.headers,
|
|
419
|
+
body: body || null
|
|
420
|
+
};
|
|
421
|
+
record.timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
422
|
+
const currentSequence = this.requestSequenceMap.get(key) || 0;
|
|
423
|
+
record.sequence = currentSequence;
|
|
424
|
+
this.requestSequenceMap.set(key, currentSequence + 1);
|
|
425
|
+
console.log(
|
|
426
|
+
`recordResponseData: Recorded response for ${req.method} ${req.url} (seq: ${record.sequence})`
|
|
427
|
+
);
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
326
430
|
async handleReplayRequest(req, res) {
|
|
327
431
|
const key = getReqID(req);
|
|
328
432
|
const filePath = getRecordingPath(this.recordingsDir, this.replayId);
|
|
329
433
|
try {
|
|
330
434
|
const session = await loadRecordingSession(filePath);
|
|
331
|
-
const
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
);
|
|
335
|
-
if (!record) {
|
|
435
|
+
const host = req.headers.host || "unknown";
|
|
436
|
+
const recordsWithKey = session.recordings.filter((r) => r.key === key && r.response).toSorted((a, b) => a.sequence - b.sequence);
|
|
437
|
+
if (recordsWithKey.length === 0) {
|
|
336
438
|
throw new Error(
|
|
337
|
-
`No recording found for ${key}
|
|
439
|
+
`No recording found for ${key} at ${req.method} ${host}${req.url}`
|
|
338
440
|
);
|
|
339
441
|
}
|
|
442
|
+
const usageCount = this.replaySequenceMap.get(key) || 0;
|
|
443
|
+
let record;
|
|
444
|
+
if (recordsWithKey.length > 1) {
|
|
445
|
+
record = recordsWithKey[recordsWithKey.length - 1];
|
|
446
|
+
} else {
|
|
447
|
+
const recordIndex = usageCount % recordsWithKey.length;
|
|
448
|
+
record = recordsWithKey[recordIndex];
|
|
449
|
+
}
|
|
450
|
+
console.log(
|
|
451
|
+
`Replaying ${req.method} ${req.url} (usage: ${usageCount}, sequence: ${record.sequence}, body_len: ${record.response?.body?.length || 0})`
|
|
452
|
+
);
|
|
453
|
+
this.replaySequenceMap.set(key, usageCount + 1);
|
|
340
454
|
if (!record.response) {
|
|
341
|
-
throw new Error(
|
|
455
|
+
throw new Error(
|
|
456
|
+
`No response recorded for this request: ${req.method} ${host}${req.url}`
|
|
457
|
+
);
|
|
342
458
|
}
|
|
343
|
-
this.replaySequenceMap.set(key, currentSequence + 1);
|
|
344
459
|
const { statusCode, headers, body } = record.response;
|
|
345
|
-
const origin = req.headers.origin;
|
|
346
460
|
const responseHeaders = {
|
|
347
461
|
...headers,
|
|
348
|
-
|
|
349
|
-
"access-control-allow-credentials": "true"
|
|
462
|
+
...this.getCorsHeaders(req)
|
|
350
463
|
};
|
|
351
464
|
res.writeHead(statusCode, responseHeaders);
|
|
352
465
|
res.end(body);
|
|
353
|
-
console.log(
|
|
354
|
-
`Replayed: ${req.method} ${req.url} (sequence: ${currentSequence})`
|
|
355
|
-
);
|
|
356
466
|
} catch (error) {
|
|
357
|
-
this.handleReplayError(res, error, key, filePath);
|
|
467
|
+
this.handleReplayError(req, res, error, key, filePath);
|
|
358
468
|
}
|
|
359
469
|
}
|
|
360
|
-
handleReplayError(res, err, key, filePath) {
|
|
470
|
+
handleReplayError(req, res, err, key, filePath) {
|
|
361
471
|
const isFileNotFound = err instanceof Error && "code" in err && err.code === "ENOENT";
|
|
362
472
|
console.error("Replay error:", err);
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
filePath
|
|
473
|
+
const corsHeaders = this.getCorsHeaders(req);
|
|
474
|
+
res.writeHead(HTTP_STATUS_NOT_FOUND, {
|
|
475
|
+
"Content-Type": "application/json",
|
|
476
|
+
...corsHeaders
|
|
368
477
|
});
|
|
478
|
+
res.end(
|
|
479
|
+
JSON.stringify({
|
|
480
|
+
error: isFileNotFound ? "Recording file not found" : "Recording not found",
|
|
481
|
+
message: err instanceof Error ? err.message : "Unknown error",
|
|
482
|
+
key,
|
|
483
|
+
filePath
|
|
484
|
+
})
|
|
485
|
+
);
|
|
369
486
|
}
|
|
370
487
|
async handleRequest(req, res) {
|
|
371
488
|
if (req.method === "OPTIONS") {
|
|
372
489
|
return this.handleCorsPreflightRequest(req, res);
|
|
373
490
|
}
|
|
374
|
-
|
|
491
|
+
const urlPath = req.url?.split("?")[0] || "";
|
|
492
|
+
if (urlPath === CONTROL_ENDPOINT) {
|
|
375
493
|
return this.handleControlRequest(req, res);
|
|
376
494
|
}
|
|
377
495
|
if (this.mode === Modes.replay) {
|
|
@@ -380,12 +498,9 @@ var ProxyServer = class {
|
|
|
380
498
|
await this.handleProxyRequest(req, res);
|
|
381
499
|
}
|
|
382
500
|
handleCorsPreflightRequest(req, res) {
|
|
383
|
-
const
|
|
501
|
+
const corsHeaders = this.getCorsHeaders(req);
|
|
384
502
|
res.writeHead(HTTP_STATUS_OK, {
|
|
385
|
-
|
|
386
|
-
"Access-Control-Allow-Credentials": "true",
|
|
387
|
-
"Access-Control-Allow-Headers": req.headers["access-control-request-headers"] || "Origin, X-Requested-With, Content-Type, Accept, Authorization",
|
|
388
|
-
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
503
|
+
...corsHeaders,
|
|
389
504
|
"Access-Control-Max-Age": "86400"
|
|
390
505
|
// 24 hours
|
|
391
506
|
});
|
|
@@ -395,6 +510,7 @@ var ProxyServer = class {
|
|
|
395
510
|
const target = this.getTarget();
|
|
396
511
|
console.log(`[${this.mode}] ${req.method} ${req.url} -> ${target}`);
|
|
397
512
|
if (this.mode === Modes.record) {
|
|
513
|
+
this.saveRequestRecordSync(req, null);
|
|
398
514
|
await this.bufferAndProxyRequest(req, res, target);
|
|
399
515
|
} else {
|
|
400
516
|
this.proxy.web(req, res, { target });
|
|
@@ -405,11 +521,20 @@ var ProxyServer = class {
|
|
|
405
521
|
req.on("data", (chunk) => {
|
|
406
522
|
chunks.push(chunk);
|
|
407
523
|
});
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
524
|
+
try {
|
|
525
|
+
await new Promise((resolve, reject) => {
|
|
526
|
+
req.on("end", () => resolve());
|
|
527
|
+
req.on("error", (err) => reject(err));
|
|
528
|
+
setTimeout(
|
|
529
|
+
() => reject(new Error("Request buffering timeout")),
|
|
530
|
+
3e4
|
|
531
|
+
);
|
|
532
|
+
});
|
|
533
|
+
} catch (error) {
|
|
534
|
+
console.error("Error buffering request:", error);
|
|
535
|
+
}
|
|
411
536
|
const body = Buffer.concat(chunks).toString("utf8");
|
|
412
|
-
|
|
537
|
+
this.updateRequestBodySync(req, body);
|
|
413
538
|
const targetUrl = new URL(target);
|
|
414
539
|
const isHttps = targetUrl.protocol === "https:";
|
|
415
540
|
const requestModule = isHttps ? https__default.default : http__default.default;
|
|
@@ -424,9 +549,33 @@ var ProxyServer = class {
|
|
|
424
549
|
},
|
|
425
550
|
(proxyRes) => {
|
|
426
551
|
this.addCorsHeaders(proxyRes, req);
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
552
|
+
const responseChunks = [];
|
|
553
|
+
proxyRes.on("data", (chunk) => {
|
|
554
|
+
responseChunks.push(chunk);
|
|
555
|
+
});
|
|
556
|
+
proxyRes.on("end", async () => {
|
|
557
|
+
const responseBody = Buffer.concat(responseChunks);
|
|
558
|
+
const recorded = await this.recordResponseData(
|
|
559
|
+
req,
|
|
560
|
+
proxyRes,
|
|
561
|
+
responseBody.toString("utf8")
|
|
562
|
+
);
|
|
563
|
+
const responseHeaders = {
|
|
564
|
+
...proxyRes.headers,
|
|
565
|
+
...this.getCorsHeaders(req)
|
|
566
|
+
};
|
|
567
|
+
res.writeHead(proxyRes.statusCode || 200, responseHeaders);
|
|
568
|
+
res.end(responseBody);
|
|
569
|
+
if (recorded) {
|
|
570
|
+
console.log(`Recorded: ${req.method} ${req.url}`);
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
proxyRes.on("error", (err) => {
|
|
574
|
+
console.error("Proxy response error:", err);
|
|
575
|
+
if (!res.headersSent) {
|
|
576
|
+
this.handleProxyError(err, req, res);
|
|
577
|
+
}
|
|
578
|
+
});
|
|
430
579
|
}
|
|
431
580
|
);
|
|
432
581
|
proxyReq.on("error", (err) => {
|
|
@@ -478,9 +627,6 @@ var ProxyServer = class {
|
|
|
478
627
|
if (backendWs.readyState === ws.WebSocket.OPEN) {
|
|
479
628
|
backendWs.send(message);
|
|
480
629
|
}
|
|
481
|
-
this.saveCurrentSession().catch((error) => {
|
|
482
|
-
console.error("Failed to save WebSocket recording:", error);
|
|
483
|
-
});
|
|
484
630
|
});
|
|
485
631
|
backendWs.on("message", (data) => {
|
|
486
632
|
const message = data.toString();
|
|
@@ -492,9 +638,6 @@ var ProxyServer = class {
|
|
|
492
638
|
if (clientWs.readyState === ws.WebSocket.OPEN) {
|
|
493
639
|
clientWs.send(message);
|
|
494
640
|
}
|
|
495
|
-
this.saveCurrentSession().catch((error) => {
|
|
496
|
-
console.error("Failed to save WebSocket recording:", error);
|
|
497
|
-
});
|
|
498
641
|
});
|
|
499
642
|
clientWs.on("error", (err) => {
|
|
500
643
|
console.error("Client WebSocket error:", err);
|
package/dist/index.d.cts
CHANGED
|
@@ -20,8 +20,15 @@ declare class ProxyServer {
|
|
|
20
20
|
private setupProxyEventHandlers;
|
|
21
21
|
private handleProxyError;
|
|
22
22
|
private handleProxyResponse;
|
|
23
|
+
/**
|
|
24
|
+
* Get CORS headers for a given request
|
|
25
|
+
* @param req The incoming HTTP request
|
|
26
|
+
* @returns An object containing CORS headers
|
|
27
|
+
*/
|
|
28
|
+
private getCorsHeaders;
|
|
23
29
|
private addCorsHeaders;
|
|
24
30
|
private getTarget;
|
|
31
|
+
private parseGetParams;
|
|
25
32
|
private handleControlRequest;
|
|
26
33
|
private clearModeTimeout;
|
|
27
34
|
private switchMode;
|
|
@@ -30,8 +37,10 @@ declare class ProxyServer {
|
|
|
30
37
|
private switchToReplayMode;
|
|
31
38
|
private setupModeTimeout;
|
|
32
39
|
private saveCurrentSession;
|
|
33
|
-
private
|
|
40
|
+
private saveRequestRecordSync;
|
|
41
|
+
private updateRequestBodySync;
|
|
34
42
|
private recordResponse;
|
|
43
|
+
private recordResponseData;
|
|
35
44
|
private handleReplayRequest;
|
|
36
45
|
private handleReplayError;
|
|
37
46
|
private handleRequest;
|
package/dist/index.d.ts
CHANGED
|
@@ -20,8 +20,15 @@ declare class ProxyServer {
|
|
|
20
20
|
private setupProxyEventHandlers;
|
|
21
21
|
private handleProxyError;
|
|
22
22
|
private handleProxyResponse;
|
|
23
|
+
/**
|
|
24
|
+
* Get CORS headers for a given request
|
|
25
|
+
* @param req The incoming HTTP request
|
|
26
|
+
* @returns An object containing CORS headers
|
|
27
|
+
*/
|
|
28
|
+
private getCorsHeaders;
|
|
23
29
|
private addCorsHeaders;
|
|
24
30
|
private getTarget;
|
|
31
|
+
private parseGetParams;
|
|
25
32
|
private handleControlRequest;
|
|
26
33
|
private clearModeTimeout;
|
|
27
34
|
private switchMode;
|
|
@@ -30,8 +37,10 @@ declare class ProxyServer {
|
|
|
30
37
|
private switchToReplayMode;
|
|
31
38
|
private setupModeTimeout;
|
|
32
39
|
private saveCurrentSession;
|
|
33
|
-
private
|
|
40
|
+
private saveRequestRecordSync;
|
|
41
|
+
private updateRequestBodySync;
|
|
34
42
|
private recordResponse;
|
|
43
|
+
private recordResponseData;
|
|
35
44
|
private handleReplayRequest;
|
|
36
45
|
private handleReplayError;
|
|
37
46
|
private handleRequest;
|