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.
Files changed (3) hide show
  1. package/README.md +6 -0
  2. package/cli.js +79 -4
  3. 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
- const portFlagIndex = args.findIndex((arg) => arg === '--port' || arg === '-p');
8
- const cliPort = portFlagIndex >= 0 ? Number(args[portFlagIndex + 1]) : Number(args[0]);
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
- `Running at: ${baseUrl}`,
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.0",
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
  },