@nxtedition/lib 15.0.34 → 15.0.36
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/package.json +2 -8
- package/lib/undici/index.js +0 -237
- package/lib/undici/interceptor/abort.js +0 -65
- package/lib/undici/interceptor/cache.js +0 -202
- package/lib/undici/interceptor/catch.js +0 -63
- package/lib/undici/interceptor/content.js +0 -141
- package/lib/undici/interceptor/log.js +0 -79
- package/lib/undici/interceptor/proxy.js +0 -200
- package/lib/undici/interceptor/redirect.js +0 -185
- package/lib/undici/interceptor/response-body-retry.js +0 -163
- package/lib/undici/interceptor/response-retry.js +0 -89
- package/lib/undici/interceptor/response-status-retry.js +0 -104
- package/lib/undici/interceptor/signal.js +0 -51
- package/lib/undici/utils.js +0 -183
- package/proxy.js +0 -106
- package/undici.js +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/lib",
|
|
3
|
-
"version": "15.0.
|
|
3
|
+
"version": "15.0.36",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Robert Nagy <robert.nagy@boffins.se>",
|
|
6
6
|
"files": [
|
|
@@ -8,8 +8,6 @@
|
|
|
8
8
|
"ass.js",
|
|
9
9
|
"rxjs/*",
|
|
10
10
|
"util/*",
|
|
11
|
-
"lib/*",
|
|
12
|
-
"undici.js",
|
|
13
11
|
"http-client.js",
|
|
14
12
|
"subtract-ranges.js",
|
|
15
13
|
"serializers.js",
|
|
@@ -74,7 +72,6 @@
|
|
|
74
72
|
"/__tests__"
|
|
75
73
|
],
|
|
76
74
|
"dependencies": {
|
|
77
|
-
"cache-control-parser": "^2.0.4",
|
|
78
75
|
"date-fns": "^2.29.3",
|
|
79
76
|
"fast-querystring": "^1.1.1",
|
|
80
77
|
"hasha": "^5.2.2",
|
|
@@ -82,13 +79,11 @@
|
|
|
82
79
|
"json5": "^2.2.3",
|
|
83
80
|
"koa-compose": "^4.1.0",
|
|
84
81
|
"lodash": "^4.17.21",
|
|
85
|
-
"lru-cache": "^10.0.1",
|
|
86
82
|
"mime": "^3.0.0",
|
|
87
83
|
"moment-timezone": "^0.5.43",
|
|
88
84
|
"nconf": "^0.12.0",
|
|
89
85
|
"nested-error-stacks": "^2.1.1",
|
|
90
86
|
"object-hash": "^3.0.0",
|
|
91
|
-
"omit-empty": "^1.0.0",
|
|
92
87
|
"pino-std-serializers": "^6.2.2",
|
|
93
88
|
"qs": "^6.11.1",
|
|
94
89
|
"request-target": "^1.0.2",
|
|
@@ -96,8 +91,7 @@
|
|
|
96
91
|
"split-string": "^6.0.0",
|
|
97
92
|
"toobusy-js": "^0.5.1",
|
|
98
93
|
"undici": "^5.25.4",
|
|
99
|
-
"url-join": "^4.0.0"
|
|
100
|
-
"xuid": "^4.1.2"
|
|
94
|
+
"url-join": "^4.0.0"
|
|
101
95
|
},
|
|
102
96
|
"devDependencies": {
|
|
103
97
|
"@types/node": "^20.8.2",
|
package/lib/undici/index.js
DELETED
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
const assert = require('assert')
|
|
2
|
-
const createError = require('http-errors')
|
|
3
|
-
const xuid = require('xuid')
|
|
4
|
-
const undici = require('undici')
|
|
5
|
-
const stream = require('stream')
|
|
6
|
-
const { parseHeaders } = require('../../http')
|
|
7
|
-
|
|
8
|
-
class Readable extends stream.Readable {
|
|
9
|
-
constructor({ statusCode, statusMessage, headers, size, ...opts }) {
|
|
10
|
-
super(opts)
|
|
11
|
-
this.statusCode = statusCode
|
|
12
|
-
this.statusMessage = statusMessage
|
|
13
|
-
this.headers = headers
|
|
14
|
-
this.body = this
|
|
15
|
-
this.size = size
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async text() {
|
|
19
|
-
const dec = new TextDecoder()
|
|
20
|
-
let str = ''
|
|
21
|
-
for await (const chunk of this) {
|
|
22
|
-
if (typeof chunk === 'string') {
|
|
23
|
-
str += chunk
|
|
24
|
-
} else {
|
|
25
|
-
str += dec.decode(chunk, { stream: true })
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
// Flush the streaming TextDecoder so that any pending
|
|
29
|
-
// incomplete multibyte characters are handled.
|
|
30
|
-
str += dec.decode(undefined, { stream: false })
|
|
31
|
-
return str
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async json() {
|
|
35
|
-
return JSON.parse(await this.text())
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async arrayBuffer() {
|
|
39
|
-
const buffers = []
|
|
40
|
-
for await (const chunk of this) {
|
|
41
|
-
buffers.push(chunk)
|
|
42
|
-
}
|
|
43
|
-
return Buffer.concat(buffers)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async buffer() {
|
|
47
|
-
return Buffer.from(await this.arrayBuffer())
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async dump() {
|
|
51
|
-
let n = 0
|
|
52
|
-
try {
|
|
53
|
-
for await (const chunk of this) {
|
|
54
|
-
// do nothing
|
|
55
|
-
n += chunk.length
|
|
56
|
-
if (n > 128 * 1024) {
|
|
57
|
-
break
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
} catch {
|
|
61
|
-
this.destroy()
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const dispatchers = {
|
|
67
|
-
abort: require('./interceptor/abort.js'),
|
|
68
|
-
catch: require('./interceptor/catch.js'),
|
|
69
|
-
content: require('./interceptor/content.js'),
|
|
70
|
-
log: require('./interceptor/log.js'),
|
|
71
|
-
redirect: require('./interceptor/redirect.js'),
|
|
72
|
-
responseBodyRetry: require('./interceptor/response-body-retry.js'),
|
|
73
|
-
responseStatusRetry: require('./interceptor/response-status-retry.js'),
|
|
74
|
-
responseRetry: require('./interceptor/response-retry.js'),
|
|
75
|
-
signal: require('./interceptor/signal.js'),
|
|
76
|
-
proxy: require('./interceptor/proxy.js'),
|
|
77
|
-
cache: require('./interceptor/cache.js'),
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
async function request(url, opts) {
|
|
81
|
-
if (typeof url === 'string') {
|
|
82
|
-
url = new URL(url)
|
|
83
|
-
} else if (url instanceof URL) {
|
|
84
|
-
// Do nothing...
|
|
85
|
-
} else if (typeof url.origin === 'string' && typeof (url.path ?? url.pathname) === 'string') {
|
|
86
|
-
// Do nothing...
|
|
87
|
-
} else {
|
|
88
|
-
throw new Error('missing url')
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (opts == null && typeof url === 'object' && url != null) {
|
|
92
|
-
opts = url
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const method = opts.method ?? (opts.body ? 'POST' : 'GET')
|
|
96
|
-
const idempotent = opts.idempotent ?? (method === 'GET' || method === 'HEAD')
|
|
97
|
-
|
|
98
|
-
let headers
|
|
99
|
-
if (Array.isArray(opts.headers)) {
|
|
100
|
-
headers = parseHeaders(opts.headers)
|
|
101
|
-
} else {
|
|
102
|
-
headers = opts.headers
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (method === 'CONNECT') {
|
|
106
|
-
throw new createError.MethodNotAllowed()
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (
|
|
110
|
-
(method === 'HEAD' || method === 'GET') &&
|
|
111
|
-
(parseInt(headers['content-length']) > 0 || headers['transfer-encoding'])
|
|
112
|
-
) {
|
|
113
|
-
throw new createError.BadRequest('HEAD and GET cannot have body')
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
opts = {
|
|
117
|
-
url,
|
|
118
|
-
method,
|
|
119
|
-
body: opts.body,
|
|
120
|
-
headers: {
|
|
121
|
-
'request-id': xuid(),
|
|
122
|
-
'user-agent': opts.userAgent ?? globalThis.userAgent,
|
|
123
|
-
...headers,
|
|
124
|
-
},
|
|
125
|
-
origin: url.origin,
|
|
126
|
-
path: url.path ? url.path : url.search ? `${url.pathname}${url.search ?? ''}` : url.pathname,
|
|
127
|
-
reset: opts.reset ?? false,
|
|
128
|
-
headersTimeout: opts.headersTimeout,
|
|
129
|
-
bodyTimeout: opts.bodyTimeout,
|
|
130
|
-
idempotent,
|
|
131
|
-
signal: opts.signal,
|
|
132
|
-
retry: opts.retry ?? 8,
|
|
133
|
-
proxy: opts.proxy,
|
|
134
|
-
cache: opts.cache,
|
|
135
|
-
upgrade: opts.upgrade,
|
|
136
|
-
follow: { count: opts.maxRedirections ?? 8, ...opts.redirect, ...opts.follow },
|
|
137
|
-
logger: opts.logger,
|
|
138
|
-
maxRedirections: 0, // Disable undici's redirect handling.
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const expectsPayload = opts.method === 'PUT' || opts.method === 'POST' || opts.method === 'PATCH'
|
|
142
|
-
|
|
143
|
-
if (opts.headers['content-length'] === '0' && !expectsPayload) {
|
|
144
|
-
// https://tools.ietf.org/html/rfc7230#section-3.3.2
|
|
145
|
-
// A user agent SHOULD NOT send a Content-Length header field when
|
|
146
|
-
// the request message does not contain a payload body and the method
|
|
147
|
-
// semantics do not anticipate such a body.
|
|
148
|
-
|
|
149
|
-
// undici will error if provided an unexpected content-length: 0 header.
|
|
150
|
-
delete opts.headers['content-length']
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const dispatcher = opts.dispatcher ?? undici.getGlobalDispatcher()
|
|
154
|
-
|
|
155
|
-
return new Promise((resolve) => {
|
|
156
|
-
let dispatch = (opts, handler) => dispatcher.dispatch(opts, handler)
|
|
157
|
-
|
|
158
|
-
dispatch = dispatchers.catch(dispatch)
|
|
159
|
-
dispatch = dispatchers.abort(dispatch)
|
|
160
|
-
dispatch = dispatchers.log(dispatch)
|
|
161
|
-
dispatch = opts.upgrade ? dispatch : dispatchers.responseRetry(dispatch)
|
|
162
|
-
dispatch = opts.upgrade ? dispatch : dispatchers.responseStatusRetry(dispatch)
|
|
163
|
-
dispatch = opts.upgrade ? dispatch : dispatchers.responseBodyRetry(dispatch)
|
|
164
|
-
dispatch = opts.upgrade ? dispatch : dispatchers.content(dispatch)
|
|
165
|
-
dispatch = dispatchers.redirect(dispatch)
|
|
166
|
-
dispatch = dispatchers.signal(dispatch)
|
|
167
|
-
dispatch = opts.upgrade ? dispatch : dispatchers.cache(dispatch)
|
|
168
|
-
dispatch = dispatchers.proxy(dispatch)
|
|
169
|
-
|
|
170
|
-
dispatch(opts, {
|
|
171
|
-
resolve,
|
|
172
|
-
logger: opts.logger,
|
|
173
|
-
/** @type {Function | null} */ abort: null,
|
|
174
|
-
/** @type {stream.Readable | null} */ body: null,
|
|
175
|
-
onConnect(abort) {
|
|
176
|
-
this.abort = abort
|
|
177
|
-
},
|
|
178
|
-
onUpgrade(statusCode, rawHeaders, socket) {
|
|
179
|
-
const headers = parseHeaders(rawHeaders)
|
|
180
|
-
|
|
181
|
-
if (statusCode !== 101) {
|
|
182
|
-
this.abort(createError(statusCode, { headers }))
|
|
183
|
-
} else {
|
|
184
|
-
this.resolve({ headers, socket })
|
|
185
|
-
}
|
|
186
|
-
},
|
|
187
|
-
onHeaders(statusCode, rawHeaders, resume, statusMessage) {
|
|
188
|
-
assert(this.abort)
|
|
189
|
-
|
|
190
|
-
const headers = parseHeaders(rawHeaders)
|
|
191
|
-
|
|
192
|
-
if (statusCode >= 400) {
|
|
193
|
-
this.abort(createError(statusCode, { headers }))
|
|
194
|
-
} else {
|
|
195
|
-
assert(statusCode >= 200)
|
|
196
|
-
|
|
197
|
-
const contentLength = Number(headers['content-length'] ?? headers['Content-Length'])
|
|
198
|
-
|
|
199
|
-
this.body = new Readable({
|
|
200
|
-
read: resume,
|
|
201
|
-
highWaterMark: 128 * 1024,
|
|
202
|
-
statusCode,
|
|
203
|
-
statusMessage,
|
|
204
|
-
headers,
|
|
205
|
-
size: Number.isFinite(contentLength) ? contentLength : null,
|
|
206
|
-
}).on('error', (err) => {
|
|
207
|
-
if (this.logger && this.body?.listenerCount('error') === 1) {
|
|
208
|
-
this.logger.error({ err }, 'unhandled response body error')
|
|
209
|
-
}
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
this.resolve(this.body)
|
|
213
|
-
this.resolve = null
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return false
|
|
217
|
-
},
|
|
218
|
-
onData(chunk) {
|
|
219
|
-
assert(this.body)
|
|
220
|
-
return this.body.push(chunk)
|
|
221
|
-
},
|
|
222
|
-
onComplete() {
|
|
223
|
-
assert(this.body)
|
|
224
|
-
this.body.push(null)
|
|
225
|
-
},
|
|
226
|
-
onError(err) {
|
|
227
|
-
if (this.body) {
|
|
228
|
-
this.body.destroy(err)
|
|
229
|
-
} else {
|
|
230
|
-
this.resolve(Promise.reject(err))
|
|
231
|
-
}
|
|
232
|
-
},
|
|
233
|
-
})
|
|
234
|
-
})
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
module.exports = { request }
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
const { AbortError } = require('../../../errors')
|
|
2
|
-
|
|
3
|
-
class Handler {
|
|
4
|
-
constructor(opts, { handler }) {
|
|
5
|
-
this.handler = handler
|
|
6
|
-
this.pos = 0
|
|
7
|
-
this.reason = null
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
onConnect(abort) {
|
|
11
|
-
this.abort = abort
|
|
12
|
-
this.handler.onConnect?.((reason) => {
|
|
13
|
-
this.reason = reason ?? new AbortError()
|
|
14
|
-
})
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
onBodySent(chunk) {
|
|
18
|
-
return this.handler.onBodySent?.(chunk)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
onUpgrade(statusCode, rawHeaders, socket) {
|
|
22
|
-
return this.handler.onUpgrade?.(statusCode, rawHeaders, socket)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
onHeaders(statusCode, rawHeaders, resume, statusMessage) {
|
|
26
|
-
if (this.reason == null) {
|
|
27
|
-
const ret = this.handler.onHeaders?.(statusCode, rawHeaders, resume, statusMessage)
|
|
28
|
-
if (this.reason == null) {
|
|
29
|
-
return ret
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return true
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
onData(chunk) {
|
|
37
|
-
if (this.reason == null) {
|
|
38
|
-
const ret = this.handler.onData?.(chunk)
|
|
39
|
-
if (this.reason == null) {
|
|
40
|
-
return ret
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
this.pos += chunk.length
|
|
45
|
-
if (this.pos < 128 * 1024) {
|
|
46
|
-
return true
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
this.abort(this.reason)
|
|
50
|
-
|
|
51
|
-
return false
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
onComplete(rawTrailers) {
|
|
55
|
-
return this.reason == null
|
|
56
|
-
? this.handler.onComplete?.(rawTrailers)
|
|
57
|
-
: this.handler.onError?.(this.reason)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
onError(err) {
|
|
61
|
-
return this.handler.onError?.(err)
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
module.exports = (dispatch) => (opts, handler) => dispatch(opts, new Handler(opts, { handler }))
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
const assert = require('node:assert')
|
|
2
|
-
const { LRUCache } = require('lru-cache')
|
|
3
|
-
const cacheControlParser = require('cache-control-parser')
|
|
4
|
-
|
|
5
|
-
class CacheHandler {
|
|
6
|
-
constructor({ key, handler, store }) {
|
|
7
|
-
this.key = key
|
|
8
|
-
this.handler = handler
|
|
9
|
-
this.store = store
|
|
10
|
-
this.value = null
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
onConnect(abort) {
|
|
14
|
-
return this.handler.onConnect(abort)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
onUpgrade(statusCode, rawHeaders, socket) {
|
|
18
|
-
return this.handler.onUpgrade?.(statusCode, rawHeaders, socket)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
onHeaders(statusCode, rawHeaders, resume, statusMessage) {
|
|
22
|
-
// NOTE: Only cache 307 respones for now...
|
|
23
|
-
if (statusCode !== 307) {
|
|
24
|
-
return this.handler.onHeaders(statusCode, rawHeaders, resume, statusMessage)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let cacheControl
|
|
28
|
-
for (let n = 0; n < rawHeaders.length; n += 2) {
|
|
29
|
-
if (
|
|
30
|
-
rawHeaders[n].length === 'cache-control'.length &&
|
|
31
|
-
rawHeaders[n].toString().toLowerCase() === 'cache-control'
|
|
32
|
-
) {
|
|
33
|
-
cacheControl = cacheControlParser.parse(rawHeaders[n + 1].toString())
|
|
34
|
-
break
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (
|
|
39
|
-
cacheControl &&
|
|
40
|
-
cacheControl.public &&
|
|
41
|
-
!cacheControl.private &&
|
|
42
|
-
!cacheControl['no-store'] &&
|
|
43
|
-
// TODO (fix): Support all cache control directives...
|
|
44
|
-
// !opts.headers['no-transform'] &&
|
|
45
|
-
!cacheControl['no-cache'] &&
|
|
46
|
-
!cacheControl['must-understand'] &&
|
|
47
|
-
!cacheControl['must-revalidate'] &&
|
|
48
|
-
!cacheControl['proxy-revalidate']
|
|
49
|
-
) {
|
|
50
|
-
const maxAge = cacheControl['s-max-age'] ?? cacheControl['max-age']
|
|
51
|
-
const ttl = cacheControl.immutable
|
|
52
|
-
? 31556952 // 1 year
|
|
53
|
-
: Number(maxAge)
|
|
54
|
-
|
|
55
|
-
if (ttl > 0) {
|
|
56
|
-
this.value = {
|
|
57
|
-
statusCode,
|
|
58
|
-
statusMessage,
|
|
59
|
-
rawHeaders,
|
|
60
|
-
rawTrailers: null,
|
|
61
|
-
body: [],
|
|
62
|
-
size: 0,
|
|
63
|
-
ttl: ttl * 1e3,
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return this.handler.onHeaders(statusCode, rawHeaders, resume, statusMessage)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
onData(chunk) {
|
|
72
|
-
if (this.value) {
|
|
73
|
-
this.value.size += chunk.bodyLength
|
|
74
|
-
if (this.value.size > this.store.maxEntrySize) {
|
|
75
|
-
this.value = null
|
|
76
|
-
} else {
|
|
77
|
-
this.value.body.push(chunk)
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return this.handler.onData(chunk)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
onComplete(rawTrailers) {
|
|
84
|
-
if (this.value) {
|
|
85
|
-
this.value.rawTrailers = rawTrailers
|
|
86
|
-
this.store.set(this.key, this.value, this.value.ttl)
|
|
87
|
-
}
|
|
88
|
-
return this.handler.onComplete(rawTrailers)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
onError(err) {
|
|
92
|
-
return this.handler.onError(err)
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// TODO (fix): Async filesystem cache.
|
|
97
|
-
class CacheStore {
|
|
98
|
-
constructor({ maxSize, maxEntrySize }) {
|
|
99
|
-
this.maxSize = maxSize
|
|
100
|
-
this.maxEntrySize = maxEntrySize
|
|
101
|
-
this.cache = new LRUCache({
|
|
102
|
-
maxSize,
|
|
103
|
-
sizeCalculation: (value) => value.body.byteLength,
|
|
104
|
-
})
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
set(key, value, ttl) {
|
|
108
|
-
this.cache.set(key, value, ttl)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
get(key) {
|
|
112
|
-
return this.cache.get(key)
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function makeKey(opts) {
|
|
117
|
-
// NOTE: Ignores headers...
|
|
118
|
-
return `${opts.method}:${opts.path}`
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const DEFAULT_CACHE_STORE = new CacheStore({ maxSize: 128 * 1024, maxEntrySize: 1024 })
|
|
122
|
-
|
|
123
|
-
module.exports = (dispatch) => (opts, handler) => {
|
|
124
|
-
if (!opts.cache) {
|
|
125
|
-
return dispatch(opts, handler)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (opts.method !== 'GET' && opts.method !== 'HEAD') {
|
|
129
|
-
dispatch(opts, handler)
|
|
130
|
-
return
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (opts.headers?.['cache-control'] || opts.headers?.authorization) {
|
|
134
|
-
// TODO (fix): Support all cache control directives...
|
|
135
|
-
// const cacheControl = cacheControlParser.parse(opts.headers['cache-control'])
|
|
136
|
-
// cacheControl['no-cache']
|
|
137
|
-
// cacheControl['no-store']
|
|
138
|
-
// cacheControl['max-age']
|
|
139
|
-
// cacheControl['max-stale']
|
|
140
|
-
// cacheControl['min-fresh']
|
|
141
|
-
// cacheControl['no-transform']
|
|
142
|
-
// cacheControl['only-if-cached']
|
|
143
|
-
dispatch(opts, handler)
|
|
144
|
-
return
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// TODO (fix): Support body...
|
|
148
|
-
assert(opts.method === 'GET' || opts.method === 'HEAD')
|
|
149
|
-
|
|
150
|
-
// Dump body...
|
|
151
|
-
opts.body?.on('error', () => {}).resume()
|
|
152
|
-
|
|
153
|
-
const store = opts.cache === true ? DEFAULT_CACHE_STORE : opts.cache
|
|
154
|
-
|
|
155
|
-
if (!store) {
|
|
156
|
-
throw new Error(`Cache store not provided.`)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
let key = makeKey(opts)
|
|
160
|
-
let value = store.get(key)
|
|
161
|
-
|
|
162
|
-
if (value == null && opts.method === 'HEAD') {
|
|
163
|
-
key = makeKey({ ...opts, method: 'GET' })
|
|
164
|
-
value = store.get(key)
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (value) {
|
|
168
|
-
const { statusCode, statusMessage, rawHeaders, rawTrailers, body } = value
|
|
169
|
-
const ac = new AbortController()
|
|
170
|
-
const signal = ac.signal
|
|
171
|
-
|
|
172
|
-
const resume = () => {}
|
|
173
|
-
const abort = () => {
|
|
174
|
-
ac.abort()
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
try {
|
|
178
|
-
handler.onConnect(abort)
|
|
179
|
-
signal.throwIfAborted()
|
|
180
|
-
handler.onHeaders(statusCode, rawHeaders, resume, statusMessage)
|
|
181
|
-
signal.throwIfAborted()
|
|
182
|
-
if (opts.method !== 'HEAD') {
|
|
183
|
-
for (const chunk of body) {
|
|
184
|
-
const ret = handler.onData(chunk)
|
|
185
|
-
signal.throwIfAborted()
|
|
186
|
-
if (ret === false) {
|
|
187
|
-
// TODO (fix): back pressure...
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
handler.onComplete(rawTrailers)
|
|
191
|
-
signal.throwIfAborted()
|
|
192
|
-
} else {
|
|
193
|
-
handler.onComplete([])
|
|
194
|
-
signal.throwIfAborted()
|
|
195
|
-
}
|
|
196
|
-
} catch (err) {
|
|
197
|
-
handler.onError(err)
|
|
198
|
-
}
|
|
199
|
-
} else {
|
|
200
|
-
dispatch(opts, new CacheHandler({ handler, store, key: makeKey(opts) }))
|
|
201
|
-
}
|
|
202
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
class Handler {
|
|
2
|
-
constructor(opts, { handler }) {
|
|
3
|
-
this.handler = handler
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
onConnect(abort) {
|
|
7
|
-
this.abort = abort
|
|
8
|
-
try {
|
|
9
|
-
return this.handler.onConnect?.(abort)
|
|
10
|
-
} catch (err) {
|
|
11
|
-
this.abort(err)
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
onUpgrade(statusCode, rawHeaders, socket) {
|
|
16
|
-
try {
|
|
17
|
-
return this.handler.onUpgrade?.(statusCode, rawHeaders, socket)
|
|
18
|
-
} catch (err) {
|
|
19
|
-
this.abort(err)
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
onBodySent(chunk) {
|
|
24
|
-
try {
|
|
25
|
-
return this.handler.onBodySent?.(chunk)
|
|
26
|
-
} catch (err) {
|
|
27
|
-
this.abort(err)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
onHeaders(statusCode, rawHeaders, resume, statusMessage) {
|
|
32
|
-
try {
|
|
33
|
-
return this.handler.onHeaders?.(statusCode, rawHeaders, resume, statusMessage)
|
|
34
|
-
} catch (err) {
|
|
35
|
-
this.abort(err)
|
|
36
|
-
return false
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
onData(chunk) {
|
|
41
|
-
try {
|
|
42
|
-
return this.handler.onData?.(chunk)
|
|
43
|
-
} catch (err) {
|
|
44
|
-
this.abort(err)
|
|
45
|
-
return false
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
onComplete(rawTrailers) {
|
|
50
|
-
try {
|
|
51
|
-
return this.handler.onComplete?.(rawTrailers)
|
|
52
|
-
} catch (err) {
|
|
53
|
-
this.abort(err)
|
|
54
|
-
return false
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
onError(err) {
|
|
59
|
-
return this.handler.onError?.(err)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
module.exports = (dispatch) => (opts, handler) => dispatch(opts, new Handler(opts, { handler }))
|