webhoster 0.1.0 → 0.3.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/.eslintrc.json +74 -58
- package/.github/copilot-instructions.md +100 -0
- package/.github/workflows/test-matrix.yml +37 -0
- package/.test/benchmark.js +28 -0
- package/.test/constants.js +4 -0
- package/{test → .test}/http2server.js +1 -1
- package/{test → .test}/httpserver.js +1 -1
- package/{test → .test}/index.js +178 -192
- package/.test/multipromise.js +32 -0
- package/{test → .test}/tls.js +3 -3
- package/{test → .test}/urlencoded.js +3 -0
- package/.vscode/launch.json +24 -3
- package/README.md +116 -90
- package/data/CookieObject.js +14 -14
- package/errata/socketio.js +6 -11
- package/examples/starter.js +11 -0
- package/helpers/HeadersParser.js +7 -8
- package/helpers/HttpListener.js +387 -42
- package/helpers/RequestHeaders.js +43 -36
- package/helpers/RequestReader.js +27 -26
- package/helpers/ResponseHeaders.js +47 -36
- package/jsconfig.json +1 -1
- package/lib/HttpHandler.js +447 -277
- package/lib/HttpRequest.js +383 -39
- package/lib/HttpResponse.js +316 -52
- package/lib/HttpTransaction.js +146 -0
- package/middleware/AutoHeadersMiddleware.js +73 -0
- package/middleware/CORSMiddleware.js +45 -47
- package/middleware/CaseInsensitiveHeadersMiddleware.js +5 -11
- package/middleware/ContentDecoderMiddleware.js +81 -35
- package/middleware/ContentEncoderMiddleware.js +179 -132
- package/middleware/ContentLengthMiddleware.js +66 -43
- package/middleware/ContentWriterMiddleware.js +5 -11
- package/middleware/HashMiddleware.js +68 -40
- package/middleware/HeadMethodMiddleware.js +24 -22
- package/middleware/MethodMiddleware.js +29 -36
- package/middleware/PathMiddleware.js +49 -66
- package/middleware/ReadFormData.js +99 -0
- package/middleware/SendHeadersMiddleware.js +0 -2
- package/middleware/SendJsonMiddleware.js +131 -0
- package/middleware/SendStringMiddleware.js +87 -0
- package/package.json +38 -29
- package/polyfill/FormData.js +164 -0
- package/rollup.config.js +0 -1
- package/scripts/test-all-sync.sh +6 -0
- package/scripts/test-all.sh +7 -0
- package/templates/starter.js +53 -0
- package/test/fixtures/stream.js +68 -0
- package/test/helpers/HttpListener/construct.js +18 -0
- package/test/helpers/HttpListener/customOptions.js +22 -0
- package/test/helpers/HttpListener/doubleCreate.js +40 -0
- package/test/helpers/HttpListener/events.js +77 -0
- package/test/helpers/HttpListener/http.js +31 -0
- package/test/helpers/HttpListener/http2.js +41 -0
- package/test/helpers/HttpListener/https.js +38 -0
- package/test/helpers/HttpListener/startAll.js +31 -0
- package/test/helpers/HttpListener/stopNotStarted.js +23 -0
- package/test/lib/HttpHandler/class.js +8 -0
- package/test/lib/HttpHandler/handleRequest.js +11 -0
- package/test/lib/HttpHandler/middleware.js +941 -0
- package/test/lib/HttpHandler/parse.js +41 -0
- package/test/lib/HttpRequest/class.js +8 -0
- package/test/lib/HttpRequest/downstream.js +171 -0
- package/test/lib/HttpRequest/properties.js +101 -0
- package/test/lib/HttpRequest/read.js +518 -0
- package/test/lib/HttpResponse/class.js +8 -0
- package/test/lib/HttpResponse/properties.js +59 -0
- package/test/lib/HttpResponse/send.js +275 -0
- package/test/lib/HttpTransaction/class.js +8 -0
- package/test/lib/HttpTransaction/ping.js +50 -0
- package/test/lib/HttpTransaction/push.js +89 -0
- package/test/middleware/SendJsonMiddleware.js +222 -0
- package/test/sanity.js +10 -0
- package/test/templates/starter.js +93 -0
- package/tsconfig.json +12 -0
- package/types/index.js +61 -34
- package/types/typings.d.ts +8 -9
- package/utils/AsyncObject.js +6 -3
- package/utils/CaseInsensitiveObject.js +2 -3
- package/utils/function.js +1 -7
- package/utils/headers.js +42 -0
- package/utils/qualityValues.js +1 -1
- package/utils/stream.js +4 -20
- package/index.cjs +0 -3200
- package/test/constants.js +0 -4
- /package/{test → .test}/cookietester.js +0 -0
|
@@ -0,0 +1,941 @@
|
|
|
1
|
+
import * as http from 'node:http';
|
|
2
|
+
|
|
3
|
+
import test from 'ava';
|
|
4
|
+
|
|
5
|
+
import HttpListener from '../../../helpers/HttpListener.js';
|
|
6
|
+
import HttpHandler from '../../../lib/HttpHandler.js';
|
|
7
|
+
|
|
8
|
+
test('HttpHandler.handleRequest() via HttpListener HTTP/1 server', async (t) => {
|
|
9
|
+
// Create a handler with simple middleware
|
|
10
|
+
const handler = new HttpHandler({
|
|
11
|
+
middleware: [
|
|
12
|
+
({ response }) => {
|
|
13
|
+
response.status = 200;
|
|
14
|
+
response.headers['content-type'] = 'text/plain';
|
|
15
|
+
return 'hello world';
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
});
|
|
19
|
+
// Start a real HTTP/1 server using HttpListener
|
|
20
|
+
const listener = new HttpListener({
|
|
21
|
+
insecurePort: 0,
|
|
22
|
+
httpHandler: handler,
|
|
23
|
+
});
|
|
24
|
+
const server = await listener.startHttpServer();
|
|
25
|
+
const address = server.address();
|
|
26
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
27
|
+
|
|
28
|
+
// Make a request to the server
|
|
29
|
+
const res = await new Promise((resolve, reject) => {
|
|
30
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
31
|
+
let data = '';
|
|
32
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
33
|
+
response.on('end', () => {
|
|
34
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
35
|
+
});
|
|
36
|
+
}).on('error', reject);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
t.is(res.status, 200);
|
|
40
|
+
t.is(res.headers['content-type'], 'text/plain');
|
|
41
|
+
t.is(res.body, 'hello world');
|
|
42
|
+
|
|
43
|
+
await listener.stopHttpServer();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('HttpHandler array middleware with BREAK and END flow', async (t) => {
|
|
47
|
+
const handler = new HttpHandler({
|
|
48
|
+
middleware: [
|
|
49
|
+
[
|
|
50
|
+
({ response }) => {
|
|
51
|
+
response.status = 201;
|
|
52
|
+
response.headers['x-branch'] = 'first';
|
|
53
|
+
return HttpHandler.BREAK;
|
|
54
|
+
},
|
|
55
|
+
({ response }) => {
|
|
56
|
+
response.headers['x-branch'] = 'should-not-run';
|
|
57
|
+
return HttpHandler.END;
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
({ response }) => {
|
|
61
|
+
response.headers['x-final'] = 'ran';
|
|
62
|
+
return 'branch done';
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
});
|
|
66
|
+
const listener = new HttpListener({
|
|
67
|
+
insecurePort: 0,
|
|
68
|
+
httpHandler: handler,
|
|
69
|
+
});
|
|
70
|
+
const server = await listener.startHttpServer();
|
|
71
|
+
const address = server.address();
|
|
72
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
73
|
+
|
|
74
|
+
const res = await new Promise((resolve, reject) => {
|
|
75
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
76
|
+
let data = '';
|
|
77
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
78
|
+
response.on('end', () => {
|
|
79
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
80
|
+
});
|
|
81
|
+
}).on('error', reject);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
t.is(res.status, 201);
|
|
85
|
+
t.is(res.headers['x-branch'], 'first');
|
|
86
|
+
t.is(res.headers['x-final'], 'ran');
|
|
87
|
+
t.is(res.body, 'branch done');
|
|
88
|
+
|
|
89
|
+
await listener.stopHttpServer();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('HttpHandler string middleware returns string as body', async (t) => {
|
|
93
|
+
const handler = new HttpHandler({
|
|
94
|
+
middleware: [
|
|
95
|
+
'direct string',
|
|
96
|
+
],
|
|
97
|
+
});
|
|
98
|
+
const listener = new HttpListener({
|
|
99
|
+
insecurePort: 0,
|
|
100
|
+
httpHandler: handler,
|
|
101
|
+
});
|
|
102
|
+
const server = await listener.startHttpServer();
|
|
103
|
+
const address = server.address();
|
|
104
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
105
|
+
|
|
106
|
+
const res = await new Promise((resolve, reject) => {
|
|
107
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
108
|
+
let data = '';
|
|
109
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
110
|
+
response.on('end', () => {
|
|
111
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
112
|
+
});
|
|
113
|
+
}).on('error', reject);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
t.is(res.status, 200);
|
|
117
|
+
t.is(res.body, 'direct string');
|
|
118
|
+
|
|
119
|
+
await listener.stopHttpServer();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('HttpHandler error handler middleware is called on error', async (t) => {
|
|
123
|
+
let errorHandled = false;
|
|
124
|
+
const handler = new HttpHandler({
|
|
125
|
+
middleware: [
|
|
126
|
+
function errorThrower() { throw new Error('fail'); },
|
|
127
|
+
],
|
|
128
|
+
errorHandlers: [
|
|
129
|
+
{
|
|
130
|
+
onError: (tx) => {
|
|
131
|
+
t.truthy(tx.error);
|
|
132
|
+
if (!tx.error) {
|
|
133
|
+
t.fail('tx.error should not be null');
|
|
134
|
+
return 500;
|
|
135
|
+
}
|
|
136
|
+
errorHandled = true;
|
|
137
|
+
tx.response.status = 500;
|
|
138
|
+
tx.response.headers['content-type'] = 'text/plain';
|
|
139
|
+
return `error: ${tx.error.message}`;
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
});
|
|
144
|
+
const listener = new HttpListener({
|
|
145
|
+
insecurePort: 0,
|
|
146
|
+
httpHandler: handler,
|
|
147
|
+
});
|
|
148
|
+
const server = await listener.startHttpServer();
|
|
149
|
+
const address = server.address();
|
|
150
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
151
|
+
|
|
152
|
+
const res = await new Promise((resolve, reject) => {
|
|
153
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
154
|
+
let data = '';
|
|
155
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
156
|
+
response.on('end', () => {
|
|
157
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
158
|
+
});
|
|
159
|
+
}).on('error', reject);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
t.is(res.status, 500);
|
|
163
|
+
t.is(res.headers['content-type'], 'text/plain');
|
|
164
|
+
t.true(res.body.startsWith('error: fail'));
|
|
165
|
+
t.true(errorHandled);
|
|
166
|
+
|
|
167
|
+
await listener.stopHttpServer();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('HttpHandler promise middleware resolves and returns value', async (t) => {
|
|
171
|
+
const handler = new HttpHandler({
|
|
172
|
+
middleware: [
|
|
173
|
+
({ response }) => {
|
|
174
|
+
response.status = 200;
|
|
175
|
+
response.headers['content-type'] = 'text/plain';
|
|
176
|
+
return Promise.resolve('async value');
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
});
|
|
180
|
+
const listener = new HttpListener({
|
|
181
|
+
insecurePort: 0,
|
|
182
|
+
httpHandler: handler,
|
|
183
|
+
});
|
|
184
|
+
const server = await listener.startHttpServer();
|
|
185
|
+
const address = server.address();
|
|
186
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
187
|
+
|
|
188
|
+
const res = await new Promise((resolve, reject) => {
|
|
189
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
190
|
+
let data = '';
|
|
191
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
192
|
+
response.on('end', () => {
|
|
193
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
194
|
+
});
|
|
195
|
+
}).on('error', reject);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
t.is(res.status, 200);
|
|
199
|
+
t.is(res.body, 'async value');
|
|
200
|
+
|
|
201
|
+
await listener.stopHttpServer();
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test('HttpHandler handles malformed request gracefully', async (t) => {
|
|
205
|
+
const handler = new HttpHandler({
|
|
206
|
+
middleware: [
|
|
207
|
+
({ response }) => {
|
|
208
|
+
response.status = 200;
|
|
209
|
+
response.headers['content-type'] = 'text/plain';
|
|
210
|
+
return 'ok';
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
errorHandlers: [
|
|
214
|
+
{
|
|
215
|
+
onError: (tx) => {
|
|
216
|
+
tx.response.status = 400;
|
|
217
|
+
tx.response.headers['content-type'] = 'text/plain';
|
|
218
|
+
return 'malformed';
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
});
|
|
223
|
+
const listener = new HttpListener({
|
|
224
|
+
insecurePort: 0,
|
|
225
|
+
httpHandler: handler,
|
|
226
|
+
});
|
|
227
|
+
const server = await listener.startHttpServer();
|
|
228
|
+
const address = server.address();
|
|
229
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
230
|
+
|
|
231
|
+
// Simulate a malformed request by closing the socket early
|
|
232
|
+
const net = await import('node:net');
|
|
233
|
+
await new Promise((resolve) => {
|
|
234
|
+
const client = net.createConnection({ port, host: '127.0.0.1' }, () => {
|
|
235
|
+
client.write('GET / HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n');
|
|
236
|
+
client.end();
|
|
237
|
+
resolve();
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Make a valid request to ensure server is still alive
|
|
242
|
+
const res = await new Promise((resolve, reject) => {
|
|
243
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
244
|
+
let data = '';
|
|
245
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
246
|
+
response.on('end', () => {
|
|
247
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
248
|
+
});
|
|
249
|
+
}).on('error', reject);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
t.is(res.status, 200);
|
|
253
|
+
t.is(res.body, 'ok');
|
|
254
|
+
|
|
255
|
+
await listener.stopHttpServer();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test('HttpHandler object middleware with .execute property', async (t) => {
|
|
259
|
+
const middlewareObj = {
|
|
260
|
+
execute(tx) {
|
|
261
|
+
tx.response.status = 200;
|
|
262
|
+
tx.response.headers['content-type'] = 'text/plain';
|
|
263
|
+
return 'executed';
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
const handler = new HttpHandler({ middleware: [middlewareObj] });
|
|
267
|
+
const listener = new HttpListener({ insecurePort: 0, httpHandler: handler });
|
|
268
|
+
const server = await listener.startHttpServer();
|
|
269
|
+
const address = server.address();
|
|
270
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
271
|
+
|
|
272
|
+
const res = await new Promise((resolve, reject) => {
|
|
273
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
274
|
+
let data = '';
|
|
275
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
276
|
+
response.on('end', () => {
|
|
277
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
278
|
+
});
|
|
279
|
+
}).on('error', reject);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
t.is(res.status, 200);
|
|
283
|
+
t.is(res.body, 'executed');
|
|
284
|
+
|
|
285
|
+
await listener.stopHttpServer();
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test('HttpHandler object middleware with .Execute static property', async (t) => {
|
|
289
|
+
const middlewareObj = {
|
|
290
|
+
Execute(tx) {
|
|
291
|
+
tx.response.status = 201;
|
|
292
|
+
tx.response.headers['content-type'] = 'text/plain';
|
|
293
|
+
return 'static executed';
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
const handler = new HttpHandler({ middleware: [middlewareObj] });
|
|
297
|
+
const listener = new HttpListener({ insecurePort: 0, httpHandler: handler });
|
|
298
|
+
const server = await listener.startHttpServer();
|
|
299
|
+
const address = server.address();
|
|
300
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
301
|
+
|
|
302
|
+
const res = await new Promise((resolve, reject) => {
|
|
303
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
304
|
+
let data = '';
|
|
305
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
306
|
+
response.on('end', () => {
|
|
307
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
308
|
+
});
|
|
309
|
+
}).on('error', reject);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
t.is(res.status, 201);
|
|
313
|
+
t.is(res.body, 'static executed');
|
|
314
|
+
|
|
315
|
+
await listener.stopHttpServer();
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test('HttpHandler promise-like object middleware (.then)', async (t) => {
|
|
319
|
+
const promiseLike = {
|
|
320
|
+
then(resolve) {
|
|
321
|
+
resolve('promise-like value');
|
|
322
|
+
},
|
|
323
|
+
};
|
|
324
|
+
const handler = new HttpHandler({ middleware: [promiseLike] });
|
|
325
|
+
const listener = new HttpListener({ insecurePort: 0, httpHandler: handler });
|
|
326
|
+
const server = await listener.startHttpServer();
|
|
327
|
+
const address = server.address();
|
|
328
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
329
|
+
|
|
330
|
+
const res = await new Promise((resolve, reject) => {
|
|
331
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
332
|
+
let data = '';
|
|
333
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
334
|
+
response.on('end', () => {
|
|
335
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
336
|
+
});
|
|
337
|
+
}).on('error', reject);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
t.is(res.status, 200);
|
|
341
|
+
t.is(res.body, 'promise-like value');
|
|
342
|
+
|
|
343
|
+
await listener.stopHttpServer();
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
test('HttpHandler number middleware sets status code', async (t) => {
|
|
347
|
+
const handler = new HttpHandler({ middleware: [404] });
|
|
348
|
+
const listener = new HttpListener({ insecurePort: 0, httpHandler: handler });
|
|
349
|
+
const server = await listener.startHttpServer();
|
|
350
|
+
const address = server.address();
|
|
351
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
352
|
+
|
|
353
|
+
const res = await new Promise((resolve, reject) => {
|
|
354
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
355
|
+
let data = '';
|
|
356
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
357
|
+
response.on('end', () => {
|
|
358
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
359
|
+
});
|
|
360
|
+
}).on('error', reject);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
t.is(res.status, 404);
|
|
364
|
+
t.is(res.body, '');
|
|
365
|
+
|
|
366
|
+
await listener.stopHttpServer();
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test('HttpHandler array middleware CONTINUE, BREAK, END E2E', async (t) => {
|
|
370
|
+
const handler = new HttpHandler({
|
|
371
|
+
middleware: [
|
|
372
|
+
[
|
|
373
|
+
HttpHandler.CONTINUE,
|
|
374
|
+
true,
|
|
375
|
+
null,
|
|
376
|
+
undefined,
|
|
377
|
+
() => HttpHandler.CONTINUE,
|
|
378
|
+
[
|
|
379
|
+
() => HttpHandler.BREAK,
|
|
380
|
+
500,
|
|
381
|
+
],
|
|
382
|
+
[
|
|
383
|
+
async () => await Promise.resolve(HttpHandler.BREAK),
|
|
384
|
+
500,
|
|
385
|
+
],
|
|
386
|
+
[
|
|
387
|
+
false,
|
|
388
|
+
500,
|
|
389
|
+
],
|
|
390
|
+
[
|
|
391
|
+
function test2() { return function inner() { return false; }; },
|
|
392
|
+
500,
|
|
393
|
+
],
|
|
394
|
+
[
|
|
395
|
+
Promise.resolve(false),
|
|
396
|
+
500,
|
|
397
|
+
],
|
|
398
|
+
({ response }) => {
|
|
399
|
+
response.status = 210;
|
|
400
|
+
response.headers['x-flow'] = 'continue';
|
|
401
|
+
return 'continued';
|
|
402
|
+
},
|
|
403
|
+
],
|
|
404
|
+
],
|
|
405
|
+
});
|
|
406
|
+
const listener = new HttpListener({ insecurePort: 0, httpHandler: handler });
|
|
407
|
+
const server = await listener.startHttpServer();
|
|
408
|
+
const address = server.address();
|
|
409
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
410
|
+
|
|
411
|
+
const res = await new Promise((resolve, reject) => {
|
|
412
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
413
|
+
let data = '';
|
|
414
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
415
|
+
response.on('end', () => {
|
|
416
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
417
|
+
});
|
|
418
|
+
}).on('error', reject);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
t.is(res.status, 210);
|
|
422
|
+
t.is(res.headers['x-flow'], 'continue');
|
|
423
|
+
t.is(res.body, 'continued');
|
|
424
|
+
|
|
425
|
+
await listener.stopHttpServer();
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
test('HttpHandler array middleware BREAK E2E', async (t) => {
|
|
429
|
+
const handler = new HttpHandler({
|
|
430
|
+
middleware: [
|
|
431
|
+
[
|
|
432
|
+
() => HttpHandler.BREAK,
|
|
433
|
+
({ response }) => {
|
|
434
|
+
response.status = 211;
|
|
435
|
+
response.headers['x-flow'] = 'should-not-run';
|
|
436
|
+
return 'should not run';
|
|
437
|
+
},
|
|
438
|
+
],
|
|
439
|
+
({ response }) => {
|
|
440
|
+
response.status = 212;
|
|
441
|
+
response.headers['x-flow'] = 'break';
|
|
442
|
+
return 'break ran';
|
|
443
|
+
},
|
|
444
|
+
],
|
|
445
|
+
});
|
|
446
|
+
const listener = new HttpListener({ insecurePort: 0, httpHandler: handler });
|
|
447
|
+
const server = await listener.startHttpServer();
|
|
448
|
+
const address = server.address();
|
|
449
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
450
|
+
|
|
451
|
+
const res = await new Promise((resolve, reject) => {
|
|
452
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
453
|
+
let data = '';
|
|
454
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
455
|
+
response.on('end', () => {
|
|
456
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
457
|
+
});
|
|
458
|
+
}).on('error', reject);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
t.is(res.status, 212);
|
|
462
|
+
t.is(res.headers['x-flow'], 'break');
|
|
463
|
+
t.is(res.body, 'break ran');
|
|
464
|
+
|
|
465
|
+
await listener.stopHttpServer();
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
test('HttpHandler array middleware END E2E', async (t) => {
|
|
469
|
+
const handler = new HttpHandler({
|
|
470
|
+
middleware: [
|
|
471
|
+
[
|
|
472
|
+
false,
|
|
473
|
+
({ response }) => {
|
|
474
|
+
response.status = 213;
|
|
475
|
+
response.headers['x-flow'] = 'should-not-run';
|
|
476
|
+
return 'should not run';
|
|
477
|
+
},
|
|
478
|
+
],
|
|
479
|
+
({ response }) => {
|
|
480
|
+
response.status = 214;
|
|
481
|
+
response.headers['x-flow'] = 'end';
|
|
482
|
+
response.body = 'end ran';
|
|
483
|
+
},
|
|
484
|
+
HttpHandler.END,
|
|
485
|
+
({ response }) => {
|
|
486
|
+
response.status = 215;
|
|
487
|
+
response.headers['x-flow'] = 'should-not-run2';
|
|
488
|
+
return 'should not run';
|
|
489
|
+
},
|
|
490
|
+
],
|
|
491
|
+
});
|
|
492
|
+
const listener = new HttpListener({ insecurePort: 0, httpHandler: handler });
|
|
493
|
+
const server = await listener.startHttpServer();
|
|
494
|
+
const address = server.address();
|
|
495
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
496
|
+
|
|
497
|
+
const res = await new Promise((resolve, reject) => {
|
|
498
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
499
|
+
let data = '';
|
|
500
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
501
|
+
response.on('end', () => {
|
|
502
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
503
|
+
});
|
|
504
|
+
}).on('error', reject);
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
t.is(res.status, 214);
|
|
508
|
+
t.is(res.headers['x-flow'], 'end');
|
|
509
|
+
t.is(res.body, 'end ran');
|
|
510
|
+
|
|
511
|
+
await listener.stopHttpServer();
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
test('HttpHandler array middleware with promise-like (.then) E2E', async (t) => {
|
|
515
|
+
const promiseLike = {
|
|
516
|
+
then(resolve) {
|
|
517
|
+
resolve('promise-array-value');
|
|
518
|
+
},
|
|
519
|
+
};
|
|
520
|
+
const handler = new HttpHandler({
|
|
521
|
+
middleware: [
|
|
522
|
+
[promiseLike],
|
|
523
|
+
],
|
|
524
|
+
});
|
|
525
|
+
const listener = new HttpListener({ insecurePort: 0, httpHandler: handler });
|
|
526
|
+
const server = await listener.startHttpServer();
|
|
527
|
+
const address = server.address();
|
|
528
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
529
|
+
|
|
530
|
+
const res = await new Promise((resolve, reject) => {
|
|
531
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
532
|
+
let data = '';
|
|
533
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
534
|
+
response.on('end', () => {
|
|
535
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
536
|
+
});
|
|
537
|
+
}).on('error', reject);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
t.is(res.status, 200);
|
|
541
|
+
t.is(res.body, 'promise-array-value');
|
|
542
|
+
|
|
543
|
+
await listener.stopHttpServer();
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
test('HttpHandler does not call next middleware after error', async (t) => {
|
|
547
|
+
let nextCalled = false;
|
|
548
|
+
const handler = new HttpHandler({
|
|
549
|
+
middleware: [
|
|
550
|
+
function errorThrower() { throw new Error('fail'); },
|
|
551
|
+
() => { nextCalled = true; return 'should not run'; },
|
|
552
|
+
],
|
|
553
|
+
errorHandlers: [
|
|
554
|
+
{
|
|
555
|
+
onError: (tx) => {
|
|
556
|
+
tx.response.status = 500;
|
|
557
|
+
tx.response.headers['content-type'] = 'text/plain';
|
|
558
|
+
return 'error handled';
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
],
|
|
562
|
+
});
|
|
563
|
+
const listener = new HttpListener({ insecurePort: 0, httpHandler: handler });
|
|
564
|
+
const server = await listener.startHttpServer();
|
|
565
|
+
const address = server.address();
|
|
566
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
567
|
+
|
|
568
|
+
const res = await new Promise((resolve, reject) => {
|
|
569
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
570
|
+
let data = '';
|
|
571
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
572
|
+
response.on('end', () => {
|
|
573
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
574
|
+
});
|
|
575
|
+
}).on('error', reject);
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
t.is(res.status, 500);
|
|
579
|
+
t.is(res.body, 'error handled');
|
|
580
|
+
t.false(nextCalled, 'Next middleware should not be called after error');
|
|
581
|
+
|
|
582
|
+
await listener.stopHttpServer();
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
test('HttpHandler calls inline error handler in middleware', async (t) => {
|
|
586
|
+
let inlineErrorCalled = false;
|
|
587
|
+
let defaultErrorCalled = false;
|
|
588
|
+
const handler = new HttpHandler({
|
|
589
|
+
middleware: [
|
|
590
|
+
function errorThrower() { throw new Error('fail'); },
|
|
591
|
+
{
|
|
592
|
+
onError(tx) {
|
|
593
|
+
inlineErrorCalled = true;
|
|
594
|
+
tx.response.status = 501;
|
|
595
|
+
tx.response.headers['content-type'] = 'text/plain';
|
|
596
|
+
return 'inline error';
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
],
|
|
600
|
+
errorHandlers: [
|
|
601
|
+
{
|
|
602
|
+
onError: (tx) => {
|
|
603
|
+
defaultErrorCalled = true;
|
|
604
|
+
tx.response.status = 502;
|
|
605
|
+
tx.response.headers['content-type'] = 'text/plain';
|
|
606
|
+
console.log('default error handler called');
|
|
607
|
+
return 'default error';
|
|
608
|
+
},
|
|
609
|
+
},
|
|
610
|
+
],
|
|
611
|
+
});
|
|
612
|
+
const listener = new HttpListener({ insecurePort: 0, httpHandler: handler });
|
|
613
|
+
const server = await listener.startHttpServer();
|
|
614
|
+
const address = server.address();
|
|
615
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
616
|
+
|
|
617
|
+
// Trigger error to hit inline error handler
|
|
618
|
+
const res = await new Promise((resolve, reject) => {
|
|
619
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
620
|
+
let data = '';
|
|
621
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
622
|
+
response.on('end', () => {
|
|
623
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
624
|
+
});
|
|
625
|
+
}).on('error', reject);
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
t.is(res.status, 501);
|
|
629
|
+
t.is(res.body, 'inline error');
|
|
630
|
+
t.true(inlineErrorCalled, 'Inline error handler should be called');
|
|
631
|
+
t.false(defaultErrorCalled, 'Default error handler should not be called');
|
|
632
|
+
|
|
633
|
+
await listener.stopHttpServer();
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
test('HttpHandler clears error on sync onError', async (t) => {
|
|
637
|
+
let errorCleared = false;
|
|
638
|
+
const handler = new HttpHandler({
|
|
639
|
+
middleware: [
|
|
640
|
+
function errorThrower() { throw new Error('fail'); },
|
|
641
|
+
{
|
|
642
|
+
onError: (tx) => {
|
|
643
|
+
if (tx.error == null) t.fail('tx.error should not be null');
|
|
644
|
+
tx.response.status = 503;
|
|
645
|
+
tx.response.headers['content-type'] = 'text/plain';
|
|
646
|
+
tx.response.body = 'sync error cleared';
|
|
647
|
+
},
|
|
648
|
+
},
|
|
649
|
+
(tx) => {
|
|
650
|
+
errorCleared = tx.error == null;
|
|
651
|
+
},
|
|
652
|
+
200,
|
|
653
|
+
],
|
|
654
|
+
errorHandlers: [],
|
|
655
|
+
});
|
|
656
|
+
const listener = new HttpListener({ insecurePort: 0, httpHandler: handler });
|
|
657
|
+
const server = await listener.startHttpServer();
|
|
658
|
+
const address = server.address();
|
|
659
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
660
|
+
|
|
661
|
+
const res = await new Promise((resolve, reject) => {
|
|
662
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
663
|
+
let data = '';
|
|
664
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
665
|
+
response.on('end', () => {
|
|
666
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
667
|
+
});
|
|
668
|
+
}).on('error', reject);
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
t.is(res.status, 200);
|
|
672
|
+
t.is(res.body, 'sync error cleared');
|
|
673
|
+
t.true(errorCleared, 'Error should be cleared after sync onError');
|
|
674
|
+
|
|
675
|
+
await listener.stopHttpServer();
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
test('HttpHandler clears error on async onError', async (t) => {
|
|
679
|
+
let errorCleared = false;
|
|
680
|
+
const handler = new HttpHandler({
|
|
681
|
+
middleware: [
|
|
682
|
+
function errorThrower() { throw new Error('fail'); },
|
|
683
|
+
{
|
|
684
|
+
onError: async (tx) => {
|
|
685
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
686
|
+
tx.response.status = 500;
|
|
687
|
+
tx.response.headers['content-type'] = 'text/plain';
|
|
688
|
+
tx.response.body = 'async error cleared';
|
|
689
|
+
},
|
|
690
|
+
},
|
|
691
|
+
(tx) => {
|
|
692
|
+
errorCleared = tx.error == null;
|
|
693
|
+
},
|
|
694
|
+
200,
|
|
695
|
+
],
|
|
696
|
+
errorHandlers: [],
|
|
697
|
+
});
|
|
698
|
+
const listener = new HttpListener({ insecurePort: 0, httpHandler: handler });
|
|
699
|
+
const server = await listener.startHttpServer();
|
|
700
|
+
const address = server.address();
|
|
701
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
702
|
+
|
|
703
|
+
const res = await new Promise((resolve, reject) => {
|
|
704
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
705
|
+
let data = '';
|
|
706
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
707
|
+
response.on('end', () => {
|
|
708
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
709
|
+
});
|
|
710
|
+
}).on('error', reject);
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
t.is(res.status, 200);
|
|
714
|
+
t.is(res.body, 'async error cleared');
|
|
715
|
+
t.true(errorCleared, 'Error should be cleared after async onError');
|
|
716
|
+
|
|
717
|
+
await listener.stopHttpServer();
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
test('HttpHandler clears error on promise-returning onError', async (t) => {
|
|
721
|
+
let errorCleared = false;
|
|
722
|
+
const handler = new HttpHandler({
|
|
723
|
+
middleware: [
|
|
724
|
+
function errorThrower() { throw new Error('fail'); },
|
|
725
|
+
{
|
|
726
|
+
onError: (tx) => new Promise((resolve) => {
|
|
727
|
+
setTimeout(() => {
|
|
728
|
+
tx.response.status = 500;
|
|
729
|
+
tx.response.headers['content-type'] = 'text/plain';
|
|
730
|
+
tx.response.body = 'promise error cleared';
|
|
731
|
+
resolve();
|
|
732
|
+
}, 10);
|
|
733
|
+
}),
|
|
734
|
+
},
|
|
735
|
+
(tx) => {
|
|
736
|
+
errorCleared = tx.error == null;
|
|
737
|
+
},
|
|
738
|
+
200,
|
|
739
|
+
],
|
|
740
|
+
errorHandlers: [],
|
|
741
|
+
});
|
|
742
|
+
const listener = new HttpListener({ insecurePort: 0, httpHandler: handler });
|
|
743
|
+
const server = await listener.startHttpServer();
|
|
744
|
+
const address = server.address();
|
|
745
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
746
|
+
|
|
747
|
+
const res = await new Promise((resolve, reject) => {
|
|
748
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
749
|
+
let data = '';
|
|
750
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
751
|
+
response.on('end', () => {
|
|
752
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
753
|
+
});
|
|
754
|
+
}).on('error', reject);
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
t.is(res.status, 200);
|
|
758
|
+
t.is(res.body, 'promise error cleared');
|
|
759
|
+
t.true(errorCleared, 'Error should be cleared after promise-returning onError');
|
|
760
|
+
|
|
761
|
+
await listener.stopHttpServer();
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
test('HttpHandler clears error before sibling middleware after onError', async (t) => {
|
|
765
|
+
let errorWasPresentInOnError = false;
|
|
766
|
+
let errorWasClearedInSibling = false;
|
|
767
|
+
const handler = new HttpHandler({
|
|
768
|
+
middleware: [
|
|
769
|
+
function errorThrower() { throw new Error('fail'); },
|
|
770
|
+
{
|
|
771
|
+
onError: (tx) => {
|
|
772
|
+
errorWasPresentInOnError = tx.error instanceof Error;
|
|
773
|
+
tx.response.status = 500;
|
|
774
|
+
// Assume error was handled here
|
|
775
|
+
},
|
|
776
|
+
},
|
|
777
|
+
(tx) => {
|
|
778
|
+
errorWasClearedInSibling = tx.error == null;
|
|
779
|
+
tx.response.status = 200;
|
|
780
|
+
return 'sibling ran';
|
|
781
|
+
},
|
|
782
|
+
],
|
|
783
|
+
});
|
|
784
|
+
const listener = new HttpListener({ insecurePort: 0, httpHandler: handler });
|
|
785
|
+
const server = await listener.startHttpServer();
|
|
786
|
+
const address = server.address();
|
|
787
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
788
|
+
|
|
789
|
+
const res = await new Promise((resolve, reject) => {
|
|
790
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
791
|
+
let data = '';
|
|
792
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
793
|
+
response.on('end', () => {
|
|
794
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
795
|
+
});
|
|
796
|
+
}).on('error', reject);
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
t.is(res.status, 200);
|
|
800
|
+
t.is(res.body, 'sibling ran');
|
|
801
|
+
t.true(errorWasPresentInOnError, 'Error should be present inside onError handler');
|
|
802
|
+
t.true(errorWasClearedInSibling, 'Error should be cleared before sibling middleware runs');
|
|
803
|
+
|
|
804
|
+
await listener.stopHttpServer();
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
test('HttpHandler does not call onError if no error occurs', async (t) => {
|
|
808
|
+
let onErrorCalled = false;
|
|
809
|
+
const handler = new HttpHandler({
|
|
810
|
+
middleware: [
|
|
811
|
+
(tx) => {
|
|
812
|
+
tx.response.status = 200;
|
|
813
|
+
tx.response.body = 'no error';
|
|
814
|
+
},
|
|
815
|
+
{
|
|
816
|
+
onError: () => {
|
|
817
|
+
onErrorCalled = true;
|
|
818
|
+
},
|
|
819
|
+
},
|
|
820
|
+
200,
|
|
821
|
+
],
|
|
822
|
+
});
|
|
823
|
+
const listener = new HttpListener({ insecurePort: 0, httpHandler: handler });
|
|
824
|
+
const server = await listener.startHttpServer();
|
|
825
|
+
const address = server.address();
|
|
826
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
827
|
+
|
|
828
|
+
const res = await new Promise((resolve, reject) => {
|
|
829
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
830
|
+
let data = '';
|
|
831
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
832
|
+
response.on('end', () => {
|
|
833
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
834
|
+
});
|
|
835
|
+
}).on('error', reject);
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
t.is(res.status, 200);
|
|
839
|
+
t.is(res.body, 'no error');
|
|
840
|
+
t.false(onErrorCalled, 'onError should NOT be called if there is no error');
|
|
841
|
+
|
|
842
|
+
await listener.stopHttpServer();
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
test('HttpHandler throws if middleware returns negative status code', async (t) => {
|
|
846
|
+
const handler = new HttpHandler({
|
|
847
|
+
middleware: [
|
|
848
|
+
-404,
|
|
849
|
+
],
|
|
850
|
+
errorHandlers: [
|
|
851
|
+
{
|
|
852
|
+
onError: () => 502,
|
|
853
|
+
},
|
|
854
|
+
],
|
|
855
|
+
|
|
856
|
+
});
|
|
857
|
+
const listener = new HttpListener({ insecurePort: 0, httpHandler: handler });
|
|
858
|
+
const server = await listener.startHttpServer();
|
|
859
|
+
const address = server.address();
|
|
860
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
861
|
+
|
|
862
|
+
const res = await new Promise((resolve, reject) => {
|
|
863
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
864
|
+
let data = '';
|
|
865
|
+
response.on('data', (chunk) => { data += chunk; });
|
|
866
|
+
response.on('end', () => {
|
|
867
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
868
|
+
});
|
|
869
|
+
}).on('error', reject);
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
t.is(res.status, 502);
|
|
873
|
+
|
|
874
|
+
await listener.stopHttpServer();
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
test('HttpHandler middleware can send back Buffer as body', async (t) => {
|
|
878
|
+
const handler = new HttpHandler({
|
|
879
|
+
middleware: [
|
|
880
|
+
({ response }) => {
|
|
881
|
+
response.status = 200;
|
|
882
|
+
response.headers['content-type'] = 'application/octet-stream';
|
|
883
|
+
return Buffer.from('ok');
|
|
884
|
+
},
|
|
885
|
+
],
|
|
886
|
+
});
|
|
887
|
+
const listener = new HttpListener({ insecurePort: 0, httpHandler: handler });
|
|
888
|
+
const server = await listener.startHttpServer();
|
|
889
|
+
const address = server.address();
|
|
890
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
891
|
+
|
|
892
|
+
const res = await new Promise((resolve, reject) => {
|
|
893
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
894
|
+
let data = Buffer.alloc(0);
|
|
895
|
+
response.on('data', (chunk) => { data = Buffer.concat([data, chunk]); });
|
|
896
|
+
response.on('end', () => {
|
|
897
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
898
|
+
});
|
|
899
|
+
}).on('error', reject);
|
|
900
|
+
});
|
|
901
|
+
|
|
902
|
+
t.is(res.status, 200);
|
|
903
|
+
t.is(res.headers['content-type'], 'application/octet-stream');
|
|
904
|
+
t.true(Buffer.isBuffer(res.body));
|
|
905
|
+
t.is(res.body.toString(), 'ok');
|
|
906
|
+
|
|
907
|
+
await listener.stopHttpServer();
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
test('HttpHandler middleware can send back Uint8Array as body', async (t) => {
|
|
911
|
+
const handler = new HttpHandler({
|
|
912
|
+
middleware: [
|
|
913
|
+
({ response }) => {
|
|
914
|
+
response.status = 200;
|
|
915
|
+
response.headers['content-type'] = 'application/octet-stream';
|
|
916
|
+
return new Uint8Array([111, 107]); // 'ok'
|
|
917
|
+
},
|
|
918
|
+
],
|
|
919
|
+
});
|
|
920
|
+
const listener = new HttpListener({ insecurePort: 0, httpHandler: handler });
|
|
921
|
+
const server = await listener.startHttpServer();
|
|
922
|
+
const address = server.address();
|
|
923
|
+
const port = typeof address === 'object' && address && 'port' in address ? Number(address.port) : Number(address);
|
|
924
|
+
|
|
925
|
+
const res = await new Promise((resolve, reject) => {
|
|
926
|
+
http.get({ port, path: '/', hostname: '127.0.0.1' }, (response) => {
|
|
927
|
+
let data = Buffer.alloc(0);
|
|
928
|
+
response.on('data', (chunk) => { data = Buffer.concat([data, chunk]); });
|
|
929
|
+
response.on('end', () => {
|
|
930
|
+
resolve({ status: response.statusCode, headers: response.headers, body: data });
|
|
931
|
+
});
|
|
932
|
+
}).on('error', reject);
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
t.is(res.status, 200);
|
|
936
|
+
t.is(res.headers['content-type'], 'application/octet-stream');
|
|
937
|
+
t.true(Buffer.isBuffer(res.body));
|
|
938
|
+
t.is(res.body.toString(), 'ok');
|
|
939
|
+
|
|
940
|
+
await listener.stopHttpServer();
|
|
941
|
+
});
|