@wooksjs/event-http 0.4.25 → 0.4.27
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 +8 -8
- package/dist/index.cjs +360 -379
- package/dist/index.d.ts +123 -136
- package/dist/index.mjs +360 -379
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
var eventCore = require('@wooksjs/event-core');
|
|
4
4
|
var url = require('url');
|
|
5
|
-
var stream = require('stream');
|
|
6
|
-
var wooks = require('wooks');
|
|
7
5
|
var http = require('http');
|
|
6
|
+
var wooks = require('wooks');
|
|
7
|
+
var stream = require('stream');
|
|
8
8
|
|
|
9
9
|
function createHttpContext(data, options) {
|
|
10
10
|
return eventCore.createEventContext({
|
|
@@ -19,58 +19,106 @@ function useHttpContext() {
|
|
|
19
19
|
return eventCore.useEventContext('HTTP');
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
function escapeRegex(s) {
|
|
23
|
+
return s.replace(/[$()*+./?[\\\]^{|}-]/g, '\\$&');
|
|
24
|
+
}
|
|
25
|
+
function safeDecode(f, v) {
|
|
26
|
+
try {
|
|
27
|
+
return f(v);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
return v;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function safeDecodeURIComponent(uri) {
|
|
34
|
+
if (!uri.includes('%')) {
|
|
35
|
+
return uri;
|
|
36
|
+
}
|
|
37
|
+
return safeDecode(decodeURIComponent, uri);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function convertTime(time, unit = 'ms') {
|
|
41
|
+
if (typeof time === 'number') {
|
|
42
|
+
return time / units[unit];
|
|
43
|
+
}
|
|
44
|
+
const rg = /(\d+)(\w+)/g;
|
|
45
|
+
let t = 0;
|
|
46
|
+
let r;
|
|
47
|
+
while ((r = rg.exec(time))) {
|
|
48
|
+
t += Number(r[1]) * (units[r[2]] || 0);
|
|
49
|
+
}
|
|
50
|
+
return t / units[unit];
|
|
51
|
+
}
|
|
52
|
+
const units = {
|
|
53
|
+
ms: 1,
|
|
54
|
+
s: 1000,
|
|
55
|
+
m: 1000 * 60,
|
|
56
|
+
h: 1000 * 60 * 60,
|
|
57
|
+
d: 1000 * 60 * 60 * 24,
|
|
58
|
+
w: 1000 * 60 * 60 * 24 * 7,
|
|
59
|
+
M: 1000 * 60 * 60 * 24 * 30,
|
|
60
|
+
Y: 1000 * 60 * 60 * 24 * 365,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
function renderCookie(key, data) {
|
|
64
|
+
let attrs = '';
|
|
65
|
+
for (const [a, v] of Object.entries(data.attrs)) {
|
|
66
|
+
const func = cookieAttrFunc[a];
|
|
67
|
+
if (typeof func === 'function') {
|
|
68
|
+
const val = func(v);
|
|
69
|
+
attrs += val ? `; ${val}` : '';
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
throw new TypeError(`Unknown Set-Cookie attribute ${a}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return `${key}=${encodeURIComponent(data.value)}${attrs}`;
|
|
76
|
+
}
|
|
77
|
+
const cookieAttrFunc = {
|
|
78
|
+
expires: (v) => `Expires=${typeof v === 'string' || typeof v === 'number' ? new Date(v).toUTCString() : v.toUTCString()}`,
|
|
79
|
+
maxAge: (v) => `Max-Age=${convertTime(v, 's').toString()}`,
|
|
80
|
+
domain: (v) => `Domain=${v}`,
|
|
81
|
+
path: (v) => `Path=${v}`,
|
|
82
|
+
secure: (v) => (v ? 'Secure' : ''),
|
|
83
|
+
httpOnly: (v) => (v ? 'HttpOnly' : ''),
|
|
84
|
+
sameSite: (v) => v ? `SameSite=${typeof v === 'string' ? v : 'Strict'}` : '',
|
|
85
|
+
};
|
|
86
|
+
|
|
22
87
|
const xForwardedFor = 'x-forwarded-for';
|
|
23
88
|
function useRequest() {
|
|
24
89
|
const { store } = useHttpContext();
|
|
25
90
|
const { init } = store('request');
|
|
26
91
|
const event = store('event');
|
|
27
|
-
const
|
|
28
|
-
const rawBody = () => init('rawBody', () => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
body = Buffer.concat([body, chunk]);
|
|
33
|
-
});
|
|
34
|
-
req.on('error', function (err) {
|
|
35
|
-
reject(err);
|
|
36
|
-
});
|
|
37
|
-
req.on('end', function () {
|
|
38
|
-
resolve(body);
|
|
39
|
-
});
|
|
92
|
+
const req = event.get('req');
|
|
93
|
+
const rawBody = async () => init('rawBody', async () => new Promise((resolve, reject) => {
|
|
94
|
+
let body = Buffer.from('');
|
|
95
|
+
req.on('data', chunk => {
|
|
96
|
+
body = Buffer.concat([body, chunk]);
|
|
40
97
|
});
|
|
41
|
-
|
|
98
|
+
req.on('error', err => {
|
|
99
|
+
reject(err);
|
|
100
|
+
});
|
|
101
|
+
req.on('end', () => {
|
|
102
|
+
resolve(body);
|
|
103
|
+
});
|
|
104
|
+
}));
|
|
42
105
|
const reqId = eventCore.useEventId().getId;
|
|
43
106
|
const forwardedIp = () => init('forwardedIp', () => {
|
|
44
|
-
if (typeof req.headers[xForwardedFor] === 'string' &&
|
|
45
|
-
req.headers[xForwardedFor])
|
|
46
|
-
return req.headers[xForwardedFor]
|
|
47
|
-
.split(',')
|
|
48
|
-
.shift()
|
|
49
|
-
?.trim();
|
|
107
|
+
if (typeof req.headers[xForwardedFor] === 'string' && req.headers[xForwardedFor]) {
|
|
108
|
+
return req.headers[xForwardedFor].split(',').shift()?.trim();
|
|
50
109
|
}
|
|
51
110
|
else {
|
|
52
111
|
return '';
|
|
53
112
|
}
|
|
54
113
|
});
|
|
55
|
-
const remoteIp = () => init('remoteIp', () => req.socket
|
|
114
|
+
const remoteIp = () => init('remoteIp', () => req.socket.remoteAddress || req.connection.remoteAddress || '');
|
|
56
115
|
function getIp(options) {
|
|
57
|
-
|
|
58
|
-
return forwardedIp() || getIp();
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
return remoteIp();
|
|
62
|
-
}
|
|
116
|
+
return options?.trustProxy ? forwardedIp() || getIp() : remoteIp();
|
|
63
117
|
}
|
|
64
|
-
const getIpList = () => init('ipList', () => {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
'',
|
|
69
|
-
forwarded: (req.headers[xForwardedFor] || '')
|
|
70
|
-
.split(',')
|
|
71
|
-
.map((s) => s.trim()),
|
|
72
|
-
};
|
|
73
|
-
});
|
|
118
|
+
const getIpList = () => init('ipList', () => ({
|
|
119
|
+
remoteIp: req.socket.remoteAddress || req.connection.remoteAddress || '',
|
|
120
|
+
forwarded: (req.headers[xForwardedFor] || '').split(',').map(s => s.trim()),
|
|
121
|
+
}));
|
|
74
122
|
return {
|
|
75
123
|
rawRequest: req,
|
|
76
124
|
url: req.url,
|
|
@@ -113,13 +161,73 @@ function useSetHeader(name) {
|
|
|
113
161
|
return hook(name);
|
|
114
162
|
}
|
|
115
163
|
|
|
164
|
+
function useCookies() {
|
|
165
|
+
const { store } = useHttpContext();
|
|
166
|
+
const { cookie } = useHeaders();
|
|
167
|
+
const { init } = store('cookies');
|
|
168
|
+
const getCookie = (name) => init(name, () => {
|
|
169
|
+
if (cookie) {
|
|
170
|
+
const result = new RegExp(`(?:^|; )${escapeRegex(name)}=(.*?)(?:;?$|; )`, 'i').exec(cookie);
|
|
171
|
+
return result?.[1] ? safeDecodeURIComponent(result[1]) : null;
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
return {
|
|
178
|
+
rawCookies: cookie,
|
|
179
|
+
getCookie,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function useSetCookies() {
|
|
183
|
+
const { store } = useHttpContext();
|
|
184
|
+
const cookiesStore = store('setCookies');
|
|
185
|
+
function setCookie(name, value, attrs) {
|
|
186
|
+
cookiesStore.set(name, {
|
|
187
|
+
value,
|
|
188
|
+
attrs: attrs || {},
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
function cookies() {
|
|
192
|
+
return cookiesStore
|
|
193
|
+
.entries()
|
|
194
|
+
.filter(a => Boolean(a[1]))
|
|
195
|
+
.map(([key, value]) => renderCookie(key, value));
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
setCookie,
|
|
199
|
+
getCookie: cookiesStore.get,
|
|
200
|
+
removeCookie: cookiesStore.del,
|
|
201
|
+
clearCookies: cookiesStore.clear,
|
|
202
|
+
cookies,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function useSetCookie(name) {
|
|
206
|
+
const { setCookie, getCookie } = useSetCookies();
|
|
207
|
+
const valueHook = eventCore.attachHook({
|
|
208
|
+
name,
|
|
209
|
+
type: 'cookie',
|
|
210
|
+
}, {
|
|
211
|
+
get: () => getCookie(name).value,
|
|
212
|
+
set: (value) => {
|
|
213
|
+
setCookie(name, value, getCookie(name).attrs);
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
return eventCore.attachHook(valueHook, {
|
|
217
|
+
get: () => getCookie(name).attrs,
|
|
218
|
+
set: (attrs) => {
|
|
219
|
+
setCookie(name, getCookie(name).value || '', attrs);
|
|
220
|
+
},
|
|
221
|
+
}, 'attrs');
|
|
222
|
+
}
|
|
223
|
+
|
|
116
224
|
function useAccept() {
|
|
117
225
|
const { store } = useHttpContext();
|
|
118
226
|
const { accept } = useHeaders();
|
|
119
227
|
const accepts = (mime) => {
|
|
120
228
|
const { set, get, has } = store('accept');
|
|
121
229
|
if (!has(mime)) {
|
|
122
|
-
return set(mime,
|
|
230
|
+
return set(mime, Boolean(accept && (accept === '*/*' || accept.includes(mime))));
|
|
123
231
|
}
|
|
124
232
|
return get(mime);
|
|
125
233
|
};
|
|
@@ -171,62 +279,39 @@ function useAuthorization() {
|
|
|
171
279
|
};
|
|
172
280
|
}
|
|
173
281
|
|
|
174
|
-
function convertTime(time, unit = 'ms') {
|
|
175
|
-
if (typeof time === 'number')
|
|
176
|
-
return time / units[unit];
|
|
177
|
-
const rg = /(\d+)(\w+)/g;
|
|
178
|
-
let t = 0;
|
|
179
|
-
let r;
|
|
180
|
-
while ((r = rg.exec(time))) {
|
|
181
|
-
t += Number(r[1]) * (units[r[2]] || 0);
|
|
182
|
-
}
|
|
183
|
-
return t / units[unit];
|
|
184
|
-
}
|
|
185
|
-
const units = {
|
|
186
|
-
ms: 1,
|
|
187
|
-
s: 1000,
|
|
188
|
-
m: 1000 * 60,
|
|
189
|
-
h: 1000 * 60 * 60,
|
|
190
|
-
d: 1000 * 60 * 60 * 24,
|
|
191
|
-
w: 1000 * 60 * 60 * 24 * 7,
|
|
192
|
-
M: 1000 * 60 * 60 * 24 * 30,
|
|
193
|
-
Y: 1000 * 60 * 60 * 24 * 365,
|
|
194
|
-
};
|
|
195
|
-
|
|
196
282
|
function renderCacheControl(data) {
|
|
197
283
|
let attrs = '';
|
|
198
284
|
for (const [a, v] of Object.entries(data)) {
|
|
199
|
-
if (
|
|
285
|
+
if (v === undefined) {
|
|
200
286
|
continue;
|
|
287
|
+
}
|
|
201
288
|
const func = cacheControlFunc[a];
|
|
202
289
|
if (typeof func === 'function') {
|
|
203
290
|
const val = func(v);
|
|
204
291
|
if (val) {
|
|
205
|
-
attrs += attrs ?
|
|
292
|
+
attrs += attrs ? `, ${val}` : val;
|
|
206
293
|
}
|
|
207
294
|
}
|
|
208
295
|
else {
|
|
209
|
-
throw new
|
|
296
|
+
throw new TypeError(`Unknown Cache-Control attribute ${a}`);
|
|
210
297
|
}
|
|
211
298
|
}
|
|
212
299
|
return attrs;
|
|
213
300
|
}
|
|
214
301
|
const cacheControlFunc = {
|
|
215
|
-
mustRevalidate: (v) => v ? 'must-revalidate' : '',
|
|
302
|
+
mustRevalidate: (v) => (v ? 'must-revalidate' : ''),
|
|
216
303
|
noCache: (v) => v ? (typeof v === 'string' ? `no-cache="${v}"` : 'no-cache') : '',
|
|
217
304
|
noStore: (v) => (v ? 'no-store' : ''),
|
|
218
305
|
noTransform: (v) => (v ? 'no-transform' : ''),
|
|
219
306
|
public: (v) => (v ? 'public' : ''),
|
|
220
307
|
private: (v) => v ? (typeof v === 'string' ? `private="${v}"` : 'private') : '',
|
|
221
|
-
proxyRevalidate: (v) => v ? 'proxy-revalidate' : '',
|
|
222
|
-
maxAge: (v) =>
|
|
223
|
-
sMaxage: (v) =>
|
|
308
|
+
proxyRevalidate: (v) => (v ? 'proxy-revalidate' : ''),
|
|
309
|
+
maxAge: (v) => `max-age=${convertTime(v, 's').toString()}`,
|
|
310
|
+
sMaxage: (v) => `s-maxage=${convertTime(v, 's').toString()}`,
|
|
224
311
|
};
|
|
225
312
|
|
|
226
313
|
const renderAge = (v) => convertTime(v, 's').toString();
|
|
227
|
-
const renderExpires = (v) => typeof v === 'string' || typeof v === 'number'
|
|
228
|
-
? new Date(v).toUTCString()
|
|
229
|
-
: v.toUTCString();
|
|
314
|
+
const renderExpires = (v) => typeof v === 'string' || typeof v === 'number' ? new Date(v).toUTCString() : v.toUTCString();
|
|
230
315
|
const renderPragmaNoCache = (v) => (v ? 'no-cache' : '');
|
|
231
316
|
function useSetCacheControl() {
|
|
232
317
|
const { setHeader } = useSetHeaders();
|
|
@@ -253,15 +338,16 @@ function useSetCacheControl() {
|
|
|
253
338
|
function useResponse() {
|
|
254
339
|
const { store } = useHttpContext();
|
|
255
340
|
const event = store('event');
|
|
256
|
-
const
|
|
341
|
+
const res = event.get('res');
|
|
257
342
|
const responded = store('response').hook('responded');
|
|
258
343
|
const statusCode = store('status').hook('code');
|
|
259
344
|
function status(code) {
|
|
260
|
-
return (statusCode.value = code
|
|
345
|
+
return (statusCode.value = code || statusCode.value);
|
|
261
346
|
}
|
|
262
347
|
const rawResponse = (options) => {
|
|
263
|
-
if (!options || !options.passthrough)
|
|
348
|
+
if (!options || !options.passthrough) {
|
|
264
349
|
responded.value = true;
|
|
350
|
+
}
|
|
265
351
|
return res;
|
|
266
352
|
};
|
|
267
353
|
return {
|
|
@@ -278,108 +364,6 @@ function useStatus() {
|
|
|
278
364
|
return store('status').hook('code');
|
|
279
365
|
}
|
|
280
366
|
|
|
281
|
-
function renderCookie(key, data) {
|
|
282
|
-
let attrs = '';
|
|
283
|
-
for (const [a, v] of Object.entries(data.attrs)) {
|
|
284
|
-
const func = cookieAttrFunc[a];
|
|
285
|
-
if (typeof func === 'function') {
|
|
286
|
-
const val = func(v);
|
|
287
|
-
attrs += val ? '; ' + val : '';
|
|
288
|
-
}
|
|
289
|
-
else {
|
|
290
|
-
throw new Error('Unknown Set-Cookie attribute ' + a);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
return `${key}=${encodeURIComponent(data.value)}${attrs}`;
|
|
294
|
-
}
|
|
295
|
-
const cookieAttrFunc = {
|
|
296
|
-
expires: (v) => 'Expires=' +
|
|
297
|
-
(typeof v === 'string' || typeof v === 'number'
|
|
298
|
-
? new Date(v).toUTCString()
|
|
299
|
-
: v.toUTCString()),
|
|
300
|
-
maxAge: (v) => 'Max-Age=' + convertTime(v, 's').toString(),
|
|
301
|
-
domain: (v) => 'Domain=' + v,
|
|
302
|
-
path: (v) => 'Path=' + v,
|
|
303
|
-
secure: (v) => (v ? 'Secure' : ''),
|
|
304
|
-
httpOnly: (v) => (v ? 'HttpOnly' : ''),
|
|
305
|
-
sameSite: (v) => v ? 'SameSite=' + (typeof v === 'string' ? v : 'Strict') : '',
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
function escapeRegex(s) {
|
|
309
|
-
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
310
|
-
}
|
|
311
|
-
function safeDecode(f, v) {
|
|
312
|
-
try {
|
|
313
|
-
return f(v);
|
|
314
|
-
}
|
|
315
|
-
catch (e) {
|
|
316
|
-
return v;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
function safeDecodeURIComponent(uri) {
|
|
320
|
-
if (uri.indexOf('%') < 0)
|
|
321
|
-
return uri;
|
|
322
|
-
return safeDecode(decodeURIComponent, uri);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
function useCookies() {
|
|
326
|
-
const { store } = useHttpContext();
|
|
327
|
-
const { cookie } = useHeaders();
|
|
328
|
-
const { init } = store('cookies');
|
|
329
|
-
const getCookie = (name) => init(name, () => {
|
|
330
|
-
if (cookie) {
|
|
331
|
-
const result = new RegExp(`(?:^|; )${escapeRegex(name)}=(.*?)(?:;?$|; )`, 'i').exec(cookie);
|
|
332
|
-
return result && result[1]
|
|
333
|
-
? safeDecodeURIComponent(result[1])
|
|
334
|
-
: null;
|
|
335
|
-
}
|
|
336
|
-
else {
|
|
337
|
-
return null;
|
|
338
|
-
}
|
|
339
|
-
});
|
|
340
|
-
return {
|
|
341
|
-
rawCookies: cookie,
|
|
342
|
-
getCookie,
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
function useSetCookies() {
|
|
346
|
-
const { store } = useHttpContext();
|
|
347
|
-
const cookiesStore = store('setCookies');
|
|
348
|
-
function setCookie(name, value, attrs) {
|
|
349
|
-
cookiesStore.set(name, {
|
|
350
|
-
value,
|
|
351
|
-
attrs: attrs || {},
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
function cookies() {
|
|
355
|
-
return cookiesStore
|
|
356
|
-
.entries()
|
|
357
|
-
.filter((a) => !!a[1])
|
|
358
|
-
.map(([key, value]) => renderCookie(key, value));
|
|
359
|
-
}
|
|
360
|
-
return {
|
|
361
|
-
setCookie,
|
|
362
|
-
getCookie: cookiesStore.get,
|
|
363
|
-
removeCookie: cookiesStore.del,
|
|
364
|
-
clearCookies: cookiesStore.clear,
|
|
365
|
-
cookies,
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
function useSetCookie(name) {
|
|
369
|
-
const { setCookie, getCookie } = useSetCookies();
|
|
370
|
-
const valueHook = eventCore.attachHook({
|
|
371
|
-
name,
|
|
372
|
-
type: 'cookie',
|
|
373
|
-
}, {
|
|
374
|
-
get: () => getCookie(name)?.value,
|
|
375
|
-
set: (value) => setCookie(name, value, getCookie(name)?.attrs),
|
|
376
|
-
});
|
|
377
|
-
return eventCore.attachHook(valueHook, {
|
|
378
|
-
get: () => getCookie(name)?.attrs,
|
|
379
|
-
set: (attrs) => setCookie(name, getCookie(name)?.value || '', attrs),
|
|
380
|
-
}, 'attrs');
|
|
381
|
-
}
|
|
382
|
-
|
|
383
367
|
class WooksURLSearchParams extends url.URLSearchParams {
|
|
384
368
|
toJson() {
|
|
385
369
|
const json = {};
|
|
@@ -420,89 +404,91 @@ class BaseHttpResponseRenderer {
|
|
|
420
404
|
if (typeof response.body === 'string' ||
|
|
421
405
|
typeof response.body === 'boolean' ||
|
|
422
406
|
typeof response.body === 'number') {
|
|
423
|
-
if (!response.getContentType())
|
|
407
|
+
if (!response.getContentType()) {
|
|
424
408
|
response.setContentType('text/plain');
|
|
409
|
+
}
|
|
425
410
|
return response.body.toString();
|
|
426
411
|
}
|
|
427
|
-
if (
|
|
412
|
+
if (response.body === undefined) {
|
|
428
413
|
return '';
|
|
429
414
|
}
|
|
430
415
|
if (response.body instanceof Uint8Array) {
|
|
431
416
|
return response.body;
|
|
432
417
|
}
|
|
433
418
|
if (typeof response.body === 'object') {
|
|
434
|
-
if (!response.getContentType())
|
|
419
|
+
if (!response.getContentType()) {
|
|
435
420
|
response.setContentType('application/json');
|
|
421
|
+
}
|
|
436
422
|
return JSON.stringify(response.body);
|
|
437
423
|
}
|
|
438
|
-
throw new Error(
|
|
424
|
+
throw new Error(`Unsupported body format "${typeof response.body}"`);
|
|
439
425
|
}
|
|
440
426
|
}
|
|
441
427
|
|
|
442
428
|
const httpStatusCodes = {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
429
|
+
100: 'Continue',
|
|
430
|
+
101: 'Switching protocols',
|
|
431
|
+
102: 'Processing',
|
|
432
|
+
103: 'Early Hints',
|
|
433
|
+
200: 'OK',
|
|
434
|
+
201: 'Created',
|
|
435
|
+
202: 'Accepted',
|
|
436
|
+
203: 'Non-Authoritative Information',
|
|
437
|
+
204: 'No Content',
|
|
438
|
+
205: 'Reset Content',
|
|
439
|
+
206: 'Partial Content',
|
|
440
|
+
207: 'Multi-Status',
|
|
441
|
+
208: 'Already Reported',
|
|
442
|
+
226: 'IM Used',
|
|
443
|
+
300: 'Multiple Choices',
|
|
444
|
+
301: 'Moved Permanently',
|
|
445
|
+
302: 'Found (Previously "Moved Temporarily")',
|
|
446
|
+
303: 'See Other',
|
|
447
|
+
304: 'Not Modified',
|
|
448
|
+
305: 'Use Proxy',
|
|
449
|
+
306: 'Switch Proxy',
|
|
450
|
+
307: 'Temporary Redirect',
|
|
451
|
+
308: 'Permanent Redirect',
|
|
452
|
+
400: 'Bad Request',
|
|
453
|
+
401: 'Unauthorized',
|
|
454
|
+
402: 'Payment Required',
|
|
455
|
+
403: 'Forbidden',
|
|
456
|
+
404: 'Not Found',
|
|
457
|
+
405: 'Method Not Allowed',
|
|
458
|
+
406: 'Not Acceptable',
|
|
459
|
+
407: 'Proxy Authentication Required',
|
|
460
|
+
408: 'Request Timeout',
|
|
461
|
+
409: 'Conflict',
|
|
462
|
+
410: 'Gone',
|
|
463
|
+
411: 'Length Required',
|
|
464
|
+
412: 'Precondition Failed',
|
|
465
|
+
413: 'Payload Too Large',
|
|
466
|
+
414: 'URI Too Long',
|
|
467
|
+
415: 'Unsupported Media Type',
|
|
468
|
+
416: 'Range Not Satisfiable',
|
|
469
|
+
417: 'Expectation Failed',
|
|
470
|
+
418: "I'm a Teapot",
|
|
471
|
+
421: 'Misdirected Request',
|
|
472
|
+
422: 'Unprocessable Entity',
|
|
473
|
+
423: 'Locked',
|
|
474
|
+
424: 'Failed Dependency',
|
|
475
|
+
425: 'Too Early',
|
|
476
|
+
426: 'Upgrade Required',
|
|
477
|
+
428: 'Precondition Required',
|
|
478
|
+
429: 'Too Many Requests',
|
|
479
|
+
431: 'Request Header Fields Too Large',
|
|
480
|
+
451: 'Unavailable For Legal Reasons',
|
|
481
|
+
500: 'Internal Server Error',
|
|
482
|
+
501: 'Not Implemented',
|
|
483
|
+
502: 'Bad Gateway',
|
|
484
|
+
503: 'Service Unavailable',
|
|
485
|
+
504: 'Gateway Timeout',
|
|
486
|
+
505: 'HTTP Version Not Supported',
|
|
487
|
+
506: 'Variant Also Negotiates',
|
|
488
|
+
507: 'Insufficient Storage',
|
|
489
|
+
508: 'Loop Detected',
|
|
490
|
+
510: 'Not Extended',
|
|
491
|
+
511: 'Network Authentication Required',
|
|
506
492
|
};
|
|
507
493
|
exports.EHttpStatusCode = void 0;
|
|
508
494
|
(function (EHttpStatusCode) {
|
|
@@ -571,6 +557,110 @@ exports.EHttpStatusCode = void 0;
|
|
|
571
557
|
EHttpStatusCode[EHttpStatusCode["NetworkAuthenticationRequired"] = 511] = "NetworkAuthenticationRequired";
|
|
572
558
|
})(exports.EHttpStatusCode || (exports.EHttpStatusCode = {}));
|
|
573
559
|
|
|
560
|
+
const preStyles = 'font-family: monospace;' +
|
|
561
|
+
'width: 100%;' +
|
|
562
|
+
'max-width: 900px;' +
|
|
563
|
+
'padding: 10px;' +
|
|
564
|
+
'margin: 20px auto;' +
|
|
565
|
+
'border-radius: 8px;' +
|
|
566
|
+
'background-color: #494949;' +
|
|
567
|
+
'box-shadow: 0px 0px 3px 2px rgb(255 255 255 / 20%);';
|
|
568
|
+
class HttpErrorRenderer extends BaseHttpResponseRenderer {
|
|
569
|
+
renderHtml(response) {
|
|
570
|
+
const data = response.body || {};
|
|
571
|
+
response.setContentType('text/html');
|
|
572
|
+
const keys = Object.keys(data).filter(key => !['statusCode', 'error', 'message'].includes(key));
|
|
573
|
+
return ('<html style="background-color: #333; color: #bbb;">' +
|
|
574
|
+
`<head><title>${data.statusCode} ${httpStatusCodes[data.statusCode]}</title></head>` +
|
|
575
|
+
`<body><center><h1>${data.statusCode} ${httpStatusCodes[data.statusCode]}</h1></center>` +
|
|
576
|
+
`<center><h4>${data.message}</h1></center><hr color="#666">` +
|
|
577
|
+
`<center style="color: #666;"> Wooks v${"0.4.27"} </center>` +
|
|
578
|
+
`${keys.length > 0
|
|
579
|
+
? `<pre style="${preStyles}">${JSON.stringify({
|
|
580
|
+
...data,
|
|
581
|
+
statusCode: undefined,
|
|
582
|
+
message: undefined,
|
|
583
|
+
error: undefined,
|
|
584
|
+
}, null, ' ')}</pre>`
|
|
585
|
+
: ''}` +
|
|
586
|
+
'</body></html>');
|
|
587
|
+
}
|
|
588
|
+
renderText(response) {
|
|
589
|
+
const data = response.body || {};
|
|
590
|
+
response.setContentType('text/plain');
|
|
591
|
+
const keys = Object.keys(data).filter(key => !['statusCode', 'error', 'message'].includes(key));
|
|
592
|
+
return (`${data.statusCode} ${httpStatusCodes[data.statusCode]}\n${data.message}` +
|
|
593
|
+
`\n\n${keys.length > 0
|
|
594
|
+
? `${JSON.stringify({
|
|
595
|
+
...data,
|
|
596
|
+
statusCode: undefined,
|
|
597
|
+
message: undefined,
|
|
598
|
+
error: undefined,
|
|
599
|
+
}, null, ' ')}`
|
|
600
|
+
: ''}`);
|
|
601
|
+
}
|
|
602
|
+
renderJson(response) {
|
|
603
|
+
const data = response.body || {};
|
|
604
|
+
response.setContentType('application/json');
|
|
605
|
+
const keys = Object.keys(data).filter(key => !['statusCode', 'error', 'message'].includes(key));
|
|
606
|
+
return (`{"statusCode":${escapeQuotes(data.statusCode)},` +
|
|
607
|
+
`"error":"${escapeQuotes(data.error)}",` +
|
|
608
|
+
`"message":"${escapeQuotes(data.message)}"` +
|
|
609
|
+
`${keys.length > 0
|
|
610
|
+
?
|
|
611
|
+
`,${keys.map(k => `"${escapeQuotes(k)}":${JSON.stringify(data[k])}`).join(',')}`
|
|
612
|
+
: ''}}`);
|
|
613
|
+
}
|
|
614
|
+
render(response) {
|
|
615
|
+
const { acceptsJson, acceptsText, acceptsHtml } = useAccept();
|
|
616
|
+
response.status = response.body?.statusCode || 500;
|
|
617
|
+
if (acceptsJson()) {
|
|
618
|
+
return this.renderJson(response);
|
|
619
|
+
}
|
|
620
|
+
else if (acceptsHtml()) {
|
|
621
|
+
return this.renderHtml(response);
|
|
622
|
+
}
|
|
623
|
+
else if (acceptsText()) {
|
|
624
|
+
return this.renderText(response);
|
|
625
|
+
}
|
|
626
|
+
else {
|
|
627
|
+
return this.renderJson(response);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
function escapeQuotes(s) {
|
|
632
|
+
return (typeof s === 'number' ? s : s || '').toString().replace(/"/g, '\\"');
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
class HttpError extends Error {
|
|
636
|
+
constructor(code = 500, _body = '') {
|
|
637
|
+
super(typeof _body === 'string' ? _body : _body.message);
|
|
638
|
+
this.code = code;
|
|
639
|
+
this._body = _body;
|
|
640
|
+
this.name = 'HttpError';
|
|
641
|
+
}
|
|
642
|
+
get body() {
|
|
643
|
+
return typeof this._body === 'string'
|
|
644
|
+
? {
|
|
645
|
+
statusCode: this.code,
|
|
646
|
+
message: this.message,
|
|
647
|
+
error: httpStatusCodes[this.code],
|
|
648
|
+
}
|
|
649
|
+
: {
|
|
650
|
+
...this._body,
|
|
651
|
+
statusCode: this.code,
|
|
652
|
+
message: this.message,
|
|
653
|
+
error: httpStatusCodes[this.code],
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
attachRenderer(renderer) {
|
|
657
|
+
this.renderer = renderer;
|
|
658
|
+
}
|
|
659
|
+
getRenderer() {
|
|
660
|
+
return this.renderer;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
574
664
|
const defaultStatus = {
|
|
575
665
|
GET: exports.EHttpStatusCode.OK,
|
|
576
666
|
POST: exports.EHttpStatusCode.Created,
|
|
@@ -651,7 +741,7 @@ class BaseHttpResponse {
|
|
|
651
741
|
...this._headers,
|
|
652
742
|
};
|
|
653
743
|
const setCookie = [...newCookies, ...cookies()];
|
|
654
|
-
if (setCookie
|
|
744
|
+
if (setCookie.length > 0) {
|
|
655
745
|
this._headers['set-cookie'] = setCookie;
|
|
656
746
|
}
|
|
657
747
|
return this;
|
|
@@ -698,7 +788,7 @@ class BaseHttpResponse {
|
|
|
698
788
|
}
|
|
699
789
|
else {
|
|
700
790
|
return new Promise((resolve, reject) => {
|
|
701
|
-
stream.on('error',
|
|
791
|
+
stream.on('error', e => {
|
|
702
792
|
stream.destroy();
|
|
703
793
|
res.end();
|
|
704
794
|
reject(e);
|
|
@@ -711,8 +801,7 @@ class BaseHttpResponse {
|
|
|
711
801
|
});
|
|
712
802
|
}
|
|
713
803
|
}
|
|
714
|
-
else if (globalThis.Response &&
|
|
715
|
-
this.body instanceof Response) {
|
|
804
|
+
else if (globalThis.Response && this.body instanceof Response) {
|
|
716
805
|
this.mergeFetchStatus(this.body.status);
|
|
717
806
|
if (method === 'HEAD') {
|
|
718
807
|
res.end();
|
|
@@ -735,10 +824,12 @@ class BaseHttpResponse {
|
|
|
735
824
|
else {
|
|
736
825
|
const renderedBody = this.renderer.render(this);
|
|
737
826
|
this.mergeStatus(renderedBody);
|
|
738
|
-
res
|
|
827
|
+
res
|
|
828
|
+
.writeHead(this.status, {
|
|
739
829
|
'content-length': Buffer.byteLength(renderedBody),
|
|
740
830
|
...this._headers,
|
|
741
|
-
})
|
|
831
|
+
})
|
|
832
|
+
.end(method === 'HEAD' ? '' : renderedBody);
|
|
742
833
|
}
|
|
743
834
|
}
|
|
744
835
|
}
|
|
@@ -749,133 +840,21 @@ async function respondWithFetch(fetchBody, res) {
|
|
|
749
840
|
res.write(chunk);
|
|
750
841
|
}
|
|
751
842
|
}
|
|
752
|
-
catch (
|
|
843
|
+
catch (error) {
|
|
753
844
|
}
|
|
754
845
|
}
|
|
755
846
|
res.end();
|
|
756
847
|
}
|
|
757
848
|
|
|
758
|
-
const preStyles = 'font-family: monospace;' +
|
|
759
|
-
'width: 100%;' +
|
|
760
|
-
'max-width: 900px;' +
|
|
761
|
-
'padding: 10px;' +
|
|
762
|
-
'margin: 20px auto;' +
|
|
763
|
-
'border-radius: 8px;' +
|
|
764
|
-
'background-color: #494949;' +
|
|
765
|
-
'box-shadow: 0px 0px 3px 2px rgb(255 255 255 / 20%);';
|
|
766
|
-
class HttpErrorRenderer extends BaseHttpResponseRenderer {
|
|
767
|
-
renderHtml(response) {
|
|
768
|
-
const data = response.body || {};
|
|
769
|
-
response.setContentType('text/html');
|
|
770
|
-
const keys = Object.keys(data).filter((key) => !['statusCode', 'error', 'message'].includes(key));
|
|
771
|
-
return ('<html style="background-color: #333; color: #bbb;">' +
|
|
772
|
-
`<head><title>${data.statusCode} ${httpStatusCodes[data.statusCode]}</title></head>` +
|
|
773
|
-
`<body><center><h1>${data.statusCode} ${httpStatusCodes[data.statusCode]}</h1></center>` +
|
|
774
|
-
`<center><h4>${data.message}</h1></center><hr color="#666">` +
|
|
775
|
-
`<center style="color: #666;"> Wooks v${"0.4.25"} </center>` +
|
|
776
|
-
`${keys.length
|
|
777
|
-
? `<pre style="${preStyles}">${JSON.stringify({
|
|
778
|
-
...data,
|
|
779
|
-
statusCode: undefined,
|
|
780
|
-
message: undefined,
|
|
781
|
-
error: undefined,
|
|
782
|
-
}, null, ' ')}</pre>`
|
|
783
|
-
: ''}` +
|
|
784
|
-
'</body></html>');
|
|
785
|
-
}
|
|
786
|
-
renderText(response) {
|
|
787
|
-
const data = response.body || {};
|
|
788
|
-
response.setContentType('text/plain');
|
|
789
|
-
const keys = Object.keys(data).filter((key) => !['statusCode', 'error', 'message'].includes(key));
|
|
790
|
-
return (`${data.statusCode} ${httpStatusCodes[data.statusCode]}\n${data.message}` +
|
|
791
|
-
`\n\n${keys.length
|
|
792
|
-
? `${JSON.stringify({
|
|
793
|
-
...data,
|
|
794
|
-
statusCode: undefined,
|
|
795
|
-
message: undefined,
|
|
796
|
-
error: undefined,
|
|
797
|
-
}, null, ' ')}`
|
|
798
|
-
: ''}`);
|
|
799
|
-
}
|
|
800
|
-
renderJson(response) {
|
|
801
|
-
const data = response.body || {};
|
|
802
|
-
response.setContentType('application/json');
|
|
803
|
-
const keys = Object.keys(data).filter((key) => !['statusCode', 'error', 'message'].includes(key));
|
|
804
|
-
return (`{"statusCode":${escapeQuotes(data.statusCode)},` +
|
|
805
|
-
`"error":"${escapeQuotes(data.error)}",` +
|
|
806
|
-
`"message":"${escapeQuotes(data.message)}"` +
|
|
807
|
-
`${keys.length
|
|
808
|
-
? ',' +
|
|
809
|
-
keys
|
|
810
|
-
.map((k) => `"${escapeQuotes(k)}":${JSON.stringify(data[k])}`)
|
|
811
|
-
.join(',')
|
|
812
|
-
: ''}}`);
|
|
813
|
-
}
|
|
814
|
-
render(response) {
|
|
815
|
-
const { acceptsJson, acceptsText, acceptsHtml } = useAccept();
|
|
816
|
-
response.status = response.body?.statusCode || 500;
|
|
817
|
-
if (acceptsJson()) {
|
|
818
|
-
return this.renderJson(response);
|
|
819
|
-
}
|
|
820
|
-
else if (acceptsHtml()) {
|
|
821
|
-
return this.renderHtml(response);
|
|
822
|
-
}
|
|
823
|
-
else if (acceptsText()) {
|
|
824
|
-
return this.renderText(response);
|
|
825
|
-
}
|
|
826
|
-
else {
|
|
827
|
-
return this.renderJson(response);
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
function escapeQuotes(s) {
|
|
832
|
-
return (typeof s === 'number' ? s : s || '')
|
|
833
|
-
.toString()
|
|
834
|
-
.replace(/[\""]/g, '\\"');
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
class HttpError extends Error {
|
|
838
|
-
constructor(code = 500, _body = '') {
|
|
839
|
-
super(typeof _body === 'string' ? _body : _body.message);
|
|
840
|
-
this.code = code;
|
|
841
|
-
this._body = _body;
|
|
842
|
-
}
|
|
843
|
-
get body() {
|
|
844
|
-
return typeof this._body === 'string'
|
|
845
|
-
? {
|
|
846
|
-
statusCode: this.code,
|
|
847
|
-
message: this.message,
|
|
848
|
-
error: httpStatusCodes[this.code],
|
|
849
|
-
}
|
|
850
|
-
: {
|
|
851
|
-
...this._body,
|
|
852
|
-
statusCode: this.code,
|
|
853
|
-
message: this.message,
|
|
854
|
-
error: httpStatusCodes[this.code],
|
|
855
|
-
};
|
|
856
|
-
}
|
|
857
|
-
attachRenderer(renderer) {
|
|
858
|
-
this.renderer = renderer;
|
|
859
|
-
}
|
|
860
|
-
getRenderer() {
|
|
861
|
-
return this.renderer;
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
|
|
865
849
|
function createWooksResponder(renderer = new BaseHttpResponseRenderer(), errorRenderer = new HttpErrorRenderer()) {
|
|
866
850
|
function createResponse(data) {
|
|
867
851
|
const { hasResponded } = useResponse();
|
|
868
|
-
if (hasResponded())
|
|
852
|
+
if (hasResponded()) {
|
|
869
853
|
return null;
|
|
854
|
+
}
|
|
870
855
|
if (data instanceof Error) {
|
|
871
856
|
const r = new BaseHttpResponse(errorRenderer);
|
|
872
|
-
|
|
873
|
-
if (data instanceof HttpError) {
|
|
874
|
-
httpError = data;
|
|
875
|
-
}
|
|
876
|
-
else {
|
|
877
|
-
httpError = new HttpError(500, data.message);
|
|
878
|
-
}
|
|
857
|
+
const httpError = data instanceof HttpError ? data : new HttpError(500, data.message);
|
|
879
858
|
r.setBody(httpError.body);
|
|
880
859
|
return r;
|
|
881
860
|
}
|
|
@@ -888,7 +867,7 @@ function createWooksResponder(renderer = new BaseHttpResponseRenderer(), errorRe
|
|
|
888
867
|
}
|
|
889
868
|
return {
|
|
890
869
|
createResponse,
|
|
891
|
-
respond: (data) => createResponse(data)?.respond(),
|
|
870
|
+
respond: async (data) => createResponse(data)?.respond(),
|
|
892
871
|
};
|
|
893
872
|
}
|
|
894
873
|
|
|
@@ -931,12 +910,14 @@ class WooksHttp extends wooks.WooksAdapterBase {
|
|
|
931
910
|
server.listen(...args);
|
|
932
911
|
});
|
|
933
912
|
}
|
|
934
|
-
close(server) {
|
|
913
|
+
async close(server) {
|
|
935
914
|
const srv = server || this.server;
|
|
936
915
|
return new Promise((resolve, reject) => {
|
|
937
|
-
srv?.close(
|
|
938
|
-
if (err)
|
|
939
|
-
|
|
916
|
+
srv?.close(err => {
|
|
917
|
+
if (err) {
|
|
918
|
+
reject(err);
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
940
921
|
resolve(srv);
|
|
941
922
|
});
|
|
942
923
|
});
|
|
@@ -948,8 +929,8 @@ class WooksHttp extends wooks.WooksAdapterBase {
|
|
|
948
929
|
this.server = server;
|
|
949
930
|
}
|
|
950
931
|
respond(data) {
|
|
951
|
-
void this.responder.respond(data)
|
|
952
|
-
this.logger.error('Uncought response exception',
|
|
932
|
+
void this.responder.respond(data).catch(error => {
|
|
933
|
+
this.logger.error('Uncought response exception', error);
|
|
953
934
|
});
|
|
954
935
|
}
|
|
955
936
|
getServerCb() {
|
|
@@ -960,10 +941,10 @@ class WooksHttp extends wooks.WooksAdapterBase {
|
|
|
960
941
|
try {
|
|
961
942
|
await this.processHandlers(handlers || [this.opts?.onNotFound]);
|
|
962
943
|
}
|
|
963
|
-
catch (
|
|
964
|
-
this.logger.error('Internal error, please report',
|
|
944
|
+
catch (error) {
|
|
945
|
+
this.logger.error('Internal error, please report', error);
|
|
965
946
|
restoreCtx();
|
|
966
|
-
this.respond(
|
|
947
|
+
this.respond(error);
|
|
967
948
|
clearCtx();
|
|
968
949
|
}
|
|
969
950
|
}
|
|
@@ -988,11 +969,11 @@ class WooksHttp extends wooks.WooksAdapterBase {
|
|
|
988
969
|
clearCtx();
|
|
989
970
|
break;
|
|
990
971
|
}
|
|
991
|
-
catch (
|
|
992
|
-
this.logger.error(`Uncought route handler exception: ${store('event').get('req').url || ''}`,
|
|
972
|
+
catch (error) {
|
|
973
|
+
this.logger.error(`Uncought route handler exception: ${store('event').get('req').url || ''}`, error);
|
|
993
974
|
if (isLastHandler) {
|
|
994
975
|
restoreCtx();
|
|
995
|
-
this.respond(
|
|
976
|
+
this.respond(error);
|
|
996
977
|
clearCtx();
|
|
997
978
|
}
|
|
998
979
|
}
|