simple-http-bin 0.1.0 → 0.1.1
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 +6 -0
- package/cli.js +79 -4
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
A tiny local httpbin-like utility.
|
|
4
4
|
|
|
5
|
+
Requests are logged to the console.
|
|
6
|
+
Use `-v` or `--verbose` for more detail. Repeat `-v` for extra detail.
|
|
7
|
+
Colored output is enabled when the terminal supports it.
|
|
8
|
+
|
|
5
9
|
## Install
|
|
6
10
|
|
|
7
11
|
```bash
|
|
@@ -25,6 +29,8 @@ npx simple-http-bin
|
|
|
25
29
|
```bash
|
|
26
30
|
simple-http-bin --port 8080
|
|
27
31
|
simple-http-bin -p 8080
|
|
32
|
+
simple-http-bin -v
|
|
33
|
+
simple-http-bin -vv
|
|
28
34
|
PORT=8080 simple-http-bin
|
|
29
35
|
HOST=0.0.0.0 simple-http-bin
|
|
30
36
|
```
|
package/cli.js
CHANGED
|
@@ -1,14 +1,52 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const http = require('node:http');
|
|
4
|
+
const chalk = require('chalk');
|
|
4
5
|
|
|
5
6
|
const DEFAULT_PORT = 51234;
|
|
6
7
|
const args = process.argv.slice(2);
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
let cliPort;
|
|
9
|
+
let verbosity = 0;
|
|
10
|
+
|
|
11
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
12
|
+
const arg = args[i];
|
|
13
|
+
|
|
14
|
+
if (arg === '--port' || arg === '-p') {
|
|
15
|
+
cliPort = Number(args[i + 1]);
|
|
16
|
+
i += 1;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (arg === '--verbose') {
|
|
21
|
+
verbosity += 1;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (/^-v+$/.test(arg)) {
|
|
26
|
+
verbosity += arg.length - 1;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (cliPort === undefined && !arg.startsWith('-')) {
|
|
31
|
+
cliPort = Number(arg);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
9
35
|
const envPort = Number(process.env.PORT);
|
|
10
36
|
const port = Number.isInteger(cliPort) && cliPort > 0 ? cliPort : Number.isInteger(envPort) && envPort > 0 ? envPort : DEFAULT_PORT;
|
|
11
37
|
const host = process.env.HOST || '127.0.0.1';
|
|
38
|
+
const logLevel = Math.max(1, verbosity + 1);
|
|
39
|
+
|
|
40
|
+
const colors = {
|
|
41
|
+
method: chalk.cyan.bold,
|
|
42
|
+
path: chalk.white,
|
|
43
|
+
statusOk: chalk.green.bold,
|
|
44
|
+
statusWarn: chalk.yellow.bold,
|
|
45
|
+
statusError: chalk.red.bold,
|
|
46
|
+
meta: chalk.gray,
|
|
47
|
+
dim: chalk.dim,
|
|
48
|
+
title: chalk.green.bold,
|
|
49
|
+
};
|
|
12
50
|
|
|
13
51
|
function sendJson(res, statusCode, payload) {
|
|
14
52
|
const body = JSON.stringify(payload, null, 2);
|
|
@@ -28,12 +66,43 @@ function readBody(req) {
|
|
|
28
66
|
});
|
|
29
67
|
}
|
|
30
68
|
|
|
69
|
+
function logRequest(req, res, path, startedAt, details = {}) {
|
|
70
|
+
res.on('finish', () => {
|
|
71
|
+
const durationMs = Date.now() - startedAt;
|
|
72
|
+
const remoteAddress = req.socket.remoteAddress || 'unknown';
|
|
73
|
+
const remotePort = req.socket.remotePort || 'unknown';
|
|
74
|
+
const userAgent = req.headers['user-agent'] || 'unknown';
|
|
75
|
+
const statusColor = res.statusCode >= 500 ? colors.statusError : res.statusCode >= 400 ? colors.statusWarn : colors.statusOk;
|
|
76
|
+
const statusText = statusColor(res.statusCode);
|
|
77
|
+
const baseLine = `${colors.method(req.method)} ${colors.path(path)} ${colors.meta('->')} ${statusText} ${colors.dim(`(${durationMs}ms)`)}`;
|
|
78
|
+
|
|
79
|
+
if (logLevel === 1) {
|
|
80
|
+
console.log(baseLine);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (logLevel === 2) {
|
|
85
|
+
console.log(`${baseLine} ${colors.meta('from')} ${colors.meta(`${remoteAddress}:${remotePort}`)} ${colors.meta('ua=')}${chalk.white(JSON.stringify(userAgent))}`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
console.log([
|
|
90
|
+
`${baseLine} ${colors.meta('from')} ${colors.meta(`${remoteAddress}:${remotePort}`)}`,
|
|
91
|
+
`${colors.meta('ua=')}${chalk.white(JSON.stringify(userAgent))}`,
|
|
92
|
+
`${colors.meta('headers=')}${chalk.white(JSON.stringify(req.headers, null, 2))}`,
|
|
93
|
+
details.bodyBytes !== undefined ? `${colors.meta('bodyBytes=')}${chalk.white(details.bodyBytes)}` : null,
|
|
94
|
+
].filter(Boolean).join('\n'));
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
31
98
|
function helpText(baseUrl) {
|
|
32
99
|
return [
|
|
33
|
-
'simple-http-bin',
|
|
100
|
+
colors.title('simple-http-bin'),
|
|
34
101
|
'',
|
|
35
|
-
|
|
102
|
+
`${colors.meta('Running at:')} ${chalk.white(baseUrl)}`,
|
|
36
103
|
`Port: ${port} (use --port <n> or PORT=<n>)`,
|
|
104
|
+
'Requests are logged to the console.',
|
|
105
|
+
'Use -v or --verbose for more detail; repeat -v for even more.',
|
|
37
106
|
'',
|
|
38
107
|
'Endpoints:',
|
|
39
108
|
' GET / Show this help text',
|
|
@@ -49,21 +118,25 @@ function helpText(baseUrl) {
|
|
|
49
118
|
}
|
|
50
119
|
|
|
51
120
|
const server = http.createServer(async (req, res) => {
|
|
121
|
+
const startedAt = Date.now();
|
|
52
122
|
const url = new URL(req.url, `http://${req.headers.host || `${host}:${port}`}`);
|
|
53
123
|
const path = url.pathname;
|
|
54
124
|
|
|
55
125
|
if (req.method === 'GET' && path === '/') {
|
|
126
|
+
logRequest(req, res, `${path}${url.search}`, startedAt);
|
|
56
127
|
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
57
128
|
res.end(helpText(`http://${host}:${port}`));
|
|
58
129
|
return;
|
|
59
130
|
}
|
|
60
131
|
|
|
61
132
|
if (path === '/headers') {
|
|
133
|
+
logRequest(req, res, `${path}${url.search}`, startedAt);
|
|
62
134
|
sendJson(res, 200, { headers: req.headers });
|
|
63
135
|
return;
|
|
64
136
|
}
|
|
65
137
|
|
|
66
138
|
if (path.startsWith('/status/')) {
|
|
139
|
+
logRequest(req, res, `${path}${url.search}`, startedAt);
|
|
67
140
|
const code = Number(path.split('/')[2]);
|
|
68
141
|
res.writeHead(Number.isInteger(code) ? code : 400, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
69
142
|
res.end(`status ${Number.isInteger(code) ? code : 400}`);
|
|
@@ -72,6 +145,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
72
145
|
|
|
73
146
|
if (path === '/anything') {
|
|
74
147
|
const body = await readBody(req);
|
|
148
|
+
logRequest(req, res, `${path}${url.search}`, startedAt, { bodyBytes: Buffer.byteLength(body) });
|
|
75
149
|
sendJson(res, 200, {
|
|
76
150
|
method: req.method,
|
|
77
151
|
path,
|
|
@@ -82,6 +156,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
82
156
|
return;
|
|
83
157
|
}
|
|
84
158
|
|
|
159
|
+
logRequest(req, res, `${path}${url.search}`, startedAt);
|
|
85
160
|
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
86
161
|
res.end('not found');
|
|
87
162
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "simple-http-bin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "A tiny local httpbin-like utility",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
"publishConfig": {
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"chalk": "^4.1.2"
|
|
21
|
+
},
|
|
19
22
|
"bin": {
|
|
20
23
|
"simple-http-bin": "cli.js"
|
|
21
24
|
},
|