@mojojs/core 1.27.0 → 2.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 +1 -1
- package/lib/app.d.ts +24 -16
- package/lib/app.js +172 -126
- package/lib/body/params.js +0 -1
- package/lib/body.d.ts +13 -5
- package/lib/body.js +47 -17
- package/lib/cache.d.ts +1 -2
- package/lib/cache.js +12 -21
- package/lib/cgi.d.ts +3 -3
- package/lib/cgi.js +6 -5
- package/lib/cli/cgi.d.ts +1 -1
- package/lib/cli/cgi.js +1 -2
- package/lib/cli/create-full-app.d.ts +1 -1
- package/lib/cli/create-full-app.js +27 -27
- package/lib/cli/create-lite-app.d.ts +1 -1
- package/lib/cli/create-lite-app.js +2 -3
- package/lib/cli/create-plugin.d.ts +1 -1
- package/lib/cli/create-plugin.js +25 -23
- package/lib/cli/eval.d.ts +1 -1
- package/lib/cli/eval.js +12 -5
- package/lib/cli/get.d.ts +1 -1
- package/lib/cli/get.js +30 -26
- package/lib/cli/repl.d.ts +1 -1
- package/lib/cli/repl.js +0 -1
- package/lib/cli/routes.d.ts +1 -1
- package/lib/cli/routes.js +10 -7
- package/lib/cli/server.d.ts +1 -1
- package/lib/cli/server.js +41 -24
- package/lib/cli/version.js +1 -2
- package/lib/cli.d.ts +1 -1
- package/lib/cli.js +25 -19
- package/lib/context.d.ts +10 -10
- package/lib/context.js +94 -34
- package/lib/core.d.ts +14 -14
- package/lib/core.js +12 -13
- package/lib/headers.js +5 -4
- package/lib/hooks.d.ts +1 -1
- package/lib/hooks.js +1 -5
- package/lib/logger.d.ts +2 -2
- package/lib/logger.js +28 -16
- package/lib/mime.js +10 -13
- package/lib/plugins/default-conditions.d.ts +1 -1
- package/lib/plugins/default-conditions.js +0 -1
- package/lib/plugins/default-helpers.d.ts +1 -1
- package/lib/plugins/default-helpers.js +28 -11
- package/lib/plugins/json-config.d.ts +1 -1
- package/lib/plugins/json-config.js +0 -1
- package/lib/plugins/mount.d.ts +1 -1
- package/lib/plugins/mount.js +0 -1
- package/lib/plugins/tmpl-engine.d.ts +1 -1
- package/lib/plugins/tmpl-engine.js +2 -5
- package/lib/plugins/yaml-config.d.ts +1 -1
- package/lib/plugins/yaml-config.js +1 -2
- package/lib/renderer.d.ts +1 -1
- package/lib/renderer.js +27 -30
- package/lib/router/pattern.d.ts +1 -1
- package/lib/router/pattern.js +25 -14
- package/lib/router/plan.d.ts +1 -1
- package/lib/router/plan.js +12 -15
- package/lib/router/route.d.ts +3 -3
- package/lib/router/route.js +35 -38
- package/lib/router.d.ts +4 -4
- package/lib/router.js +25 -26
- package/lib/server/cookie.d.ts +1 -1
- package/lib/server/cookie.js +1 -2
- package/lib/server/request.d.ts +3 -3
- package/lib/server/request.js +44 -17
- package/lib/server/response.d.ts +3 -3
- package/lib/server/response.js +20 -20
- package/lib/server.d.ts +7 -2
- package/lib/server.js +69 -19
- package/lib/session.d.ts +3 -3
- package/lib/session.js +46 -34
- package/lib/sse.d.ts +1 -1
- package/lib/sse.js +0 -1
- package/lib/static.d.ts +1 -1
- package/lib/static.js +27 -22
- package/lib/types.d.ts +6 -5
- package/lib/types.js +0 -1
- package/lib/user-agent/cookie-jar.d.ts +2 -2
- package/lib/user-agent/cookie-jar.js +6 -9
- package/lib/user-agent/mock.d.ts +4 -4
- package/lib/user-agent/mock.js +6 -7
- package/lib/user-agent/response.d.ts +2 -2
- package/lib/user-agent/response.js +14 -3
- package/lib/user-agent/test.d.ts +5 -5
- package/lib/user-agent/test.js +14 -15
- package/lib/user-agent/transport/http.d.ts +2 -2
- package/lib/user-agent/transport/http.js +7 -10
- package/lib/user-agent/transport/https.d.ts +2 -2
- package/lib/user-agent/transport/https.js +2 -6
- package/lib/user-agent/transport/ws.d.ts +2 -2
- package/lib/user-agent/transport/ws.js +2 -3
- package/lib/user-agent.d.ts +18 -19
- package/lib/user-agent.js +64 -49
- package/lib/util.d.ts +13 -0
- package/lib/util.js +35 -11
- package/lib/validator/result.d.ts +1 -1
- package/lib/validator/result.js +8 -1
- package/lib/validator.d.ts +2 -2
- package/lib/validator.js +3 -6
- package/lib/websocket.d.ts +2 -2
- package/lib/websocket.js +9 -1
- package/package.json +22 -29
- package/lib/app.js.map +0 -1
- package/lib/body/params.js.map +0 -1
- package/lib/body.js.map +0 -1
- package/lib/cache.js.map +0 -1
- package/lib/cgi.js.map +0 -1
- package/lib/cli/cgi.js.map +0 -1
- package/lib/cli/create-full-app.js.map +0 -1
- package/lib/cli/create-lite-app.js.map +0 -1
- package/lib/cli/create-plugin.js.map +0 -1
- package/lib/cli/eval.js.map +0 -1
- package/lib/cli/get.js.map +0 -1
- package/lib/cli/repl.js.map +0 -1
- package/lib/cli/routes.js.map +0 -1
- package/lib/cli/server.js.map +0 -1
- package/lib/cli/version.js.map +0 -1
- package/lib/cli.js.map +0 -1
- package/lib/context.js.map +0 -1
- package/lib/core.js.map +0 -1
- package/lib/headers.js.map +0 -1
- package/lib/hooks.js.map +0 -1
- package/lib/logger.js.map +0 -1
- package/lib/mime.js.map +0 -1
- package/lib/plugins/default-conditions.js.map +0 -1
- package/lib/plugins/default-helpers.js.map +0 -1
- package/lib/plugins/json-config.js.map +0 -1
- package/lib/plugins/mount.js.map +0 -1
- package/lib/plugins/tmpl-engine.js.map +0 -1
- package/lib/plugins/yaml-config.js.map +0 -1
- package/lib/renderer.js.map +0 -1
- package/lib/router/pattern.js.map +0 -1
- package/lib/router/plan.js.map +0 -1
- package/lib/router/route.js.map +0 -1
- package/lib/router.js.map +0 -1
- package/lib/server/cookie.js.map +0 -1
- package/lib/server/request.js.map +0 -1
- package/lib/server/response.js.map +0 -1
- package/lib/server.js.map +0 -1
- package/lib/session.js.map +0 -1
- package/lib/sse.js.map +0 -1
- package/lib/static.js.map +0 -1
- package/lib/types.js.map +0 -1
- package/lib/user-agent/cookie-jar.js.map +0 -1
- package/lib/user-agent/mock.js.map +0 -1
- package/lib/user-agent/response.js.map +0 -1
- package/lib/user-agent/test.js.map +0 -1
- package/lib/user-agent/transport/http.js.map +0 -1
- package/lib/user-agent/transport/https.js.map +0 -1
- package/lib/user-agent/transport/ws.js.map +0 -1
- package/lib/user-agent.js.map +0 -1
- package/lib/util.js.map +0 -1
- package/lib/validator/result.js.map +0 -1
- package/lib/validator.js.map +0 -1
- package/lib/websocket.js.map +0 -1
package/lib/server.js
CHANGED
|
@@ -2,11 +2,11 @@ import cluster from 'node:cluster';
|
|
|
2
2
|
import http from 'node:http';
|
|
3
3
|
import https from 'node:https';
|
|
4
4
|
import os from 'node:os';
|
|
5
|
-
import { Stream } from 'node:stream';
|
|
5
|
+
import { pipeline, Stream } from 'node:stream';
|
|
6
6
|
import { URL } from 'node:url';
|
|
7
|
-
import { ServerRequest } from
|
|
8
|
-
import { ServerResponse } from
|
|
9
|
-
import { WebSocket } from
|
|
7
|
+
import { ServerRequest } from "./server/request.js";
|
|
8
|
+
import { ServerResponse } from "./server/response.js";
|
|
9
|
+
import { WebSocket } from "./websocket.js";
|
|
10
10
|
import Path from '@mojojs/path';
|
|
11
11
|
import { termEscape } from '@mojojs/util';
|
|
12
12
|
import { WebSocketServer } from 'ws';
|
|
@@ -22,16 +22,52 @@ const devKey = certs.child('development.key').toString();
|
|
|
22
22
|
* HTTP and WebSocket server class.
|
|
23
23
|
*/
|
|
24
24
|
export class Server {
|
|
25
|
+
/**
|
|
26
|
+
* Application this server handles.
|
|
27
|
+
*/
|
|
28
|
+
app;
|
|
29
|
+
/**
|
|
30
|
+
* Limit the amount of time the parser will wait to receive the complete HTTP headers, defaults to `60000`
|
|
31
|
+
* (60 seconds).
|
|
32
|
+
*/
|
|
33
|
+
headersTimeout;
|
|
34
|
+
/**
|
|
35
|
+
* Limit the amount of time of inactivity a server needs to wait for additional incoming data, after it has finished
|
|
36
|
+
* writing the last response, before a socket will be destroyed, defaults to `5000` (5 seconds).
|
|
37
|
+
*/
|
|
38
|
+
keepAliveTimeout;
|
|
39
|
+
/**
|
|
40
|
+
* Maximum number of requests socket can handle before closing keep alive connection, defaults to `0`.
|
|
41
|
+
*/
|
|
42
|
+
maxRequestsPerSocket;
|
|
43
|
+
/**
|
|
44
|
+
* Maximum WebSocket message size in bytes, defaults to `262144` (256 KiB). Connections exceeding the limit are closed
|
|
45
|
+
* with status `1009`.
|
|
46
|
+
*/
|
|
47
|
+
maxWebSocketSize;
|
|
48
|
+
/**
|
|
49
|
+
* Limit the amount of time for receiving the entire request from the client, defaults to `300000` (300 seconds).
|
|
50
|
+
*/
|
|
51
|
+
requestTimeout;
|
|
52
|
+
/**
|
|
53
|
+
* Reverse proxy mode.
|
|
54
|
+
*/
|
|
55
|
+
reverseProxy;
|
|
56
|
+
/**
|
|
57
|
+
* Server URLs.
|
|
58
|
+
*/
|
|
59
|
+
urls = [];
|
|
60
|
+
_cluster;
|
|
61
|
+
_listen;
|
|
62
|
+
_servers = [];
|
|
63
|
+
_quiet;
|
|
64
|
+
_workers;
|
|
25
65
|
constructor(app, options = {}) {
|
|
26
|
-
/**
|
|
27
|
-
* Server URLs.
|
|
28
|
-
*/
|
|
29
|
-
this.urls = [];
|
|
30
|
-
this._servers = [];
|
|
31
66
|
this.app = app;
|
|
32
67
|
this.headersTimeout = options.headersTimeout;
|
|
33
68
|
this.keepAliveTimeout = options.keepAliveTimeout;
|
|
34
69
|
this.maxRequestsPerSocket = options.maxRequestsPerSocket;
|
|
70
|
+
this.maxWebSocketSize = options.maxWebSocketSize ?? 256 * 1024;
|
|
35
71
|
this.requestTimeout = options.requestTimeout;
|
|
36
72
|
this.reverseProxy = options.reverseProxy ?? false;
|
|
37
73
|
this._cluster = options.cluster ?? false;
|
|
@@ -72,6 +108,7 @@ export class Server {
|
|
|
72
108
|
cluster.fork();
|
|
73
109
|
}
|
|
74
110
|
else {
|
|
111
|
+
await app.warmup();
|
|
75
112
|
for (const location of this._listen) {
|
|
76
113
|
await this._createServer(location);
|
|
77
114
|
}
|
|
@@ -100,8 +137,7 @@ export class Server {
|
|
|
100
137
|
options.key = await new Path(searchParams.get('key') ?? devKey).readFile();
|
|
101
138
|
isHttps = true;
|
|
102
139
|
}
|
|
103
|
-
|
|
104
|
-
const wss = new WebSocketServer({ noServer: true });
|
|
140
|
+
const wss = new WebSocketServer({ maxPayload: this.maxWebSocketSize, noServer: true });
|
|
105
141
|
const server = (isHttps ? https : http).createServer(options, this._handleRequest.bind(this));
|
|
106
142
|
this._servers.push(server);
|
|
107
143
|
if (this.maxRequestsPerSocket !== undefined)
|
|
@@ -118,9 +154,9 @@ export class Server {
|
|
|
118
154
|
const stderr = process.stderr;
|
|
119
155
|
socket.on('data', (chunk) => stderr.write(termEscape(`-- Server <<< Client\n${chunk}`)));
|
|
120
156
|
const write = socket.write;
|
|
121
|
-
socket.write = (chunk,
|
|
157
|
+
socket.write = (chunk, ...args) => {
|
|
122
158
|
stderr.write(termEscape(`-- Server >>> Client\n${chunk}`));
|
|
123
|
-
return write.apply(socket, [chunk,
|
|
159
|
+
return write.apply(socket, [chunk, ...args]);
|
|
124
160
|
};
|
|
125
161
|
});
|
|
126
162
|
}
|
|
@@ -202,11 +238,18 @@ function sendHeaders(res, raw) {
|
|
|
202
238
|
const statusCode = res.statusCode;
|
|
203
239
|
const statusMessage = res.statusMessage;
|
|
204
240
|
const headers = res.headers.toArray();
|
|
205
|
-
|
|
206
|
-
|
|
241
|
+
try {
|
|
242
|
+
if (statusMessage === null) {
|
|
243
|
+
raw.writeHead(statusCode, headers);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
raw.writeHead(statusCode, statusMessage, headers);
|
|
247
|
+
}
|
|
207
248
|
}
|
|
208
|
-
|
|
209
|
-
|
|
249
|
+
catch (error) {
|
|
250
|
+
// The response counts as sent already, so close the connection instead of letting the client wait forever
|
|
251
|
+
raw.destroy();
|
|
252
|
+
throw error;
|
|
210
253
|
}
|
|
211
254
|
}
|
|
212
255
|
function sendResponse(res, body, raw) {
|
|
@@ -217,7 +260,15 @@ function sendResponse(res, body, raw) {
|
|
|
217
260
|
}
|
|
218
261
|
else if (body instanceof Stream) {
|
|
219
262
|
sendHeaders(res, raw);
|
|
220
|
-
|
|
263
|
+
// Both streams need to be cleaned up, and only aborted connections are expected
|
|
264
|
+
pipeline(body, raw, error => {
|
|
265
|
+
if (error === undefined ||
|
|
266
|
+
error === null ||
|
|
267
|
+
error.code === 'ERR_STREAM_PREMATURE_CLOSE') {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
res._ctx?.deref()?.log.error(`Response stream failed: ${error.stack}`);
|
|
271
|
+
});
|
|
221
272
|
}
|
|
222
273
|
else {
|
|
223
274
|
sendHeaders(res, raw);
|
|
@@ -227,4 +278,3 @@ function sendResponse(res, body, raw) {
|
|
|
227
278
|
function urlToSocketPath(url) {
|
|
228
279
|
return url.host + url.pathname;
|
|
229
280
|
}
|
|
230
|
-
//# sourceMappingURL=server.js.map
|
package/lib/session.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { App } from './app.
|
|
2
|
-
import type { Context } from './context.
|
|
3
|
-
import type { SessionData } from './types.
|
|
1
|
+
import type { App } from './app.ts';
|
|
2
|
+
import type { Context } from './context.ts';
|
|
3
|
+
import type { SessionData } from './types.ts';
|
|
4
4
|
/**
|
|
5
5
|
* Session manager class.
|
|
6
6
|
*/
|
package/lib/session.js
CHANGED
|
@@ -2,42 +2,55 @@ import crypto from 'node:crypto';
|
|
|
2
2
|
import { promisify } from 'node:util';
|
|
3
3
|
const scrypt = promisify(crypto.scrypt);
|
|
4
4
|
const randomBytes = promisify(crypto.randomBytes);
|
|
5
|
+
// Deriving keys with scrypt is slow on purpose, so it is only done once per secret
|
|
6
|
+
const keys = new Map();
|
|
7
|
+
function deriveKey(secret) {
|
|
8
|
+
let key = keys.get(secret);
|
|
9
|
+
if (key === undefined) {
|
|
10
|
+
key = scrypt(secret, 'salt', 32);
|
|
11
|
+
keys.set(secret, key);
|
|
12
|
+
// Do not cache failures
|
|
13
|
+
key.catch(() => keys.delete(secret));
|
|
14
|
+
}
|
|
15
|
+
return key;
|
|
16
|
+
}
|
|
5
17
|
/**
|
|
6
18
|
* Session manager class.
|
|
7
19
|
*/
|
|
8
20
|
export class Session {
|
|
21
|
+
/**
|
|
22
|
+
* Name for session cookies, defaults to `mojo`.
|
|
23
|
+
*/
|
|
24
|
+
cookieName = 'mojo';
|
|
25
|
+
/**
|
|
26
|
+
* Path for session cookies, defaults to `/`.
|
|
27
|
+
*/
|
|
28
|
+
cookiePath = '/';
|
|
29
|
+
/**
|
|
30
|
+
* Default time for sessions to expire in seconds from now, defaults to `3600`. The expiration timeout gets refreshed
|
|
31
|
+
* for every request. Setting the value to `0` will allow sessions to persist until the browser window is closed,
|
|
32
|
+
* this can have security implications though.
|
|
33
|
+
*/
|
|
34
|
+
expiration = 3600;
|
|
35
|
+
/**
|
|
36
|
+
* Set the `HttpOnly` value on all session cookies.
|
|
37
|
+
*/
|
|
38
|
+
httpOnly = true;
|
|
39
|
+
/**
|
|
40
|
+
* Set the `Partitioned` value on all session cookies, to be used in accordance with the CHIPS amendment to RFC 6265.
|
|
41
|
+
* Note that this property is EXPERIMENTAL and might change without warning!
|
|
42
|
+
*/
|
|
43
|
+
partitioned = false;
|
|
44
|
+
/**
|
|
45
|
+
* Set the `SameSite` value on all session cookies, defaults to `lax`.
|
|
46
|
+
*/
|
|
47
|
+
sameSite = 'lax';
|
|
48
|
+
/**
|
|
49
|
+
* Set the secure flag on all session cookies, so that browsers send them only over HTTPS connections.
|
|
50
|
+
*/
|
|
51
|
+
secure = false;
|
|
52
|
+
_app;
|
|
9
53
|
constructor(app) {
|
|
10
|
-
/**
|
|
11
|
-
* Name for session cookies, defaults to `mojo`.
|
|
12
|
-
*/
|
|
13
|
-
this.cookieName = 'mojo';
|
|
14
|
-
/**
|
|
15
|
-
* Path for session cookies, defaults to `/`.
|
|
16
|
-
*/
|
|
17
|
-
this.cookiePath = '/';
|
|
18
|
-
/**
|
|
19
|
-
* Default time for sessions to expire in seconds from now, defaults to `3600`. The expiration timeout gets refreshed
|
|
20
|
-
* for every request. Setting the value to `0` will allow sessions to persist until the browser window is closed,
|
|
21
|
-
* this can have security implications though.
|
|
22
|
-
*/
|
|
23
|
-
this.expiration = 3600;
|
|
24
|
-
/**
|
|
25
|
-
* Set the `HttpOnly` value on all session cookies.
|
|
26
|
-
*/
|
|
27
|
-
this.httpOnly = true;
|
|
28
|
-
/**
|
|
29
|
-
* Set the `Partitioned` value on all session cookies, to be used in accordance with the CHIPS amendment to RFC 6265.
|
|
30
|
-
* Note that this property is EXPERIMENTAL and might change without warning!
|
|
31
|
-
*/
|
|
32
|
-
this.partitioned = false;
|
|
33
|
-
/**
|
|
34
|
-
* Set the `SameSite` value on all session cookies, defaults to `lax`.
|
|
35
|
-
*/
|
|
36
|
-
this.sameSite = 'lax';
|
|
37
|
-
/**
|
|
38
|
-
* Set the secure flag on all session cookies, so that browsers send them only over HTTPS connections.
|
|
39
|
-
*/
|
|
40
|
-
this.secure = false;
|
|
41
54
|
this._app = new WeakRef(app);
|
|
42
55
|
}
|
|
43
56
|
/**
|
|
@@ -52,7 +65,7 @@ export class Session {
|
|
|
52
65
|
const authTag = Buffer.from(match[3], 'base64');
|
|
53
66
|
for (const secret of secrets) {
|
|
54
67
|
try {
|
|
55
|
-
const key = await
|
|
68
|
+
const key = await deriveKey(secret);
|
|
56
69
|
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv, { authTagLength: 16 });
|
|
57
70
|
decipher.setAuthTag(authTag);
|
|
58
71
|
const decrypted = decipher.update(value, 'base64', 'utf8');
|
|
@@ -68,7 +81,7 @@ export class Session {
|
|
|
68
81
|
* Encrypt cookie.
|
|
69
82
|
*/
|
|
70
83
|
static async encrypt(secret, value) {
|
|
71
|
-
const key = await
|
|
84
|
+
const key = await deriveKey(secret);
|
|
72
85
|
const iv = await randomBytes(12);
|
|
73
86
|
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
|
|
74
87
|
const encrypted = cipher.update(value, 'utf8', 'base64') + cipher.final('base64');
|
|
@@ -123,4 +136,3 @@ export class Session {
|
|
|
123
136
|
});
|
|
124
137
|
}
|
|
125
138
|
}
|
|
126
|
-
//# sourceMappingURL=session.js.map
|
package/lib/sse.d.ts
CHANGED
package/lib/sse.js
CHANGED
package/lib/static.d.ts
CHANGED
package/lib/static.js
CHANGED
|
@@ -1,23 +1,21 @@
|
|
|
1
|
-
import crypto from 'node:crypto';
|
|
2
1
|
import Path from '@mojojs/path';
|
|
3
2
|
/**
|
|
4
3
|
* Static file server class.
|
|
5
4
|
*/
|
|
6
5
|
export class Static {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
6
|
+
/**
|
|
7
|
+
* Subdirectory used for all static assets.
|
|
8
|
+
*/
|
|
9
|
+
assetDir = 'assets';
|
|
10
|
+
/**
|
|
11
|
+
* Prefix to use for all static files.
|
|
12
|
+
*/
|
|
13
|
+
prefix = '/static';
|
|
14
|
+
/**
|
|
15
|
+
* Directories to serve static files from, first one has the highest precedence.
|
|
16
|
+
*/
|
|
17
|
+
publicPaths = [Path.currentFile().sibling('..', 'vendor', 'public').toString()];
|
|
18
|
+
_assets;
|
|
21
19
|
/**
|
|
22
20
|
* Get static asset path.
|
|
23
21
|
*/
|
|
@@ -79,9 +77,10 @@ export class Static {
|
|
|
79
77
|
// If-None-Match
|
|
80
78
|
const ifNoneMatch = req.get('If-None-Match');
|
|
81
79
|
if (etag !== undefined && ifNoneMatch !== null) {
|
|
82
|
-
|
|
80
|
+
// Weak comparison, because proxies like nginx turn ETags into weak ones when compressing responses
|
|
81
|
+
const etags = ifNoneMatch.split(/\s*,\s*/).map(value => value.replace(/^W\//, '').replaceAll('"', ''));
|
|
83
82
|
for (const match of etags) {
|
|
84
|
-
if (match === etag)
|
|
83
|
+
if (match === etag || match === '*')
|
|
85
84
|
return true;
|
|
86
85
|
}
|
|
87
86
|
}
|
|
@@ -91,7 +90,8 @@ export class Static {
|
|
|
91
90
|
const epoch = Date.parse(ifModifiedSince);
|
|
92
91
|
if (isNaN(epoch))
|
|
93
92
|
return false;
|
|
94
|
-
|
|
93
|
+
// HTTP dates have no milliseconds
|
|
94
|
+
if (epoch >= Math.floor(options.lastModified.getTime() / 1000) * 1000)
|
|
95
95
|
return true;
|
|
96
96
|
}
|
|
97
97
|
return false;
|
|
@@ -113,7 +113,8 @@ export class Static {
|
|
|
113
113
|
const { res } = ctx;
|
|
114
114
|
res.set('Accept-Ranges', 'bytes');
|
|
115
115
|
const lastModified = stat.mtime;
|
|
116
|
-
|
|
116
|
+
// Size too, because reproducible builds often give all files the same modification time
|
|
117
|
+
const etag = `${lastModified.getTime()}-${length}`;
|
|
117
118
|
if (this.isFresh(ctx, { etag, lastModified })) {
|
|
118
119
|
await res.status(304).send();
|
|
119
120
|
return;
|
|
@@ -131,10 +132,15 @@ export class Static {
|
|
|
131
132
|
if (bytes === null)
|
|
132
133
|
return await this._rangeNotSatisfiable(ctx);
|
|
133
134
|
let start = parseInt(bytes[1]);
|
|
135
|
+
let end = parseInt(bytes[2]);
|
|
136
|
+
// Suffix range for the last bytes of the file
|
|
137
|
+
if (isNaN(start) && isNaN(end) === false) {
|
|
138
|
+
start = Math.max(length - end, 0);
|
|
139
|
+
end = length - 1;
|
|
140
|
+
}
|
|
134
141
|
if (isNaN(start))
|
|
135
142
|
start = 0;
|
|
136
|
-
|
|
137
|
-
if (isNaN(end))
|
|
143
|
+
if (isNaN(end) || end >= length)
|
|
138
144
|
end = length - 1;
|
|
139
145
|
if (start > end)
|
|
140
146
|
return await this._rangeNotSatisfiable(ctx);
|
|
@@ -175,4 +181,3 @@ export class Static {
|
|
|
175
181
|
await ctx.res.status(416).send();
|
|
176
182
|
}
|
|
177
183
|
}
|
|
178
|
-
//# sourceMappingURL=static.js.map
|
package/lib/types.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { App } from './app.
|
|
2
|
-
import type { Context } from './context.
|
|
3
|
-
import type { Route } from './router/route.
|
|
4
|
-
import type { SafeString } from './util.
|
|
5
|
-
import type { ValidatorResult } from './validator/result.
|
|
1
|
+
import type { App } from './app.ts';
|
|
2
|
+
import type { Context } from './context.ts';
|
|
3
|
+
import type { Route } from './router/route.ts';
|
|
4
|
+
import type { SafeString } from './util.ts';
|
|
5
|
+
import type { ValidatorResult } from './validator/result.ts';
|
|
6
6
|
import type { Agent } from 'node:http';
|
|
7
7
|
import type { Readable, Stream } from 'node:stream';
|
|
8
8
|
import type { URL } from 'node:url';
|
|
@@ -156,6 +156,7 @@ export interface ServerOptions {
|
|
|
156
156
|
keepAliveTimeout?: number;
|
|
157
157
|
listen?: string[];
|
|
158
158
|
maxRequestsPerSocket?: number;
|
|
159
|
+
maxWebSocketSize?: number;
|
|
159
160
|
quiet?: boolean;
|
|
160
161
|
requestTimeout?: number;
|
|
161
162
|
reverseProxy?: boolean;
|
package/lib/types.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type URL } from 'node:url';
|
|
2
|
-
import
|
|
2
|
+
import { CookieJar as ToughCookieJar } from 'tough-cookie';
|
|
3
3
|
/**
|
|
4
4
|
* Cookie jar class.
|
|
5
5
|
*/
|
|
@@ -7,7 +7,7 @@ export declare class CookieJar {
|
|
|
7
7
|
/**
|
|
8
8
|
* Cookie storage.
|
|
9
9
|
*/
|
|
10
|
-
storage:
|
|
10
|
+
storage: ToughCookieJar;
|
|
11
11
|
/**
|
|
12
12
|
* Load cookies from storage.
|
|
13
13
|
*/
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { format } from 'node:url';
|
|
2
|
-
import
|
|
2
|
+
import { Cookie, CookieJar as ToughCookieJar } from 'tough-cookie';
|
|
3
3
|
/**
|
|
4
4
|
* Cookie jar class.
|
|
5
5
|
*/
|
|
6
6
|
export class CookieJar {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
this.storage = new tough.CookieJar();
|
|
12
|
-
}
|
|
7
|
+
/**
|
|
8
|
+
* Cookie storage.
|
|
9
|
+
*/
|
|
10
|
+
storage = new ToughCookieJar();
|
|
13
11
|
/**
|
|
14
12
|
* Load cookies from storage.
|
|
15
13
|
*/
|
|
@@ -24,7 +22,7 @@ export class CookieJar {
|
|
|
24
22
|
*/
|
|
25
23
|
async storeCookies(url, headers) {
|
|
26
24
|
const cookieURL = this._cookieURL(url);
|
|
27
|
-
for (const cookie of headers.map(value =>
|
|
25
|
+
for (const cookie of headers.map(value => Cookie.parse(value))) {
|
|
28
26
|
if (cookie !== undefined)
|
|
29
27
|
await this.storage.setCookie(cookie, cookieURL);
|
|
30
28
|
}
|
|
@@ -33,4 +31,3 @@ export class CookieJar {
|
|
|
33
31
|
return format(currentURL, { auth: false, fragment: false, search: false });
|
|
34
32
|
}
|
|
35
33
|
}
|
|
36
|
-
//# sourceMappingURL=cookie-jar.js.map
|
package/lib/user-agent/mock.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { App } from '../app.
|
|
2
|
-
import type { ServerOptions, UserAgentOptions } from '../types.
|
|
3
|
-
import { Server } from '../server.
|
|
4
|
-
import { UserAgent } from '../user-agent.
|
|
1
|
+
import type { App } from '../app.ts';
|
|
2
|
+
import type { ServerOptions, UserAgentOptions } from '../types.ts';
|
|
3
|
+
import { Server } from '../server.ts';
|
|
4
|
+
import { UserAgent } from '../user-agent.ts';
|
|
5
5
|
/**
|
|
6
6
|
* Mock user-agent class.
|
|
7
7
|
*/
|
package/lib/user-agent/mock.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { Server } from
|
|
2
|
-
import { UserAgent } from
|
|
1
|
+
import { Server } from "../server.js";
|
|
2
|
+
import { UserAgent } from "../user-agent.js";
|
|
3
3
|
/**
|
|
4
4
|
* Mock user-agent class.
|
|
5
5
|
*/
|
|
6
6
|
export class MockUserAgent extends UserAgent {
|
|
7
|
+
/**
|
|
8
|
+
* Server to use for mock requests.
|
|
9
|
+
*/
|
|
10
|
+
server = undefined;
|
|
7
11
|
constructor(options) {
|
|
8
12
|
super(options);
|
|
9
|
-
/**
|
|
10
|
-
* Server to use for mock requests.
|
|
11
|
-
*/
|
|
12
|
-
this.server = undefined;
|
|
13
13
|
this.server = undefined;
|
|
14
14
|
}
|
|
15
15
|
async [Symbol.asyncDispose]() {
|
|
@@ -41,4 +41,3 @@ export class MockUserAgent extends UserAgent {
|
|
|
41
41
|
return await this.server.stop();
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
//# sourceMappingURL=mock.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { SSEEvent } from '../types.
|
|
1
|
+
import type { SSEEvent } from '../types.ts';
|
|
2
2
|
import type { Readable } from 'node:stream';
|
|
3
|
-
import { Body } from '../body.
|
|
3
|
+
import { Body } from '../body.ts';
|
|
4
4
|
interface UserAgentResponseOptions {
|
|
5
5
|
body: Readable;
|
|
6
6
|
headers: string[];
|
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
import { Body } from
|
|
2
|
-
import { parseEvent } from
|
|
1
|
+
import { Body } from "../body.js";
|
|
2
|
+
import { parseEvent } from "../sse.js";
|
|
3
3
|
/**
|
|
4
4
|
* User-agent response class.
|
|
5
5
|
*/
|
|
6
6
|
export class UserAgentResponse extends Body {
|
|
7
|
+
/**
|
|
8
|
+
* HTTP version.
|
|
9
|
+
*/
|
|
10
|
+
httpVersion;
|
|
11
|
+
/**
|
|
12
|
+
* Response status code.
|
|
13
|
+
*/
|
|
14
|
+
statusCode;
|
|
15
|
+
/**
|
|
16
|
+
* Response status message.
|
|
17
|
+
*/
|
|
18
|
+
statusMessage;
|
|
7
19
|
constructor(options) {
|
|
8
20
|
super(options.headers, options.body);
|
|
9
21
|
this.httpVersion = options.httpVersion;
|
|
@@ -73,4 +85,3 @@ export class UserAgentResponse extends Body {
|
|
|
73
85
|
return this.get('Content-Type');
|
|
74
86
|
}
|
|
75
87
|
}
|
|
76
|
-
//# sourceMappingURL=response.js.map
|
package/lib/user-agent/test.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { UserAgentResponse } from './response.
|
|
2
|
-
import type { App } from '../app.
|
|
3
|
-
import type { JSONValue, SSEEvent, ServerOptions, TestContext, TestUserAgentOptions, UserAgentRequestOptions, UserAgentWebSocketOptions } from '../types.
|
|
4
|
-
import type { WebSocket } from '../websocket.
|
|
1
|
+
import type { UserAgentResponse } from './response.ts';
|
|
2
|
+
import type { App } from '../app.ts';
|
|
3
|
+
import type { JSONValue, SSEEvent, ServerOptions, TestContext, TestUserAgentOptions, UserAgentRequestOptions, UserAgentWebSocketOptions } from '../types.ts';
|
|
4
|
+
import type { WebSocket } from '../websocket.ts';
|
|
5
5
|
import type { URL } from 'node:url';
|
|
6
6
|
import assert from 'node:assert/strict';
|
|
7
|
-
import { MockUserAgent } from './mock.
|
|
7
|
+
import { MockUserAgent } from './mock.ts';
|
|
8
8
|
import DOM from '@mojojs/dom';
|
|
9
9
|
import StackUtils from 'stack-utils';
|
|
10
10
|
type SkipFunction = (...args: any[]) => any;
|
package/lib/user-agent/test.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import { on } from 'node:events';
|
|
3
|
-
import { MockUserAgent } from
|
|
3
|
+
import { MockUserAgent } from "./mock.js";
|
|
4
4
|
import DOM from '@mojojs/dom';
|
|
5
5
|
import { jsonPointer } from '@mojojs/util';
|
|
6
6
|
import * as yaml from 'js-yaml';
|
|
@@ -18,21 +18,21 @@ function addNodeAssertMethods(tapInstance) {
|
|
|
18
18
|
* Test user-agent class.
|
|
19
19
|
*/
|
|
20
20
|
export class TestUserAgent extends MockUserAgent {
|
|
21
|
+
/**
|
|
22
|
+
* Current HTTP response content.
|
|
23
|
+
*/
|
|
24
|
+
body = Buffer.from('');
|
|
25
|
+
_assert = undefined;
|
|
26
|
+
_dom = undefined;
|
|
27
|
+
_finished = undefined;
|
|
28
|
+
_messages = undefined;
|
|
29
|
+
_res = undefined;
|
|
30
|
+
_sse = undefined;
|
|
31
|
+
_sseEvents = undefined;
|
|
32
|
+
_stack = new StackUtils();
|
|
33
|
+
_ws = undefined;
|
|
21
34
|
constructor(options = {}) {
|
|
22
35
|
super(options);
|
|
23
|
-
/**
|
|
24
|
-
* Current HTTP response content.
|
|
25
|
-
*/
|
|
26
|
-
this.body = Buffer.from('');
|
|
27
|
-
this._assert = undefined;
|
|
28
|
-
this._dom = undefined;
|
|
29
|
-
this._finished = undefined;
|
|
30
|
-
this._messages = undefined;
|
|
31
|
-
this._res = undefined;
|
|
32
|
-
this._sse = undefined;
|
|
33
|
-
this._sseEvents = undefined;
|
|
34
|
-
this._stack = new StackUtils();
|
|
35
|
-
this._ws = undefined;
|
|
36
36
|
if (options.tap !== undefined)
|
|
37
37
|
this._prepareTap(options.tap);
|
|
38
38
|
}
|
|
@@ -472,4 +472,3 @@ export class TestUserAgent extends MockUserAgent {
|
|
|
472
472
|
});
|
|
473
473
|
}
|
|
474
474
|
}
|
|
475
|
-
//# sourceMappingURL=test.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { UserAgentRequestOptions } from '../../types.
|
|
1
|
+
import type { UserAgentRequestOptions } from '../../types.ts';
|
|
2
2
|
import type { URL } from 'node:url';
|
|
3
3
|
import http from 'node:http';
|
|
4
|
-
import { UserAgentResponse } from '../response.
|
|
4
|
+
import { UserAgentResponse } from '../response.ts';
|
|
5
5
|
/**
|
|
6
6
|
* HTTP transport class.
|
|
7
7
|
*/
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import http from 'node:http';
|
|
2
2
|
import Stream from 'node:stream';
|
|
3
|
-
import { UserAgentResponse } from
|
|
3
|
+
import { UserAgentResponse } from "../response.js";
|
|
4
4
|
import { termEscape } from '@mojojs/util';
|
|
5
5
|
/**
|
|
6
6
|
* HTTP transport class.
|
|
7
7
|
*/
|
|
8
8
|
export class HTTPTransport {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
this.agent = new http.Agent();
|
|
14
|
-
}
|
|
9
|
+
/**
|
|
10
|
+
* HTTP agent.
|
|
11
|
+
*/
|
|
12
|
+
agent = new http.Agent();
|
|
15
13
|
/**
|
|
16
14
|
* Destroy active keep-alive connections.
|
|
17
15
|
*/
|
|
@@ -40,9 +38,9 @@ export class HTTPTransport {
|
|
|
40
38
|
const stderr = process.stderr;
|
|
41
39
|
socket.on('data', (chunk) => stderr.write(termEscape(`-- Client <<< Server\n${chunk}`)));
|
|
42
40
|
const write = socket.write;
|
|
43
|
-
socket.write = (chunk,
|
|
41
|
+
socket.write = (chunk, ...args) => {
|
|
44
42
|
stderr.write(termEscape(`-- Client >>> Server\n${chunk}`));
|
|
45
|
-
return write.apply(socket, [chunk,
|
|
43
|
+
return write.apply(socket, [chunk, ...args]);
|
|
46
44
|
};
|
|
47
45
|
});
|
|
48
46
|
}
|
|
@@ -75,4 +73,3 @@ export class HTTPTransport {
|
|
|
75
73
|
return http.request(url, options, cb);
|
|
76
74
|
}
|
|
77
75
|
}
|
|
78
|
-
//# sourceMappingURL=http.js.map
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { UserAgentRequestOptions } from '../../types.
|
|
1
|
+
import type { UserAgentRequestOptions } from '../../types.ts';
|
|
2
2
|
import type http from 'node:http';
|
|
3
3
|
import type { URL } from 'node:url';
|
|
4
4
|
import https from 'node:https';
|
|
5
|
-
import { HTTPTransport } from './http.
|
|
5
|
+
import { HTTPTransport } from './http.ts';
|
|
6
6
|
/**
|
|
7
7
|
* HTTPS transport class.
|
|
8
8
|
*/
|