undici 7.0.0-alpha.6 → 7.0.0-alpha.8
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/docs/docs/api/CacheStore.md +13 -1
- package/docs/docs/api/Dispatcher.md +8 -8
- package/docs/docs/api/RedirectHandler.md +1 -1
- package/docs/docs/api/RetryHandler.md +2 -2
- package/index.js +9 -0
- package/lib/cache/memory-cache-store.js +7 -10
- package/lib/cache/sqlite-cache-store.js +446 -0
- package/lib/core/util.js +5 -0
- package/lib/dispatcher/client-h1.js +11 -0
- package/lib/dispatcher/client-h2.js +20 -6
- package/lib/dispatcher/dispatcher-base.js +2 -1
- package/lib/dispatcher/dispatcher.js +4 -0
- package/lib/handler/cache-handler.js +189 -195
- package/lib/handler/cache-revalidation-handler.js +44 -71
- package/lib/handler/decorator-handler.js +3 -0
- package/lib/handler/redirect-handler.js +39 -55
- package/lib/handler/retry-handler.js +9 -15
- package/lib/handler/unwrap-handler.js +96 -0
- package/lib/handler/wrap-handler.js +98 -0
- package/lib/interceptor/cache.js +254 -193
- package/lib/util/cache.js +117 -41
- package/package.json +4 -3
- package/types/agent.d.ts +1 -1
- package/types/cache-interceptor.d.ts +84 -6
- package/types/dispatcher.d.ts +28 -3
- package/types/env-http-proxy-agent.d.ts +1 -1
- package/types/handlers.d.ts +4 -4
- package/types/mock-agent.d.ts +1 -1
- package/types/mock-client.d.ts +1 -1
- package/types/mock-pool.d.ts +1 -1
- package/types/proxy-agent.d.ts +1 -1
- package/types/retry-handler.d.ts +3 -3
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const assert = require('node:assert')
|
|
4
|
-
const DecoratorHandler = require('../handler/decorator-handler')
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* This takes care of revalidation requests we send to the origin. If we get
|
|
@@ -14,123 +13,97 @@ const DecoratorHandler = require('../handler/decorator-handler')
|
|
|
14
13
|
*
|
|
15
14
|
* @see https://www.rfc-editor.org/rfc/rfc9111.html#name-validation
|
|
16
15
|
*
|
|
17
|
-
* @
|
|
18
|
-
* @implements {DispatchHandlers}
|
|
16
|
+
* @implements {import('../../types/dispatcher.d.ts').default.DispatchHandler}
|
|
19
17
|
*/
|
|
20
|
-
class CacheRevalidationHandler
|
|
18
|
+
class CacheRevalidationHandler {
|
|
21
19
|
#successful = false
|
|
20
|
+
|
|
22
21
|
/**
|
|
23
|
-
* @type {((boolean) => void) | null}
|
|
22
|
+
* @type {((boolean, any) => void) | null}
|
|
24
23
|
*/
|
|
25
24
|
#callback
|
|
25
|
+
|
|
26
26
|
/**
|
|
27
|
-
* @type {(import('../../types/dispatcher.d.ts').default.
|
|
27
|
+
* @type {(import('../../types/dispatcher.d.ts').default.DispatchHandler)}
|
|
28
28
|
*/
|
|
29
29
|
#handler
|
|
30
30
|
|
|
31
|
-
#
|
|
31
|
+
#context
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @type {boolean}
|
|
35
|
+
*/
|
|
36
|
+
#allowErrorStatusCodes
|
|
32
37
|
|
|
33
38
|
/**
|
|
34
39
|
* @param {(boolean) => void} callback Function to call if the cached value is valid
|
|
35
40
|
* @param {import('../../types/dispatcher.d.ts').default.DispatchHandlers} handler
|
|
41
|
+
* @param {boolean} allowErrorStatusCodes
|
|
36
42
|
*/
|
|
37
|
-
constructor (callback, handler) {
|
|
43
|
+
constructor (callback, handler, allowErrorStatusCodes) {
|
|
38
44
|
if (typeof callback !== 'function') {
|
|
39
45
|
throw new TypeError('callback must be a function')
|
|
40
46
|
}
|
|
41
47
|
|
|
42
|
-
super(handler)
|
|
43
|
-
|
|
44
48
|
this.#callback = callback
|
|
45
49
|
this.#handler = handler
|
|
50
|
+
this.#allowErrorStatusCodes = allowErrorStatusCodes
|
|
46
51
|
}
|
|
47
52
|
|
|
48
|
-
|
|
53
|
+
onRequestStart (_, context) {
|
|
49
54
|
this.#successful = false
|
|
50
|
-
this.#
|
|
55
|
+
this.#context = context
|
|
51
56
|
}
|
|
52
57
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
* @param {string} statusMessage
|
|
60
|
-
* @returns {boolean}
|
|
61
|
-
*/
|
|
62
|
-
onHeaders (
|
|
58
|
+
onRequestUpgrade (controller, statusCode, headers, socket) {
|
|
59
|
+
this.#handler.onRequestUpgrade?.(controller, statusCode, headers, socket)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
onResponseStart (
|
|
63
|
+
controller,
|
|
63
64
|
statusCode,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
statusMessage
|
|
65
|
+
statusMessage,
|
|
66
|
+
headers
|
|
67
67
|
) {
|
|
68
68
|
assert(this.#callback != null)
|
|
69
69
|
|
|
70
70
|
// https://www.rfc-editor.org/rfc/rfc9111.html#name-handling-a-validation-respo
|
|
71
|
-
|
|
72
|
-
this.#
|
|
71
|
+
// https://datatracker.ietf.org/doc/html/rfc5861#section-4
|
|
72
|
+
this.#successful = statusCode === 304 ||
|
|
73
|
+
(this.#allowErrorStatusCodes && statusCode >= 500 && statusCode <= 504)
|
|
74
|
+
this.#callback(this.#successful, this.#context)
|
|
73
75
|
this.#callback = null
|
|
74
76
|
|
|
75
77
|
if (this.#successful) {
|
|
76
78
|
return true
|
|
77
79
|
}
|
|
78
80
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
rawHeaders,
|
|
87
|
-
resume,
|
|
88
|
-
statusMessage
|
|
89
|
-
)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return true
|
|
81
|
+
this.#handler.onRequestStart?.(controller, this.#context)
|
|
82
|
+
this.#handler.onResponseStart?.(
|
|
83
|
+
controller,
|
|
84
|
+
statusCode,
|
|
85
|
+
statusMessage,
|
|
86
|
+
headers
|
|
87
|
+
)
|
|
93
88
|
}
|
|
94
89
|
|
|
95
|
-
|
|
96
|
-
* @see {DispatchHandlers.onData}
|
|
97
|
-
*
|
|
98
|
-
* @param {Buffer} chunk
|
|
99
|
-
* @returns {boolean}
|
|
100
|
-
*/
|
|
101
|
-
onData (chunk) {
|
|
90
|
+
onResponseData (controller, chunk) {
|
|
102
91
|
if (this.#successful) {
|
|
103
|
-
return
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (typeof this.#handler.onData === 'function') {
|
|
107
|
-
return this.#handler.onData(chunk)
|
|
92
|
+
return
|
|
108
93
|
}
|
|
109
94
|
|
|
110
|
-
return
|
|
95
|
+
return this.#handler.onResponseData(controller, chunk)
|
|
111
96
|
}
|
|
112
97
|
|
|
113
|
-
|
|
114
|
-
* @see {DispatchHandlers.onComplete}
|
|
115
|
-
*
|
|
116
|
-
* @param {string[] | null} rawTrailers
|
|
117
|
-
*/
|
|
118
|
-
onComplete (rawTrailers) {
|
|
98
|
+
onResponseEnd (controller, trailers) {
|
|
119
99
|
if (this.#successful) {
|
|
120
100
|
return
|
|
121
101
|
}
|
|
122
102
|
|
|
123
|
-
|
|
124
|
-
this.#handler.onComplete(rawTrailers)
|
|
125
|
-
}
|
|
103
|
+
this.#handler.onResponseEnd?.(controller, trailers)
|
|
126
104
|
}
|
|
127
105
|
|
|
128
|
-
|
|
129
|
-
* @see {DispatchHandlers.onError}
|
|
130
|
-
*
|
|
131
|
-
* @param {Error} err
|
|
132
|
-
*/
|
|
133
|
-
onError (err) {
|
|
106
|
+
onResponseError (controller, err) {
|
|
134
107
|
if (this.#successful) {
|
|
135
108
|
return
|
|
136
109
|
}
|
|
@@ -140,8 +113,8 @@ class CacheRevalidationHandler extends DecoratorHandler {
|
|
|
140
113
|
this.#callback = null
|
|
141
114
|
}
|
|
142
115
|
|
|
143
|
-
if (typeof this.#handler.
|
|
144
|
-
this.#handler.
|
|
116
|
+
if (typeof this.#handler.onResponseError === 'function') {
|
|
117
|
+
this.#handler.onResponseError(controller, err)
|
|
145
118
|
} else {
|
|
146
119
|
throw err
|
|
147
120
|
}
|
|
@@ -10,6 +10,8 @@ const redirectableStatusCodes = [300, 301, 302, 303, 307, 308]
|
|
|
10
10
|
|
|
11
11
|
const kBody = Symbol('body')
|
|
12
12
|
|
|
13
|
+
const noop = () => {}
|
|
14
|
+
|
|
13
15
|
class BodyAsyncIterable {
|
|
14
16
|
constructor (body) {
|
|
15
17
|
this[kBody] = body
|
|
@@ -38,16 +40,12 @@ class RedirectHandler {
|
|
|
38
40
|
throw new InvalidArgumentError('maxRedirections must be a positive number')
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
util.assertRequestHandler(handler, opts.method, opts.upgrade)
|
|
42
|
-
|
|
43
43
|
this.dispatch = dispatch
|
|
44
44
|
this.location = null
|
|
45
|
-
this.abort = null
|
|
46
45
|
this.opts = { ...opts, maxRedirections: 0 } // opts must be a copy
|
|
47
46
|
this.maxRedirections = maxRedirections
|
|
48
47
|
this.handler = handler
|
|
49
48
|
this.history = []
|
|
50
|
-
this.redirectionLimitReached = false
|
|
51
49
|
|
|
52
50
|
if (util.isStream(this.opts.body)) {
|
|
53
51
|
// TODO (fix): Provide some way for the user to cache the file to e.g. /tmp
|
|
@@ -84,40 +82,51 @@ class RedirectHandler {
|
|
|
84
82
|
}
|
|
85
83
|
}
|
|
86
84
|
|
|
87
|
-
|
|
88
|
-
this.
|
|
89
|
-
this.handler.onConnect(abort, { history: this.history })
|
|
85
|
+
onRequestStart (controller, context) {
|
|
86
|
+
this.handler.onRequestStart?.(controller, { ...context, history: this.history })
|
|
90
87
|
}
|
|
91
88
|
|
|
92
|
-
|
|
93
|
-
this.handler.
|
|
89
|
+
onRequestUpgrade (controller, statusCode, headers, socket) {
|
|
90
|
+
this.handler.onRequestUpgrade?.(controller, statusCode, headers, socket)
|
|
94
91
|
}
|
|
95
92
|
|
|
96
|
-
|
|
97
|
-
this.handler.onError(error)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
onHeaders (statusCode, headers, resume, statusText) {
|
|
101
|
-
this.location = this.history.length >= this.maxRedirections || util.isDisturbed(this.opts.body)
|
|
102
|
-
? null
|
|
103
|
-
: parseLocation(statusCode, headers)
|
|
104
|
-
|
|
93
|
+
onResponseStart (controller, statusCode, statusMessage, headers) {
|
|
105
94
|
if (this.opts.throwOnMaxRedirect && this.history.length >= this.maxRedirections) {
|
|
106
|
-
|
|
107
|
-
|
|
95
|
+
throw new Error('max redirects')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// https://tools.ietf.org/html/rfc7231#section-6.4.2
|
|
99
|
+
// https://fetch.spec.whatwg.org/#http-redirect-fetch
|
|
100
|
+
// In case of HTTP 301 or 302 with POST, change the method to GET
|
|
101
|
+
if ((statusCode === 301 || statusCode === 302) && this.opts.method === 'POST') {
|
|
102
|
+
this.opts.method = 'GET'
|
|
103
|
+
if (util.isStream(this.opts.body)) {
|
|
104
|
+
util.destroy(this.opts.body.on('error', noop))
|
|
108
105
|
}
|
|
106
|
+
this.opts.body = null
|
|
107
|
+
}
|
|
109
108
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
109
|
+
// https://tools.ietf.org/html/rfc7231#section-6.4.4
|
|
110
|
+
// In case of HTTP 303, always replace method to be either HEAD or GET
|
|
111
|
+
if (statusCode === 303 && this.opts.method !== 'HEAD') {
|
|
112
|
+
this.opts.method = 'GET'
|
|
113
|
+
if (util.isStream(this.opts.body)) {
|
|
114
|
+
util.destroy(this.opts.body.on('error', noop))
|
|
115
|
+
}
|
|
116
|
+
this.opts.body = null
|
|
113
117
|
}
|
|
114
118
|
|
|
119
|
+
this.location = this.history.length >= this.maxRedirections || util.isDisturbed(this.opts.body) || redirectableStatusCodes.indexOf(statusCode) === -1
|
|
120
|
+
? null
|
|
121
|
+
: headers.location
|
|
122
|
+
|
|
115
123
|
if (this.opts.origin) {
|
|
116
124
|
this.history.push(new URL(this.opts.path, this.opts.origin))
|
|
117
125
|
}
|
|
118
126
|
|
|
119
127
|
if (!this.location) {
|
|
120
|
-
|
|
128
|
+
this.handler.onResponseStart?.(controller, statusCode, statusMessage, headers)
|
|
129
|
+
return
|
|
121
130
|
}
|
|
122
131
|
|
|
123
132
|
const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)))
|
|
@@ -131,16 +140,9 @@ class RedirectHandler {
|
|
|
131
140
|
this.opts.origin = origin
|
|
132
141
|
this.opts.maxRedirections = 0
|
|
133
142
|
this.opts.query = null
|
|
134
|
-
|
|
135
|
-
// https://tools.ietf.org/html/rfc7231#section-6.4.4
|
|
136
|
-
// In case of HTTP 303, always replace method to be either HEAD or GET
|
|
137
|
-
if (statusCode === 303 && this.opts.method !== 'HEAD') {
|
|
138
|
-
this.opts.method = 'GET'
|
|
139
|
-
this.opts.body = null
|
|
140
|
-
}
|
|
141
143
|
}
|
|
142
144
|
|
|
143
|
-
|
|
145
|
+
onResponseData (controller, chunk) {
|
|
144
146
|
if (this.location) {
|
|
145
147
|
/*
|
|
146
148
|
https://tools.ietf.org/html/rfc7231#section-6.4
|
|
@@ -160,11 +162,11 @@ class RedirectHandler {
|
|
|
160
162
|
servers and browsers implementors, we ignore the body as there is no specified way to eventually parse it.
|
|
161
163
|
*/
|
|
162
164
|
} else {
|
|
163
|
-
|
|
165
|
+
this.handler.onResponseData?.(controller, chunk)
|
|
164
166
|
}
|
|
165
167
|
}
|
|
166
168
|
|
|
167
|
-
|
|
169
|
+
onResponseEnd (controller, trailers) {
|
|
168
170
|
if (this.location) {
|
|
169
171
|
/*
|
|
170
172
|
https://tools.ietf.org/html/rfc7231#section-6.4
|
|
@@ -174,32 +176,14 @@ class RedirectHandler {
|
|
|
174
176
|
|
|
175
177
|
See comment on onData method above for more detailed information.
|
|
176
178
|
*/
|
|
177
|
-
|
|
178
|
-
this.location = null
|
|
179
|
-
this.abort = null
|
|
180
|
-
|
|
181
179
|
this.dispatch(this.opts, this)
|
|
182
180
|
} else {
|
|
183
|
-
this.handler.
|
|
181
|
+
this.handler.onResponseEnd(controller, trailers)
|
|
184
182
|
}
|
|
185
183
|
}
|
|
186
184
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
this.handler.onBodySent(chunk)
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function parseLocation (statusCode, headers) {
|
|
195
|
-
if (redirectableStatusCodes.indexOf(statusCode) === -1) {
|
|
196
|
-
return null
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
for (let i = 0; i < headers.length; i += 2) {
|
|
200
|
-
if (headers[i].length === 8 && util.headerNameToString(headers[i]) === 'location') {
|
|
201
|
-
return headers[i + 1]
|
|
202
|
-
}
|
|
185
|
+
onResponseError (controller, error) {
|
|
186
|
+
this.handler.onResponseError?.(controller, error)
|
|
203
187
|
}
|
|
204
188
|
}
|
|
205
189
|
|
|
@@ -37,6 +37,7 @@ class RetryHandler {
|
|
|
37
37
|
this.opts = { ...dispatchOpts, body: wrapRequestBody(opts.body) }
|
|
38
38
|
this.abort = null
|
|
39
39
|
this.aborted = false
|
|
40
|
+
this.connectCalled = false
|
|
40
41
|
this.retryOpts = {
|
|
41
42
|
retry: retryFn ?? RetryHandler[kRetryHandlerDefaultRetry],
|
|
42
43
|
retryAfter: retryAfter ?? true,
|
|
@@ -68,16 +69,6 @@ class RetryHandler {
|
|
|
68
69
|
this.end = null
|
|
69
70
|
this.etag = null
|
|
70
71
|
this.resume = null
|
|
71
|
-
|
|
72
|
-
// Handle possible onConnect duplication
|
|
73
|
-
this.handler.onConnect(reason => {
|
|
74
|
-
this.aborted = true
|
|
75
|
-
if (this.abort) {
|
|
76
|
-
this.abort(reason)
|
|
77
|
-
} else {
|
|
78
|
-
this.reason = reason
|
|
79
|
-
}
|
|
80
|
-
})
|
|
81
72
|
}
|
|
82
73
|
|
|
83
74
|
onRequestSent () {
|
|
@@ -92,11 +83,14 @@ class RetryHandler {
|
|
|
92
83
|
}
|
|
93
84
|
}
|
|
94
85
|
|
|
95
|
-
onConnect (abort) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
this.
|
|
86
|
+
onConnect (abort, context) {
|
|
87
|
+
this.abort = abort
|
|
88
|
+
if (!this.connectCalled) {
|
|
89
|
+
this.connectCalled = true
|
|
90
|
+
this.handler.onConnect(reason => {
|
|
91
|
+
this.aborted = true
|
|
92
|
+
this.abort(reason)
|
|
93
|
+
}, context)
|
|
100
94
|
}
|
|
101
95
|
}
|
|
102
96
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { parseHeaders } = require('../core/util')
|
|
4
|
+
const { InvalidArgumentError } = require('../core/errors')
|
|
5
|
+
|
|
6
|
+
const kResume = Symbol('resume')
|
|
7
|
+
|
|
8
|
+
class UnwrapController {
|
|
9
|
+
#paused = false
|
|
10
|
+
#reason = null
|
|
11
|
+
#aborted = false
|
|
12
|
+
#abort
|
|
13
|
+
|
|
14
|
+
[kResume] = null
|
|
15
|
+
|
|
16
|
+
constructor (abort) {
|
|
17
|
+
this.#abort = abort
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
pause () {
|
|
21
|
+
this.#paused = true
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
resume () {
|
|
25
|
+
if (this.#paused) {
|
|
26
|
+
this.#paused = false
|
|
27
|
+
this[kResume]?.()
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
abort (reason) {
|
|
32
|
+
if (!this.#aborted) {
|
|
33
|
+
this.#aborted = true
|
|
34
|
+
this.#reason = reason
|
|
35
|
+
this.#abort(reason)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get aborted () {
|
|
40
|
+
return this.#aborted
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get reason () {
|
|
44
|
+
return this.#reason
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get paused () {
|
|
48
|
+
return this.#paused
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = class UnwrapHandler {
|
|
53
|
+
#handler
|
|
54
|
+
#controller
|
|
55
|
+
|
|
56
|
+
constructor (handler) {
|
|
57
|
+
this.#handler = handler
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static unwrap (handler) {
|
|
61
|
+
// TODO (fix): More checks...
|
|
62
|
+
return !handler.onRequestStart ? handler : new UnwrapHandler(handler)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
onConnect (abort, context) {
|
|
66
|
+
this.#controller = new UnwrapController(abort)
|
|
67
|
+
this.#handler.onRequestStart?.(this.#controller, context)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
onUpgrade (statusCode, rawHeaders, socket) {
|
|
71
|
+
this.#handler.onRequestUpgrade?.(this.#controller, statusCode, parseHeaders(rawHeaders), socket)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
onHeaders (statusCode, rawHeaders, resume, statusMessage) {
|
|
75
|
+
this.#controller[kResume] = resume
|
|
76
|
+
this.#handler.onResponseStart?.(this.#controller, statusCode, statusMessage, parseHeaders(rawHeaders))
|
|
77
|
+
return !this.#controller.paused
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
onData (data) {
|
|
81
|
+
this.#handler.onResponseData?.(this.#controller, data)
|
|
82
|
+
return !this.#controller.paused
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
onComplete (rawTrailers) {
|
|
86
|
+
this.#handler.onResponseEnd?.(this.#controller, parseHeaders(rawTrailers))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
onError (err) {
|
|
90
|
+
if (!this.#handler.onResponseError) {
|
|
91
|
+
throw new InvalidArgumentError('invalid onError method')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.#handler.onResponseError?.(this.#controller, err)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { InvalidArgumentError } = require('../core/errors')
|
|
4
|
+
|
|
5
|
+
module.exports = class WrapHandler {
|
|
6
|
+
#handler
|
|
7
|
+
|
|
8
|
+
constructor (handler) {
|
|
9
|
+
this.#handler = handler
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static wrap (handler) {
|
|
13
|
+
// TODO (fix): More checks...
|
|
14
|
+
return handler.onRequestStart ? handler : new WrapHandler(handler)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Unwrap Interface
|
|
18
|
+
|
|
19
|
+
onConnect (abort, context) {
|
|
20
|
+
return this.#handler.onConnect?.(abort, context)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
onHeaders (statusCode, rawHeaders, resume, statusMessage) {
|
|
24
|
+
return this.#handler.onHeaders?.(statusCode, rawHeaders, resume, statusMessage)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
onUpgrade (statusCode, rawHeaders, socket) {
|
|
28
|
+
return this.#handler.onUpgrade?.(statusCode, rawHeaders, socket)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
onData (data) {
|
|
32
|
+
return this.#handler.onData?.(data)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
onComplete (trailers) {
|
|
36
|
+
return this.#handler.onComplete?.(trailers)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
onError (err) {
|
|
40
|
+
if (!this.#handler.onError) {
|
|
41
|
+
throw new InvalidArgumentError('invalid onError method')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return this.#handler.onError?.(err)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Wrap Interface
|
|
48
|
+
|
|
49
|
+
onRequestStart (controller, context) {
|
|
50
|
+
this.#handler.onConnect?.((reason) => controller.abort(reason), context)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
onRequestUpgrade (controller, statusCode, headers, socket) {
|
|
54
|
+
const rawHeaders = []
|
|
55
|
+
for (const [key, val] of Object.entries(headers)) {
|
|
56
|
+
// TODO (fix): What if val is Array
|
|
57
|
+
rawHeaders.push(Buffer.from(key), Buffer.from(val))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.#handler.onUpgrade?.(statusCode, rawHeaders, socket)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
onResponseStart (controller, statusCode, statusMessage, headers) {
|
|
64
|
+
const rawHeaders = []
|
|
65
|
+
for (const [key, val] of Object.entries(headers)) {
|
|
66
|
+
// TODO (fix): What if val is Array
|
|
67
|
+
rawHeaders.push(Buffer.from(key), Buffer.from(val))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (this.#handler.onHeaders?.(statusCode, rawHeaders, () => controller.resume(), statusMessage) === false) {
|
|
71
|
+
controller.pause()
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
onResponseData (controller, data) {
|
|
76
|
+
if (this.#handler.onData?.(data) === false) {
|
|
77
|
+
controller.pause()
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
onResponseEnd (controller, trailers) {
|
|
82
|
+
const rawTrailers = []
|
|
83
|
+
for (const [key, val] of Object.entries(trailers)) {
|
|
84
|
+
// TODO (fix): What if val is Array
|
|
85
|
+
rawTrailers.push(Buffer.from(key), Buffer.from(val))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
this.#handler.onComplete?.(rawTrailers)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
onResponseError (controller, err) {
|
|
92
|
+
if (!this.#handler.onError) {
|
|
93
|
+
throw new InvalidArgumentError('invalid onError method')
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this.#handler.onError?.(err)
|
|
97
|
+
}
|
|
98
|
+
}
|