-
Notifications
You must be signed in to change notification settings - Fork 45
/
request.ts
84 lines (74 loc) · 2.17 KB
/
request.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/* eslint-disable @typescript-eslint/no-explicit-any */
// Define prototype for lightweight pseudo Request object
import type { IncomingMessage } from 'node:http'
import type { Http2ServerRequest } from 'node:http2'
import { Readable } from 'node:stream'
const newRequestFromIncoming = (
method: string,
url: string,
incoming: IncomingMessage | Http2ServerRequest
): Request => {
const headerRecord: [string, string][] = []
const len = incoming.rawHeaders.length
for (let i = 0; i < len; i += 2) {
headerRecord.push([incoming.rawHeaders[i], incoming.rawHeaders[i + 1]])
}
const init = {
method: method,
headers: headerRecord,
} as RequestInit
if (!(method === 'GET' || method === 'HEAD')) {
// lazy-consume request body
init.body = Readable.toWeb(incoming) as ReadableStream<Uint8Array>
// node 18 fetch needs half duplex mode when request body is stream
;(init as any).duplex = 'half'
}
return new Request(url, init)
}
const getRequestCache = Symbol('getRequestCache')
const requestCache = Symbol('requestCache')
const incomingKey = Symbol('incomingKey')
const requestPrototype: Record<string | symbol, any> = {
get method() {
return this[incomingKey].method || 'GET'
},
get url() {
return `http://${this[incomingKey].headers.host}${this[incomingKey].url}`
},
[getRequestCache]() {
return (this[requestCache] ||= newRequestFromIncoming(this.method, this.url, this[incomingKey]))
},
}
;[
'body',
'bodyUsed',
'cache',
'credentials',
'destination',
'headers',
'integrity',
'mode',
'redirect',
'referrer',
'referrerPolicy',
'signal',
].forEach((k) => {
Object.defineProperty(requestPrototype, k, {
get() {
return this[getRequestCache]()[k]
},
})
})
;['arrayBuffer', 'blob', 'clone', 'formData', 'json', 'text'].forEach((k) => {
Object.defineProperty(requestPrototype, k, {
value: function () {
return this[getRequestCache]()[k]()
},
})
})
Object.setPrototypeOf(requestPrototype, global.Request.prototype)
export const newRequest = (incoming: IncomingMessage | Http2ServerRequest) => {
const req = Object.create(requestPrototype)
req[incomingKey] = incoming
return req
}