http-log-replay 1.0.0
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 +197 -0
- package/ecosystem.config.js +19 -0
- package/index.js +95 -0
- package/package.json +55 -0
- package/recorder.js +114 -0
- package/replayer.js +232 -0
- package/server.js +129 -0
- package/traffic-generator.js +149 -0
- package/ui/dist/ui/3rdpartylicenses.txt +487 -0
- package/ui/dist/ui/browser/favicon.ico +0 -0
- package/ui/dist/ui/browser/index.html +13 -0
- package/ui/dist/ui/browser/main-4NFTVOCL.js +5 -0
- package/ui/dist/ui/browser/styles-5INURTSO.css +0 -0
- package/ui/dist/ui/prerendered-routes.json +3 -0
package/README.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# π¦ Traffic Mirror (http-log-replay)
|
|
2
|
+
|
|
3
|
+
> **Regressions No More.** Record production traffic and replay it against your changes to verify correctness with zero effort.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/http-log-replay)
|
|
6
|
+
[](https://opensource.org/licenses/ISC)
|
|
7
|
+
|
|
8
|
+
**Traffic Mirror** is a powerful regression testing tool designed for modern engineering teams. It allows you to **record** HTTP traffic from a live environment (like Production) and **replay** it against two different target environments (e.g., Stable vs. Canary) to instantly detect regressions, bugs, or side-effects.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## π Table of Contents
|
|
13
|
+
|
|
14
|
+
- [π Features](#-features)
|
|
15
|
+
- [π Quick Start (Zero Setup)](#-quick-start-zero-setup)
|
|
16
|
+
- [π» Usage Guide: Web UI](#-usage-guide-web-ui)
|
|
17
|
+
- [π₯οΈ Usage Guide: CLI](#οΈ-usage-guide-cli)
|
|
18
|
+
- [π³ Usage Guide: Docker](#-usage-guide-docker)
|
|
19
|
+
- [π οΈ Development & Contribution](#οΈ-development--contribution)
|
|
20
|
+
- [π¦ Maintenance](#-maintenance)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## π Features
|
|
25
|
+
|
|
26
|
+
- **π‘οΈ Zero-Config Recording**: Acts as a transparent HTTP proxy to capture real requests.
|
|
27
|
+
- **β‘ High-Performance Replay**: Replay thousands of requests in parallel with customizable concurrency.
|
|
28
|
+
- **π Intelligent Diffing**: compares JSON responses and ignores dynamic fields (like timestamps, UUIDs) that cause false positives.
|
|
29
|
+
- **π Rich Reporting**: Generates detailed HTML reports showing exactly what broke.
|
|
30
|
+
- **π Swagger Integration**: Auto-generate test traffic from your OpenAPI definitions if you don't have live traffic.
|
|
31
|
+
- **ποΈ Two Modes**: Full interactive **Web UI** for debugging and a lightweight **CLI** for CI/CD pipelines.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## π Quick Start (Zero Setup)
|
|
36
|
+
|
|
37
|
+
You don't need to install anything if you have Node.js (v18+) installed. Just run:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx http-log-replay ui --port 4200
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This will launch the **Traffic Mirror Dashboard** at [http://localhost:4200](http://localhost:4200).
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## π» Usage Guide: Web UI
|
|
48
|
+
|
|
49
|
+
The Web UI is the best way to get started. It guides you through the entire workflow in three simple tabs.
|
|
50
|
+
|
|
51
|
+
### 1. π΄ Record / Generate
|
|
52
|
+
- **Manual Mode**: Start the proxy, point your application/client to it, and use your app normally. Requests are saved to a file.
|
|
53
|
+
- **Auto-Generate**: Upload an OpenAPI/Swagger JSON file to automatically generate realistic traffic patterns.
|
|
54
|
+
|
|
55
|
+
### 2. βΆοΈ Replay
|
|
56
|
+
- Configure your **Primary** (Stable) and **Secondary** (Test) environments.
|
|
57
|
+
- Set **Concurrency** to speed up large suites.
|
|
58
|
+
- Add **Ignore Fields** (e.g., `createdAt`, `traceId`) to filter out noise in the comparison.
|
|
59
|
+
- Click **Replay & Compare**.
|
|
60
|
+
|
|
61
|
+
### 3. π Report
|
|
62
|
+
- Instantly view the results.
|
|
63
|
+
- **Green**: Exact match.
|
|
64
|
+
- **Red**: Mismatch (click to expand the JSON diff).
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## π₯οΈ Usage Guide: CLI
|
|
69
|
+
|
|
70
|
+
Perfect for **CI/CD pipelines** or headless environments.
|
|
71
|
+
|
|
72
|
+
### 1. Record Traffic
|
|
73
|
+
Start a recording proxy on port `3000` forwarding to your real API at `localhost:8080`.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npx http-log-replay record --target http://localhost:8080 --port 3000 --out traffic.jsonl
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 2. Auto-Generate from Swagger
|
|
80
|
+
Generate traffic without manual clicking.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npx http-log-replay generate --file ./openapi.json --target http://localhost:3000
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 3. Replay & Verify
|
|
87
|
+
Replay recorded traffic against two environments and generate an HTML report.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npx http-log-replay replay \
|
|
91
|
+
--log traffic.jsonl \
|
|
92
|
+
--primary http://prod-api.com \
|
|
93
|
+
--secondary http://staging-api.com \
|
|
94
|
+
--report report.html \
|
|
95
|
+
--ignore "timestamp,id"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## π³ Usage Guide: Docker
|
|
101
|
+
|
|
102
|
+
We provide optimized Docker images for both the UI and CLI.
|
|
103
|
+
|
|
104
|
+
### Prerequisites
|
|
105
|
+
- Docker & Docker Compose installed.
|
|
106
|
+
|
|
107
|
+
### Option A: Complete Environment (Recommended)
|
|
108
|
+
Use the included `docker-compose.yml` to run everything.
|
|
109
|
+
|
|
110
|
+
**Start the GUI:**
|
|
111
|
+
```bash
|
|
112
|
+
docker-compose up gui
|
|
113
|
+
```
|
|
114
|
+
> Access at [http://localhost:4200](http://localhost:4200). Data is persisted to your host folder.
|
|
115
|
+
|
|
116
|
+
**Run CLI Commands:**
|
|
117
|
+
```bash
|
|
118
|
+
docker-compose run cli record --target http://host.docker.internal:8080 ...
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Option B: Manual Docker Run
|
|
122
|
+
|
|
123
|
+
**UI Image:**
|
|
124
|
+
```bash
|
|
125
|
+
docker run -p 4200:4200 -v $(pwd):/app traffic-mirror-gui
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**CLI Image:**
|
|
129
|
+
```bash
|
|
130
|
+
docker run -v $(pwd):/app traffic-mirror-cli --help
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## π οΈ Development & Contribution
|
|
136
|
+
|
|
137
|
+
We welcome contributions! Here is how to run the project locally for development.
|
|
138
|
+
|
|
139
|
+
### 1. Setup
|
|
140
|
+
```bash
|
|
141
|
+
git clone https://github.com/your-username/http-log-replay.git
|
|
142
|
+
cd http-log-replay
|
|
143
|
+
npm install
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 2. Build the UI
|
|
147
|
+
The UI is built with Angular. You must build it before running the app.
|
|
148
|
+
```bash
|
|
149
|
+
cd ui
|
|
150
|
+
npm install
|
|
151
|
+
npm run build
|
|
152
|
+
cd ..
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### 3. Run Locally (Dev Mode)
|
|
156
|
+
```bash
|
|
157
|
+
# Start the full app (Frontend + Backend)
|
|
158
|
+
node index.js ui --port 4200
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### 4. Testing & Code Quality
|
|
162
|
+
We use **Jest** for testing and **ESLint** for code quality.
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# Run Unit Tests
|
|
166
|
+
npm test
|
|
167
|
+
|
|
168
|
+
# Lint Code
|
|
169
|
+
npm run lint
|
|
170
|
+
|
|
171
|
+
# Format Code
|
|
172
|
+
npm run format
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## π¦ Maintenance
|
|
178
|
+
|
|
179
|
+
### Publishing to NPM
|
|
180
|
+
1. **Bump Version**: Update `version` in `package.json`.
|
|
181
|
+
2. **Build UI**: The `prepublishOnly` script will automatically build the Angular UI.
|
|
182
|
+
3. **Publish**:
|
|
183
|
+
```bash
|
|
184
|
+
npm publish
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### File Structure
|
|
188
|
+
- `index.js`: CLI entry point.
|
|
189
|
+
- `recorder.js`: Proxy logic.
|
|
190
|
+
- `replayer.js`: Replay & Diff logic.
|
|
191
|
+
- `server.js`: Express server for the UI.
|
|
192
|
+
- `ui/`: Angular frontend source code.
|
|
193
|
+
- `tests/`: Jest unit tests.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
_Built with β€οΈ by the Traffic Mirror Team._
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
apps: [
|
|
3
|
+
{
|
|
4
|
+
name: 'traffic-mirror-ui',
|
|
5
|
+
script: './index.js',
|
|
6
|
+
args: 'ui --port 4200',
|
|
7
|
+
instances: 4, // Keep 1 instance to avoid port conflicts with the recorder
|
|
8
|
+
autorestart: true,
|
|
9
|
+
watch: false,
|
|
10
|
+
max_memory_restart: '1G',
|
|
11
|
+
env: {
|
|
12
|
+
NODE_ENV: 'development',
|
|
13
|
+
},
|
|
14
|
+
env_production: {
|
|
15
|
+
NODE_ENV: 'production',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
};
|
package/index.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* eslint-disable no-console */
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const recorder = require('./recorder');
|
|
5
|
+
const replayAndDiff = require('./replayer');
|
|
6
|
+
const startServer = require('./server');
|
|
7
|
+
const generator = require('./traffic-generator');
|
|
8
|
+
|
|
9
|
+
const program = new Command();
|
|
10
|
+
|
|
11
|
+
program
|
|
12
|
+
.name('traffic-mirror')
|
|
13
|
+
.description('Record and Replay HTTP traffic to detect regressions')
|
|
14
|
+
.version('1.0.0');
|
|
15
|
+
|
|
16
|
+
// Command: Record
|
|
17
|
+
program
|
|
18
|
+
.command('record')
|
|
19
|
+
.description('Start a proxy to record traffic to a JSONL file')
|
|
20
|
+
.requiredOption('-t, --target <url>', 'The target URL to proxy to (e.g., http://localhost:8080)')
|
|
21
|
+
.option('-p, --port <number>', 'Port to listen on', '3000')
|
|
22
|
+
.option('-o, --out <file>', 'Output file for logs', 'traffic.jsonl')
|
|
23
|
+
.action((options) => {
|
|
24
|
+
recorder.start(options.target, options.port, options.out);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Command: Replay
|
|
28
|
+
program
|
|
29
|
+
.command('replay')
|
|
30
|
+
.description('Replay logs against two environments and diff the results')
|
|
31
|
+
.requiredOption('-l, --log <file>', 'The log file to replay')
|
|
32
|
+
.requiredOption('-a, --primary <url>', 'Primary environment URL (e.g., Stable)')
|
|
33
|
+
.requiredOption('-b, --secondary <url>', 'Secondary environment URL (e.g., Staging)')
|
|
34
|
+
.option('-r, --report <file>', 'Path to save HTML report', 'report.html')
|
|
35
|
+
.option('-i, --ignore <items>', 'Comma separated list of JSON fields to ignore', (val) =>
|
|
36
|
+
val.split(',')
|
|
37
|
+
)
|
|
38
|
+
.option('-x, --exclude-endpoints <items>', 'List of URL paths to skip', (val) => val.split(',')) // <--- NEW FLAG
|
|
39
|
+
.option('-c, --concurrency <number>', 'Number of concurrent requests', (val) => parseInt(val, 10), 5) // Default 5
|
|
40
|
+
.option('--auth <token>', 'Inject Authorization header (e.g. "Bearer eyJhb...")')
|
|
41
|
+
.action((options) => {
|
|
42
|
+
const injectedHeaders = options.auth ? { Authorization: options.auth } : {};
|
|
43
|
+
// Pass injectedHeaders to the replayer
|
|
44
|
+
replayAndDiff(
|
|
45
|
+
options.log,
|
|
46
|
+
options.primary,
|
|
47
|
+
options.secondary,
|
|
48
|
+
options.report,
|
|
49
|
+
options.ignore || [],
|
|
50
|
+
injectedHeaders,
|
|
51
|
+
options.excludeEndpoints || [],
|
|
52
|
+
() => { }, // Empty callback for CLI
|
|
53
|
+
options.concurrency || 1
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
program
|
|
58
|
+
.command('ui')
|
|
59
|
+
.description('Start the Web Interface')
|
|
60
|
+
.option('-p, --port <number>', 'Port for the UI', '4200')
|
|
61
|
+
.action((options) => {
|
|
62
|
+
startServer(options.port);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
program
|
|
66
|
+
.command('generate')
|
|
67
|
+
.description('Auto-generate traffic from Swagger file')
|
|
68
|
+
.option('-t, --target <url>', 'Proxy URL', 'http://localhost:3000')
|
|
69
|
+
.option('-f, --file <path>', 'Swagger file path', './full_documentation.json')
|
|
70
|
+
.option('-x, --exclude <items>', 'Comma separated list of endpoints to exclude', (val) =>
|
|
71
|
+
val.split(',')
|
|
72
|
+
) // <--- NEW OPTION
|
|
73
|
+
.option('-s, --source <url>', 'Source Server URL', 'http://localhost:1338')
|
|
74
|
+
.action(async (options) => {
|
|
75
|
+
try {
|
|
76
|
+
console.log('π Starting Traffic Generation...');
|
|
77
|
+
|
|
78
|
+
// Pass options.exclude (or empty array) as 3rd arg
|
|
79
|
+
await generator.run(
|
|
80
|
+
options.target,
|
|
81
|
+
options.file,
|
|
82
|
+
options.exclude || [],
|
|
83
|
+
(log) => {
|
|
84
|
+
console.log(log.message);
|
|
85
|
+
},
|
|
86
|
+
options.source
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
console.log('β
Done.');
|
|
90
|
+
} catch (e) {
|
|
91
|
+
console.error('β Error:', e.message);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "http-log-replay",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Traffic Mirror is a regression testing tool that records HTTP traffic from a live environment and replays it against two different environments (e.g., Stable vs. Canary) to detect differences in responses.",
|
|
5
|
+
"author": "Xhani Manolis Trungu",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"traffic-mirror": "./index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"*.js",
|
|
12
|
+
"ui/dist"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "jest tests/",
|
|
16
|
+
"lint": "eslint .",
|
|
17
|
+
"format": "prettier --write .",
|
|
18
|
+
"start:pm2": "pm2 start ecosystem.config.js",
|
|
19
|
+
"prepublishOnly": "cd ui && npm install && npm run build"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"http-log",
|
|
23
|
+
"http-log-replay",
|
|
24
|
+
"traffic-replay",
|
|
25
|
+
"regression-testing",
|
|
26
|
+
"api-regression",
|
|
27
|
+
"api-testing"
|
|
28
|
+
],
|
|
29
|
+
"license": "ISC",
|
|
30
|
+
"type": "commonjs",
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"axios": "^1.13.2",
|
|
33
|
+
"colors": "^1.4.0",
|
|
34
|
+
"commander": "^14.0.2",
|
|
35
|
+
"cors": "^2.8.5",
|
|
36
|
+
"deep-diff": "^1.0.2",
|
|
37
|
+
"diff": "^8.0.2",
|
|
38
|
+
"express": "^5.2.1",
|
|
39
|
+
"http-proxy": "^1.18.1",
|
|
40
|
+
"open": "^11.0.0",
|
|
41
|
+
"readline": "^1.3.0",
|
|
42
|
+
"socket.io": "^4.8.1"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"eslint": "^9.39.2",
|
|
46
|
+
"eslint-config-prettier": "^10.1.8",
|
|
47
|
+
"jest": "^30.2.0",
|
|
48
|
+
"pm2": "^6.0.14",
|
|
49
|
+
"prettier": "^3.7.4"
|
|
50
|
+
},
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "https://github.com/xhani-manolis-trungu/traffic-mirror"
|
|
54
|
+
}
|
|
55
|
+
}
|
package/recorder.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
const http = require('http');
|
|
3
|
+
const httpProxy = require('http-proxy');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
|
|
6
|
+
class Recorder {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.server = null;
|
|
9
|
+
this.proxy = httpProxy.createProxyServer({});
|
|
10
|
+
|
|
11
|
+
// 1. Handle Proxy Errors (Critical for preventing crashes)
|
|
12
|
+
this.proxy.on('error', (err, req, res) => {
|
|
13
|
+
console.error(`β Proxy Error [${req.url}]:`, err.message);
|
|
14
|
+
if (!res.headersSent) {
|
|
15
|
+
res.writeHead(502, { 'Content-Type': 'application/json' });
|
|
16
|
+
}
|
|
17
|
+
res.end(JSON.stringify({ error: 'Proxy Request Failed', details: err.message }));
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
start(target, port, outputFile, onLog = () => { }) {
|
|
22
|
+
if (this.server) throw new Error('Recorder already running');
|
|
23
|
+
|
|
24
|
+
console.log(`π Recording to file: ${outputFile}`);
|
|
25
|
+
const stream = fs.createWriteStream(outputFile, { flags: 'a' });
|
|
26
|
+
|
|
27
|
+
// Handle file write errors
|
|
28
|
+
stream.on('error', (err) => {
|
|
29
|
+
console.error('β File Write Error:', err.message);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
this.server = http.createServer((req, res) => {
|
|
33
|
+
// 2. Capture Request Body safely
|
|
34
|
+
// We use a separate array but we DO NOT attach 'data' listeners directly
|
|
35
|
+
// unless we plan to buffer it for the proxy.
|
|
36
|
+
// For simplicity, let's rely on the response capture primarily.
|
|
37
|
+
|
|
38
|
+
const reqChunks = [];
|
|
39
|
+
// We tap into the stream without consuming it destructively if possible,
|
|
40
|
+
// but the safest way with http-proxy is often just to listen to the proxy events.
|
|
41
|
+
// HOWEVER, for your specific "Request Stealing" fix:
|
|
42
|
+
req.on('data', (chunk) => reqChunks.push(chunk));
|
|
43
|
+
|
|
44
|
+
// --- Response Capture Logic ---
|
|
45
|
+
const originalWrite = res.write;
|
|
46
|
+
const originalEnd = res.end;
|
|
47
|
+
const resChunks = [];
|
|
48
|
+
|
|
49
|
+
res.write = function (chunk, ...args) {
|
|
50
|
+
if (chunk) resChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
51
|
+
return originalWrite.apply(res, [chunk, ...args]);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
res.end = function (chunk, ...args) {
|
|
55
|
+
if (chunk) resChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
56
|
+
|
|
57
|
+
// WRAP IN TRY/CATCH to prevent silent crashes
|
|
58
|
+
try {
|
|
59
|
+
const reqBody = Buffer.concat(reqChunks).toString();
|
|
60
|
+
const resBody = Buffer.concat(resChunks).toString('utf8');
|
|
61
|
+
|
|
62
|
+
const logData = {
|
|
63
|
+
timestamp: new Date().toISOString(),
|
|
64
|
+
method: req.method,
|
|
65
|
+
url: req.url,
|
|
66
|
+
status: res.statusCode,
|
|
67
|
+
requestBody: reqBody || '',
|
|
68
|
+
responseBody: resBody || '',
|
|
69
|
+
// requestHeaders: req.headers, // Uncomment if needed (verbose)
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const jsonLine = JSON.stringify(logData) + '\n';
|
|
73
|
+
|
|
74
|
+
// 3. Write and Log
|
|
75
|
+
stream.write(jsonLine);
|
|
76
|
+
onLog(logData);
|
|
77
|
+
|
|
78
|
+
// Debug Log to prove it worked
|
|
79
|
+
process.stdout.write('.'); // Prints a dot for every logged request
|
|
80
|
+
} catch (e) {
|
|
81
|
+
console.error('\nβ Error generating log:', e.message);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return originalEnd.apply(res, [chunk, ...args]);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// 4. Force Plain Text (Disable Gzip)
|
|
88
|
+
delete req.headers['accept-encoding'];
|
|
89
|
+
|
|
90
|
+
// 5. Forward to Target
|
|
91
|
+
// We must set 'buffer' to the request stream if we consumed it,
|
|
92
|
+
// but since we are just tapping 'data' without pausing, http-proxy might miss it.
|
|
93
|
+
// A simple workaround for node streams in this context:
|
|
94
|
+
this.proxy.web(req, res, { target });
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
this.server.listen(port, () => {
|
|
98
|
+
console.log(`\nβΊοΈ Recorder listening on port ${port}`);
|
|
99
|
+
console.log(`β‘οΈ Forwarding to ${target}`);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return `Recorder started`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
stop() {
|
|
106
|
+
if (this.server) {
|
|
107
|
+
this.server.close();
|
|
108
|
+
this.server = null;
|
|
109
|
+
return 'Recorder stopped';
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = new Recorder();
|