@hono/node-server 2.0.0-rc.1 → 2.0.0-rc.2
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 +55 -11
- package/dist/{listener-Brd4yZ5d.js → index.cjs} +388 -108
- package/dist/{index.d.ts → index.d.cts} +12 -1
- package/dist/index.d.mts +12 -1
- package/dist/index.mjs +981 -4
- package/dist/{serve-static.js → serve-static.cjs} +1 -1
- package/dist/serve-static.mjs +1 -1
- package/dist/utils/{response.js → response.cjs} +1 -1
- package/dist/utils/response.mjs +1 -1
- package/package.json +44 -29
- package/dist/index.js +0 -28
- package/dist/listener-RIBxK9_x.mjs +0 -715
- package/dist/vercel.d.mts +0 -8
- package/dist/vercel.d.ts +0 -8
- package/dist/vercel.js +0 -10
- package/dist/vercel.mjs +0 -9
- /package/dist/{conninfo.js → conninfo.cjs} +0 -0
- /package/dist/{conninfo.d.ts → conninfo.d.cts} +0 -0
- /package/dist/{constants-DEKKqoym.mjs → constants-BLSFu_RU.mjs} +0 -0
- /package/dist/{constants-B7DBcQew.js → constants-BXAKTxRC.cjs} +0 -0
- /package/dist/{serve-static.d.ts → serve-static.d.cts} +0 -0
- /package/dist/utils/{response.d.ts → response.d.cts} +0 -0
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_constants = require('./constants-BXAKTxRC.cjs');
|
|
3
|
+
let node_http = require("node:http");
|
|
2
4
|
let node_http2 = require("node:http2");
|
|
3
5
|
let node_stream = require("node:stream");
|
|
6
|
+
let hono_ws = require("hono/ws");
|
|
4
7
|
|
|
5
8
|
//#region src/error.ts
|
|
6
9
|
var RequestError = class extends Error {
|
|
@@ -12,69 +15,19 @@ var RequestError = class extends Error {
|
|
|
12
15
|
|
|
13
16
|
//#endregion
|
|
14
17
|
//#region src/url.ts
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
const nextIndex = dotIndex + 1;
|
|
19
|
-
if (nextIndex === url.length) return true;
|
|
20
|
-
const next = url.charCodeAt(nextIndex);
|
|
21
|
-
if (isPathDelimiter(next)) return true;
|
|
22
|
-
if (next !== 46) return false;
|
|
23
|
-
const nextNextIndex = dotIndex + 2;
|
|
24
|
-
if (nextNextIndex === url.length) return true;
|
|
25
|
-
return isPathDelimiter(url.charCodeAt(nextNextIndex));
|
|
26
|
-
};
|
|
27
|
-
const allowedRequestUrlChar = new Uint8Array(128);
|
|
28
|
-
for (let c = 48; c <= 57; c++) allowedRequestUrlChar[c] = 1;
|
|
29
|
-
for (let c = 65; c <= 90; c++) allowedRequestUrlChar[c] = 1;
|
|
30
|
-
for (let c = 97; c <= 122; c++) allowedRequestUrlChar[c] = 1;
|
|
31
|
-
(() => {
|
|
32
|
-
const chars = "-./:?#[]@!$&'()*+,;=~_";
|
|
33
|
-
for (let i = 0; i < 22; i++) allowedRequestUrlChar[chars.charCodeAt(i)] = 1;
|
|
34
|
-
})();
|
|
35
|
-
const safeHostChar = new Uint8Array(128);
|
|
36
|
-
for (let c = 48; c <= 57; c++) safeHostChar[c] = 1;
|
|
37
|
-
for (let c = 97; c <= 122; c++) safeHostChar[c] = 1;
|
|
38
|
-
(() => {
|
|
39
|
-
const chars = ".-_:";
|
|
40
|
-
for (let i = 0; i < 4; i++) safeHostChar[chars.charCodeAt(i)] = 1;
|
|
41
|
-
})();
|
|
18
|
+
const reValidRequestUrl = /^\/[!#$&-;=?-\[\]_a-z~]*$/;
|
|
19
|
+
const reDotSegment = /\/\.\.?(?:[/?#]|$)/;
|
|
20
|
+
const reValidHost = /^[a-z0-9._-]+(?::(?:[1-5]\d{3,4}|[6-9]\d{3}))?$/;
|
|
42
21
|
const buildUrl = (scheme, host, incomingUrl) => {
|
|
43
22
|
const url = `${scheme}://${host}${incomingUrl}`;
|
|
44
|
-
|
|
45
|
-
for (let i = 0, len = host.length; i < len; i++) {
|
|
46
|
-
const c = host.charCodeAt(i);
|
|
47
|
-
if (c > 127 || safeHostChar[c] === 0) {
|
|
48
|
-
needsHostValidationByURL = true;
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
51
|
-
if (c === 58) {
|
|
52
|
-
i++;
|
|
53
|
-
const firstDigit = host.charCodeAt(i);
|
|
54
|
-
if (firstDigit < 49 || firstDigit > 57 || i + 4 > len || i + (firstDigit < 54 ? 5 : 4) < len) {
|
|
55
|
-
needsHostValidationByURL = true;
|
|
56
|
-
break;
|
|
57
|
-
}
|
|
58
|
-
for (; i < len; i++) {
|
|
59
|
-
const c = host.charCodeAt(i);
|
|
60
|
-
if (c < 48 || c > 57) {
|
|
61
|
-
needsHostValidationByURL = true;
|
|
62
|
-
break;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
if (needsHostValidationByURL) {
|
|
23
|
+
if (!reValidHost.test(host)) {
|
|
68
24
|
const urlObj = new URL(url);
|
|
69
25
|
if (urlObj.hostname.length !== host.length && urlObj.hostname !== (host.includes(":") ? host.replace(/:\d+$/, "") : host).toLowerCase()) throw new RequestError("Invalid host header");
|
|
70
26
|
return urlObj.href;
|
|
71
27
|
} else if (incomingUrl.length === 0) return url + "/";
|
|
72
28
|
else {
|
|
73
29
|
if (incomingUrl.charCodeAt(0) !== 47) throw new RequestError("Invalid URL");
|
|
74
|
-
|
|
75
|
-
const c = incomingUrl.charCodeAt(i);
|
|
76
|
-
if (c > 127 || allowedRequestUrlChar[c] === 0 || c === 46 && hasDotSegment(incomingUrl, i)) return new URL(url).href;
|
|
77
|
-
}
|
|
30
|
+
if (!reValidRequestUrl.test(incomingUrl) || reDotSegment.test(incomingUrl)) return new URL(url).href;
|
|
78
31
|
return url;
|
|
79
32
|
}
|
|
80
33
|
};
|
|
@@ -86,7 +39,7 @@ const toRequestError = (e) => {
|
|
|
86
39
|
return new RequestError(e.message, { cause: e });
|
|
87
40
|
};
|
|
88
41
|
const GlobalRequest = global.Request;
|
|
89
|
-
var Request = class extends GlobalRequest {
|
|
42
|
+
var Request$1 = class extends GlobalRequest {
|
|
90
43
|
constructor(input, options) {
|
|
91
44
|
if (typeof input === "object" && getRequestCache in input) {
|
|
92
45
|
const hasReplacementBody = options !== void 0 && "body" in options && options.body != null;
|
|
@@ -100,9 +53,9 @@ var Request = class extends GlobalRequest {
|
|
|
100
53
|
const newHeadersFromIncoming = (incoming) => {
|
|
101
54
|
const headerRecord = [];
|
|
102
55
|
const rawHeaders = incoming.rawHeaders;
|
|
103
|
-
for (let i = 0
|
|
104
|
-
const
|
|
105
|
-
if (key.charCodeAt(0) !== 58) headerRecord.push([key,
|
|
56
|
+
for (let i = 0, len = rawHeaders.length; i < len; i += 2) {
|
|
57
|
+
const key = rawHeaders[i];
|
|
58
|
+
if (key.charCodeAt(0) !== 58) headerRecord.push([key, rawHeaders[i + 1]]);
|
|
106
59
|
}
|
|
107
60
|
return new Headers(headerRecord);
|
|
108
61
|
};
|
|
@@ -115,7 +68,7 @@ const newRequestFromIncoming = (method, url, headers, incoming, abortController)
|
|
|
115
68
|
};
|
|
116
69
|
if (method === "TRACE") {
|
|
117
70
|
init.method = "GET";
|
|
118
|
-
const req = new Request(url, init);
|
|
71
|
+
const req = new Request$1(url, init);
|
|
119
72
|
Object.defineProperty(req, "method", { get() {
|
|
120
73
|
return "TRACE";
|
|
121
74
|
} });
|
|
@@ -138,7 +91,7 @@ const newRequestFromIncoming = (method, url, headers, incoming, abortController)
|
|
|
138
91
|
}
|
|
139
92
|
} });
|
|
140
93
|
} else init.body = node_stream.Readable.toWeb(incoming);
|
|
141
|
-
return new Request(url, init);
|
|
94
|
+
return new Request$1(url, init);
|
|
142
95
|
};
|
|
143
96
|
const getRequestCache = Symbol("getRequestCache");
|
|
144
97
|
const requestCache = Symbol("requestCache");
|
|
@@ -330,7 +283,7 @@ const requestPrototype = {
|
|
|
330
283
|
} });
|
|
331
284
|
init.duplex = "half";
|
|
332
285
|
}
|
|
333
|
-
const req = new Request(this[urlKey], init);
|
|
286
|
+
const req = new Request$1(this[urlKey], init);
|
|
334
287
|
if (method === "TRACE") Object.defineProperty(req, "method", { get() {
|
|
335
288
|
return "TRACE";
|
|
336
289
|
} });
|
|
@@ -350,6 +303,9 @@ const requestPrototype = {
|
|
|
350
303
|
return false;
|
|
351
304
|
}
|
|
352
305
|
};
|
|
306
|
+
Object.defineProperty(requestPrototype, "signal", { get() {
|
|
307
|
+
return this[getAbortController]().signal;
|
|
308
|
+
} });
|
|
353
309
|
[
|
|
354
310
|
"cache",
|
|
355
311
|
"credentials",
|
|
@@ -359,7 +315,6 @@ const requestPrototype = {
|
|
|
359
315
|
"redirect",
|
|
360
316
|
"referrer",
|
|
361
317
|
"referrerPolicy",
|
|
362
|
-
"signal",
|
|
363
318
|
"keepalive"
|
|
364
319
|
].forEach((k) => {
|
|
365
320
|
Object.defineProperty(requestPrototype, k, { get() {
|
|
@@ -392,7 +347,18 @@ Object.defineProperty(requestPrototype, "json", { value: function() {
|
|
|
392
347
|
if (this[bodyConsumedDirectlyKey]) return rejectBodyUnusable();
|
|
393
348
|
return this.text().then(JSON.parse);
|
|
394
349
|
} });
|
|
395
|
-
Object.
|
|
350
|
+
Object.defineProperty(requestPrototype, Symbol.for("nodejs.util.inspect.custom"), { value: function(depth, options, inspectFn) {
|
|
351
|
+
return `Request (lightweight) ${inspectFn({
|
|
352
|
+
method: this.method,
|
|
353
|
+
url: this.url,
|
|
354
|
+
headers: this.headers,
|
|
355
|
+
nativeRequest: this[requestCache]
|
|
356
|
+
}, {
|
|
357
|
+
...options,
|
|
358
|
+
depth: depth == null ? null : depth - 1
|
|
359
|
+
})}`;
|
|
360
|
+
} });
|
|
361
|
+
Object.setPrototypeOf(requestPrototype, Request$1.prototype);
|
|
396
362
|
const newRequest = (incoming, defaultHostname) => {
|
|
397
363
|
const req = Object.create(requestPrototype);
|
|
398
364
|
req[incomingKey] = incoming;
|
|
@@ -425,6 +391,7 @@ const newRequest = (incoming, defaultHostname) => {
|
|
|
425
391
|
|
|
426
392
|
//#endregion
|
|
427
393
|
//#region src/response.ts
|
|
394
|
+
const defaultContentType = "text/plain; charset=UTF-8";
|
|
428
395
|
const responseCache = Symbol("responseCache");
|
|
429
396
|
const getResponseCache = Symbol("getResponseCache");
|
|
430
397
|
const cacheKey = Symbol("cache");
|
|
@@ -450,16 +417,16 @@ var Response$1 = class Response$1 {
|
|
|
450
417
|
headers = new Headers(init.#init.headers);
|
|
451
418
|
}
|
|
452
419
|
} else this.#init = init;
|
|
453
|
-
if (typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) this[cacheKey] = [
|
|
420
|
+
if (body == null || typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) this[cacheKey] = [
|
|
454
421
|
init?.status || 200,
|
|
455
|
-
body,
|
|
422
|
+
body ?? null,
|
|
456
423
|
headers || init?.headers
|
|
457
424
|
];
|
|
458
425
|
}
|
|
459
426
|
get headers() {
|
|
460
427
|
const cache = this[cacheKey];
|
|
461
428
|
if (cache) {
|
|
462
|
-
if (!(cache[2] instanceof Headers)) cache[2] = new Headers(cache[2] || { "content-type":
|
|
429
|
+
if (!(cache[2] instanceof Headers)) cache[2] = new Headers(cache[2] || (cache[1] === null ? void 0 : { "content-type": defaultContentType }));
|
|
463
430
|
return cache[2];
|
|
464
431
|
}
|
|
465
432
|
return this[getResponseCache]().headers;
|
|
@@ -497,8 +464,62 @@ var Response$1 = class Response$1 {
|
|
|
497
464
|
return this[getResponseCache]()[k]();
|
|
498
465
|
} });
|
|
499
466
|
});
|
|
467
|
+
Object.defineProperty(Response$1.prototype, Symbol.for("nodejs.util.inspect.custom"), { value: function(depth, options, inspectFn) {
|
|
468
|
+
return `Response (lightweight) ${inspectFn({
|
|
469
|
+
status: this.status,
|
|
470
|
+
headers: this.headers,
|
|
471
|
+
ok: this.ok,
|
|
472
|
+
nativeResponse: this[responseCache]
|
|
473
|
+
}, {
|
|
474
|
+
...options,
|
|
475
|
+
depth: depth == null ? null : depth - 1
|
|
476
|
+
})}`;
|
|
477
|
+
} });
|
|
500
478
|
Object.setPrototypeOf(Response$1, GlobalResponse);
|
|
501
479
|
Object.setPrototypeOf(Response$1.prototype, GlobalResponse.prototype);
|
|
480
|
+
const validRedirectUrl = /^https?:\/\/[!#-;=?-[\]_a-z~A-Z]+$/;
|
|
481
|
+
const parseRedirectUrl = (url) => {
|
|
482
|
+
if (url instanceof URL) return url.href;
|
|
483
|
+
if (validRedirectUrl.test(url)) return url;
|
|
484
|
+
return new URL(url).href;
|
|
485
|
+
};
|
|
486
|
+
const validRedirectStatuses = new Set([
|
|
487
|
+
301,
|
|
488
|
+
302,
|
|
489
|
+
303,
|
|
490
|
+
307,
|
|
491
|
+
308
|
|
492
|
+
]);
|
|
493
|
+
Object.defineProperty(Response$1, "redirect", {
|
|
494
|
+
value: function redirect(url, status = 302) {
|
|
495
|
+
if (!validRedirectStatuses.has(status)) throw new RangeError("Invalid status code");
|
|
496
|
+
return new Response$1(null, {
|
|
497
|
+
status,
|
|
498
|
+
headers: { location: parseRedirectUrl(url) }
|
|
499
|
+
});
|
|
500
|
+
},
|
|
501
|
+
writable: true,
|
|
502
|
+
configurable: true
|
|
503
|
+
});
|
|
504
|
+
Object.defineProperty(Response$1, "json", {
|
|
505
|
+
value: function json(data, init) {
|
|
506
|
+
const body = JSON.stringify(data);
|
|
507
|
+
if (body === void 0) throw new TypeError("The data is not JSON serializable");
|
|
508
|
+
const initHeaders = init?.headers;
|
|
509
|
+
let headers;
|
|
510
|
+
if (initHeaders) {
|
|
511
|
+
headers = new Headers(initHeaders);
|
|
512
|
+
if (!headers.has("content-type")) headers.set("content-type", "application/json");
|
|
513
|
+
} else headers = { "content-type": "application/json" };
|
|
514
|
+
return new Response$1(body, {
|
|
515
|
+
status: init?.status ?? 200,
|
|
516
|
+
statusText: init?.statusText,
|
|
517
|
+
headers
|
|
518
|
+
});
|
|
519
|
+
},
|
|
520
|
+
writable: true,
|
|
521
|
+
configurable: true
|
|
522
|
+
});
|
|
502
523
|
|
|
503
524
|
//#endregion
|
|
504
525
|
//#region src/utils.ts
|
|
@@ -537,7 +558,7 @@ function writeFromReadableStream(stream, writable) {
|
|
|
537
558
|
else if (writable.destroyed) return;
|
|
538
559
|
return writeFromReadableStreamDefaultReader(stream.getReader(), writable);
|
|
539
560
|
}
|
|
540
|
-
const buildOutgoingHttpHeaders = (headers) => {
|
|
561
|
+
const buildOutgoingHttpHeaders = (headers, defaultContentType) => {
|
|
541
562
|
const res = {};
|
|
542
563
|
if (!(headers instanceof Headers)) headers = new Headers(headers ?? void 0);
|
|
543
564
|
if (headers.has("set-cookie")) {
|
|
@@ -546,13 +567,63 @@ const buildOutgoingHttpHeaders = (headers) => {
|
|
|
546
567
|
else res[k] = v;
|
|
547
568
|
if (cookies.length > 0) res["set-cookie"] = cookies;
|
|
548
569
|
} else for (const [k, v] of headers) res[k] = v;
|
|
549
|
-
res["content-type"] ??=
|
|
570
|
+
if (defaultContentType) res["content-type"] ??= defaultContentType;
|
|
550
571
|
return res;
|
|
551
572
|
};
|
|
552
573
|
|
|
553
574
|
//#endregion
|
|
554
575
|
//#region src/listener.ts
|
|
555
576
|
const outgoingEnded = Symbol("outgoingEnded");
|
|
577
|
+
const incomingDraining = Symbol("incomingDraining");
|
|
578
|
+
const DRAIN_TIMEOUT_MS = 500;
|
|
579
|
+
const MAX_DRAIN_BYTES = 64 * 1024 * 1024;
|
|
580
|
+
const drainIncoming = (incoming) => {
|
|
581
|
+
const incomingWithDrainState = incoming;
|
|
582
|
+
if (incoming.destroyed || incomingWithDrainState[incomingDraining]) return;
|
|
583
|
+
incomingWithDrainState[incomingDraining] = true;
|
|
584
|
+
if (incoming instanceof node_http2.Http2ServerRequest) {
|
|
585
|
+
try {
|
|
586
|
+
incoming.stream?.close?.(node_http2.constants.NGHTTP2_NO_ERROR);
|
|
587
|
+
} catch {}
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
let bytesRead = 0;
|
|
591
|
+
const cleanup = () => {
|
|
592
|
+
clearTimeout(timer);
|
|
593
|
+
incoming.off("data", onData);
|
|
594
|
+
incoming.off("end", cleanup);
|
|
595
|
+
incoming.off("error", cleanup);
|
|
596
|
+
};
|
|
597
|
+
const forceClose = () => {
|
|
598
|
+
cleanup();
|
|
599
|
+
const socket = incoming.socket;
|
|
600
|
+
if (socket && !socket.destroyed) socket.destroySoon();
|
|
601
|
+
};
|
|
602
|
+
const timer = setTimeout(forceClose, DRAIN_TIMEOUT_MS);
|
|
603
|
+
timer.unref?.();
|
|
604
|
+
const onData = (chunk) => {
|
|
605
|
+
bytesRead += chunk.length;
|
|
606
|
+
if (bytesRead > MAX_DRAIN_BYTES) forceClose();
|
|
607
|
+
};
|
|
608
|
+
incoming.on("data", onData);
|
|
609
|
+
incoming.on("end", cleanup);
|
|
610
|
+
incoming.on("error", cleanup);
|
|
611
|
+
incoming.resume();
|
|
612
|
+
};
|
|
613
|
+
const makeCloseHandler = (req, incoming, outgoing, needsBodyCleanup) => () => {
|
|
614
|
+
if (incoming.errored) req[abortRequest](incoming.errored.toString());
|
|
615
|
+
else if (!outgoing.writableFinished) req[abortRequest]("Client connection prematurely closed.");
|
|
616
|
+
if (needsBodyCleanup && !incoming.readableEnded) setTimeout(() => {
|
|
617
|
+
if (!incoming.readableEnded) setTimeout(() => {
|
|
618
|
+
drainIncoming(incoming);
|
|
619
|
+
});
|
|
620
|
+
});
|
|
621
|
+
};
|
|
622
|
+
const isImmediateCacheableResponse = (res) => {
|
|
623
|
+
if (!(cacheKey in res)) return false;
|
|
624
|
+
const body = res[cacheKey][1];
|
|
625
|
+
return body === null || typeof body === "string" || body instanceof Uint8Array;
|
|
626
|
+
};
|
|
556
627
|
const handleRequestError = () => new Response(null, { status: 400 });
|
|
557
628
|
const handleFetchError = (e) => new Response(null, { status: e instanceof Error && (e.name === "TimeoutError" || e.constructor.name === "TimeoutError") ? 504 : 500 });
|
|
558
629
|
const handleResponseError = (e, outgoing) => {
|
|
@@ -570,15 +641,44 @@ const flushHeaders = (outgoing) => {
|
|
|
570
641
|
};
|
|
571
642
|
const responseViaCache = async (res, outgoing) => {
|
|
572
643
|
let [status, body, header] = res[cacheKey];
|
|
644
|
+
if (!header) {
|
|
645
|
+
if (body === null) {
|
|
646
|
+
outgoing.writeHead(status);
|
|
647
|
+
outgoing.end();
|
|
648
|
+
} else if (typeof body === "string") {
|
|
649
|
+
outgoing.writeHead(status, {
|
|
650
|
+
"Content-Type": defaultContentType,
|
|
651
|
+
"Content-Length": Buffer.byteLength(body)
|
|
652
|
+
});
|
|
653
|
+
outgoing.end(body);
|
|
654
|
+
} else if (body instanceof Uint8Array) {
|
|
655
|
+
outgoing.writeHead(status, {
|
|
656
|
+
"Content-Type": defaultContentType,
|
|
657
|
+
"Content-Length": body.byteLength
|
|
658
|
+
});
|
|
659
|
+
outgoing.end(body);
|
|
660
|
+
} else if (body instanceof Blob) {
|
|
661
|
+
outgoing.writeHead(status, {
|
|
662
|
+
"Content-Type": defaultContentType,
|
|
663
|
+
"Content-Length": body.size
|
|
664
|
+
});
|
|
665
|
+
outgoing.end(new Uint8Array(await body.arrayBuffer()));
|
|
666
|
+
} else {
|
|
667
|
+
outgoing.writeHead(status, { "Content-Type": defaultContentType });
|
|
668
|
+
flushHeaders(outgoing);
|
|
669
|
+
await writeFromReadableStream(body, outgoing)?.catch((e) => handleResponseError(e, outgoing));
|
|
670
|
+
}
|
|
671
|
+
outgoing[outgoingEnded]?.();
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
573
674
|
let hasContentLength = false;
|
|
574
|
-
if (
|
|
575
|
-
else if (header instanceof Headers) {
|
|
675
|
+
if (header instanceof Headers) {
|
|
576
676
|
hasContentLength = header.has("content-length");
|
|
577
|
-
header = buildOutgoingHttpHeaders(header);
|
|
677
|
+
header = buildOutgoingHttpHeaders(header, body === null ? void 0 : defaultContentType);
|
|
578
678
|
} else if (Array.isArray(header)) {
|
|
579
679
|
const headerObj = new Headers(header);
|
|
580
680
|
hasContentLength = headerObj.has("content-length");
|
|
581
|
-
header = buildOutgoingHttpHeaders(headerObj);
|
|
681
|
+
header = buildOutgoingHttpHeaders(headerObj, body === null ? void 0 : defaultContentType);
|
|
582
682
|
} else for (const key in header) if (key.length === 14 && key.toLowerCase() === "content-length") {
|
|
583
683
|
hasContentLength = true;
|
|
584
684
|
break;
|
|
@@ -589,7 +689,8 @@ const responseViaCache = async (res, outgoing) => {
|
|
|
589
689
|
else if (body instanceof Blob) header["Content-Length"] = body.size;
|
|
590
690
|
}
|
|
591
691
|
outgoing.writeHead(status, header);
|
|
592
|
-
if (
|
|
692
|
+
if (body == null) outgoing.end();
|
|
693
|
+
else if (typeof body === "string" || body instanceof Uint8Array) outgoing.end(body);
|
|
593
694
|
else if (body instanceof Blob) outgoing.end(new Uint8Array(await body.arrayBuffer()));
|
|
594
695
|
else {
|
|
595
696
|
flushHeaders(outgoing);
|
|
@@ -608,7 +709,7 @@ const responseViaResponseObject = async (res, outgoing, options = {}) => {
|
|
|
608
709
|
}
|
|
609
710
|
else res = await res.catch(handleFetchError);
|
|
610
711
|
if (cacheKey in res) return responseViaCache(res, outgoing);
|
|
611
|
-
const resHeaderRecord = buildOutgoingHttpHeaders(res.headers);
|
|
712
|
+
const resHeaderRecord = buildOutgoingHttpHeaders(res.headers, res.body === null ? void 0 : defaultContentType);
|
|
612
713
|
if (res.body) {
|
|
613
714
|
const reader = res.body.getReader();
|
|
614
715
|
const values = [];
|
|
@@ -656,47 +757,47 @@ const responseViaResponseObject = async (res, outgoing, options = {}) => {
|
|
|
656
757
|
};
|
|
657
758
|
const getRequestListener = (fetchCallback, options = {}) => {
|
|
658
759
|
const autoCleanupIncoming = options.autoCleanupIncoming ?? true;
|
|
659
|
-
if (options.overrideGlobalObjects !== false && global.Request !== Request) {
|
|
660
|
-
Object.defineProperty(global, "Request", { value: Request });
|
|
760
|
+
if (options.overrideGlobalObjects !== false && global.Request !== Request$1) {
|
|
761
|
+
Object.defineProperty(global, "Request", { value: Request$1 });
|
|
661
762
|
Object.defineProperty(global, "Response", { value: Response$1 });
|
|
662
763
|
}
|
|
663
764
|
return async (incoming, outgoing) => {
|
|
664
765
|
let res, req;
|
|
766
|
+
let needsBodyCleanup = false;
|
|
767
|
+
let closeHandlerAttached = false;
|
|
768
|
+
const ensureCloseHandler = () => {
|
|
769
|
+
if (!req || closeHandlerAttached) return;
|
|
770
|
+
closeHandlerAttached = true;
|
|
771
|
+
outgoing.on("close", makeCloseHandler(req, incoming, outgoing, needsBodyCleanup));
|
|
772
|
+
};
|
|
665
773
|
try {
|
|
666
774
|
req = newRequest(incoming, options.hostname);
|
|
667
|
-
|
|
668
|
-
if (
|
|
775
|
+
needsBodyCleanup = autoCleanupIncoming && !(incoming.method === "GET" || incoming.method === "HEAD");
|
|
776
|
+
if (needsBodyCleanup) {
|
|
669
777
|
incoming[wrapBodyStream] = true;
|
|
670
|
-
incoming.on("end", () => {
|
|
671
|
-
incomingEnded = true;
|
|
672
|
-
});
|
|
673
778
|
if (incoming instanceof node_http2.Http2ServerRequest) outgoing[outgoingEnded] = () => {
|
|
674
|
-
if (!
|
|
675
|
-
if (!
|
|
779
|
+
if (!incoming.readableEnded) setTimeout(() => {
|
|
780
|
+
if (!incoming.readableEnded) setTimeout(() => {
|
|
676
781
|
incoming.destroy();
|
|
677
782
|
outgoing.destroy();
|
|
678
783
|
});
|
|
679
784
|
});
|
|
680
785
|
};
|
|
681
786
|
}
|
|
682
|
-
outgoing.on("close", () => {
|
|
683
|
-
let abortReason;
|
|
684
|
-
if (incoming.errored) abortReason = incoming.errored.toString();
|
|
685
|
-
else if (!outgoing.writableFinished) abortReason = "Client connection prematurely closed.";
|
|
686
|
-
if (abortReason !== void 0) req[abortRequest](abortReason);
|
|
687
|
-
if (!incomingEnded) setTimeout(() => {
|
|
688
|
-
if (!incomingEnded) setTimeout(() => {
|
|
689
|
-
incoming.destroy();
|
|
690
|
-
});
|
|
691
|
-
});
|
|
692
|
-
});
|
|
693
787
|
res = fetchCallback(req, {
|
|
694
788
|
incoming,
|
|
695
789
|
outgoing
|
|
696
790
|
});
|
|
697
|
-
if (
|
|
791
|
+
if (!isPromise(res) && isImmediateCacheableResponse(res)) {
|
|
792
|
+
if (needsBodyCleanup && !incoming.readableEnded) outgoing.once("finish", () => {
|
|
793
|
+
if (!incoming.readableEnded) drainIncoming(incoming);
|
|
794
|
+
});
|
|
795
|
+
return responseViaCache(res, outgoing);
|
|
796
|
+
}
|
|
797
|
+
ensureCloseHandler();
|
|
698
798
|
} catch (e) {
|
|
699
799
|
if (!res) if (options.errorHandler) {
|
|
800
|
+
ensureCloseHandler();
|
|
700
801
|
res = await options.errorHandler(req ? e : toRequestError(e));
|
|
701
802
|
if (!res) return;
|
|
702
803
|
} else if (!req) res = handleRequestError();
|
|
@@ -712,15 +813,194 @@ const getRequestListener = (fetchCallback, options = {}) => {
|
|
|
712
813
|
};
|
|
713
814
|
|
|
714
815
|
//#endregion
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
816
|
+
//#region src/websocket.ts
|
|
817
|
+
/**
|
|
818
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
|
819
|
+
*/
|
|
820
|
+
const CloseEvent = globalThis.CloseEvent ?? class extends Event {
|
|
821
|
+
#eventInitDict;
|
|
822
|
+
constructor(type, eventInitDict = {}) {
|
|
823
|
+
super(type, eventInitDict);
|
|
824
|
+
this.#eventInitDict = eventInitDict;
|
|
825
|
+
}
|
|
826
|
+
get wasClean() {
|
|
827
|
+
return this.#eventInitDict.wasClean ?? false;
|
|
828
|
+
}
|
|
829
|
+
get code() {
|
|
830
|
+
return this.#eventInitDict.code ?? 0;
|
|
831
|
+
}
|
|
832
|
+
get reason() {
|
|
833
|
+
return this.#eventInitDict.reason ?? "";
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
const generateConnectionSymbol = () => Symbol("connection");
|
|
837
|
+
const CONNECTION_SYMBOL_KEY = Symbol("CONNECTION_SYMBOL_KEY");
|
|
838
|
+
const WAIT_FOR_WEBSOCKET_SYMBOL = Symbol("WAIT_FOR_WEBSOCKET_SYMBOL");
|
|
839
|
+
const rejectUpgradeRequest = (socket, status) => {
|
|
840
|
+
socket.end(`HTTP/1.1 ${status.toString()} ${node_http.STATUS_CODES[status] ?? ""}\r\nConnection: close\r
|
|
841
|
+
Content-Length: 0\r
|
|
842
|
+
\r
|
|
843
|
+
`);
|
|
844
|
+
};
|
|
845
|
+
const createUpgradeRequest = (request) => {
|
|
846
|
+
const protocol = request.socket.encrypted ? "https" : "http";
|
|
847
|
+
const url = new URL(request.url ?? "/", `${protocol}://${request.headers.host ?? "localhost"}`);
|
|
848
|
+
const headers = new Headers();
|
|
849
|
+
for (const key in request.headers) {
|
|
850
|
+
const value = request.headers[key];
|
|
851
|
+
if (!value) continue;
|
|
852
|
+
headers.append(key, Array.isArray(value) ? value[0] : value);
|
|
853
|
+
}
|
|
854
|
+
return new Request(url, { headers });
|
|
855
|
+
};
|
|
856
|
+
const setupWebSocket = (options) => {
|
|
857
|
+
const { server, fetchCallback, wss } = options;
|
|
858
|
+
const waiterMap = /* @__PURE__ */ new Map();
|
|
859
|
+
wss.on("connection", (ws, request) => {
|
|
860
|
+
const waiter = waiterMap.get(request);
|
|
861
|
+
if (waiter) {
|
|
862
|
+
waiter.resolve(ws);
|
|
863
|
+
waiterMap.delete(request);
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
const waitForWebSocket = (request, connectionSymbol) => {
|
|
867
|
+
return new Promise((resolve) => {
|
|
868
|
+
waiterMap.set(request, {
|
|
869
|
+
resolve,
|
|
870
|
+
connectionSymbol
|
|
871
|
+
});
|
|
872
|
+
});
|
|
873
|
+
};
|
|
874
|
+
server.on("upgrade", async (request, socket, head) => {
|
|
875
|
+
if (request.headers.upgrade?.toLowerCase() !== "websocket") return;
|
|
876
|
+
const env = {
|
|
877
|
+
incoming: request,
|
|
878
|
+
outgoing: void 0,
|
|
879
|
+
wss,
|
|
880
|
+
[WAIT_FOR_WEBSOCKET_SYMBOL]: waitForWebSocket
|
|
881
|
+
};
|
|
882
|
+
let status = 400;
|
|
883
|
+
try {
|
|
884
|
+
const response = await fetchCallback(createUpgradeRequest(request), env);
|
|
885
|
+
if (response instanceof Response) status = response.status;
|
|
886
|
+
} catch {
|
|
887
|
+
if (server.listenerCount("upgrade") === 1) rejectUpgradeRequest(socket, 500);
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
const waiter = waiterMap.get(request);
|
|
891
|
+
if (!waiter || waiter.connectionSymbol !== env[CONNECTION_SYMBOL_KEY]) {
|
|
892
|
+
waiterMap.delete(request);
|
|
893
|
+
if (server.listenerCount("upgrade") === 1) rejectUpgradeRequest(socket, status);
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
897
|
+
wss.emit("connection", ws, request);
|
|
898
|
+
});
|
|
899
|
+
});
|
|
900
|
+
server.on("close", () => {
|
|
901
|
+
wss.close();
|
|
902
|
+
});
|
|
903
|
+
};
|
|
904
|
+
const upgradeWebSocket = (0, hono_ws.defineWebSocketHelper)(async (c, events, options) => {
|
|
905
|
+
if (c.req.header("upgrade")?.toLowerCase() !== "websocket") return;
|
|
906
|
+
const env = c.env;
|
|
907
|
+
const waitForWebSocket = env[WAIT_FOR_WEBSOCKET_SYMBOL];
|
|
908
|
+
if (!waitForWebSocket || !env.incoming) return new Response(null, { status: 500 });
|
|
909
|
+
const connectionSymbol = generateConnectionSymbol();
|
|
910
|
+
env[CONNECTION_SYMBOL_KEY] = connectionSymbol;
|
|
911
|
+
(async () => {
|
|
912
|
+
const ws = await waitForWebSocket(env.incoming, connectionSymbol);
|
|
913
|
+
const messagesReceivedInStarting = [];
|
|
914
|
+
const bufferMessage = (data, isBinary) => {
|
|
915
|
+
messagesReceivedInStarting.push([data, isBinary]);
|
|
916
|
+
};
|
|
917
|
+
ws.on("message", bufferMessage);
|
|
918
|
+
const ctx = {
|
|
919
|
+
binaryType: "arraybuffer",
|
|
920
|
+
close(code, reason) {
|
|
921
|
+
ws.close(code, reason);
|
|
922
|
+
},
|
|
923
|
+
protocol: ws.protocol,
|
|
924
|
+
raw: ws,
|
|
925
|
+
get readyState() {
|
|
926
|
+
return ws.readyState;
|
|
927
|
+
},
|
|
928
|
+
send(source, opts) {
|
|
929
|
+
ws.send(source, { compress: opts?.compress });
|
|
930
|
+
},
|
|
931
|
+
url: new URL(c.req.url)
|
|
932
|
+
};
|
|
933
|
+
try {
|
|
934
|
+
events?.onOpen?.(new Event("open"), ctx);
|
|
935
|
+
} catch (e) {
|
|
936
|
+
(options?.onError ?? console.error)(e);
|
|
937
|
+
}
|
|
938
|
+
const handleMessage = (data, isBinary) => {
|
|
939
|
+
const datas = Array.isArray(data) ? data : [data];
|
|
940
|
+
for (const data of datas) try {
|
|
941
|
+
events?.onMessage?.(new MessageEvent("message", { data: isBinary ? data instanceof ArrayBuffer ? data : data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) : data.toString("utf-8") }), ctx);
|
|
942
|
+
} catch (e) {
|
|
943
|
+
(options?.onError ?? console.error)(e);
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
ws.off("message", bufferMessage);
|
|
947
|
+
for (const message of messagesReceivedInStarting) handleMessage(...message);
|
|
948
|
+
ws.on("message", (data, isBinary) => {
|
|
949
|
+
handleMessage(data, isBinary);
|
|
950
|
+
});
|
|
951
|
+
ws.on("close", (code, reason) => {
|
|
952
|
+
try {
|
|
953
|
+
events?.onClose?.(new CloseEvent("close", {
|
|
954
|
+
code,
|
|
955
|
+
reason: reason.toString()
|
|
956
|
+
}), ctx);
|
|
957
|
+
} catch (e) {
|
|
958
|
+
(options?.onError ?? console.error)(e);
|
|
959
|
+
}
|
|
960
|
+
});
|
|
961
|
+
ws.on("error", (error) => {
|
|
962
|
+
try {
|
|
963
|
+
events?.onError?.(new ErrorEvent("error", { error }), ctx);
|
|
964
|
+
} catch (e) {
|
|
965
|
+
(options?.onError ?? console.error)(e);
|
|
966
|
+
}
|
|
967
|
+
});
|
|
968
|
+
})();
|
|
969
|
+
return new Response();
|
|
720
970
|
});
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
971
|
+
|
|
972
|
+
//#endregion
|
|
973
|
+
//#region src/server.ts
|
|
974
|
+
const createAdaptorServer = (options) => {
|
|
975
|
+
const fetchCallback = options.fetch;
|
|
976
|
+
const requestListener = getRequestListener(fetchCallback, {
|
|
977
|
+
hostname: options.hostname,
|
|
978
|
+
overrideGlobalObjects: options.overrideGlobalObjects,
|
|
979
|
+
autoCleanupIncoming: options.autoCleanupIncoming
|
|
980
|
+
});
|
|
981
|
+
const server = (options.createServer || node_http.createServer)(options.serverOptions || {}, requestListener);
|
|
982
|
+
if (options.websocket && options.websocket.server) {
|
|
983
|
+
if (options.websocket.server.options.noServer !== true) throw new Error("WebSocket server must be created with { noServer: true } option");
|
|
984
|
+
setupWebSocket({
|
|
985
|
+
server,
|
|
986
|
+
fetchCallback,
|
|
987
|
+
wss: options.websocket.server
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
return server;
|
|
991
|
+
};
|
|
992
|
+
const serve = (options, listeningListener) => {
|
|
993
|
+
const server = createAdaptorServer(options);
|
|
994
|
+
server.listen(options?.port ?? 3e3, options.hostname, () => {
|
|
995
|
+
const serverInfo = server.address();
|
|
996
|
+
listeningListener && listeningListener(serverInfo);
|
|
997
|
+
});
|
|
998
|
+
return server;
|
|
999
|
+
};
|
|
1000
|
+
|
|
1001
|
+
//#endregion
|
|
1002
|
+
exports.RequestError = RequestError;
|
|
1003
|
+
exports.createAdaptorServer = createAdaptorServer;
|
|
1004
|
+
exports.getRequestListener = getRequestListener;
|
|
1005
|
+
exports.serve = serve;
|
|
1006
|
+
exports.upgradeWebSocket = upgradeWebSocket;
|