@nxtedition/nxt-undici 2.0.46 → 2.0.48
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/lib/index.js +29 -22
- package/lib/interceptor/cache.js +30 -29
- package/lib/interceptor/log.js +58 -49
- package/lib/interceptor/proxy.js +16 -28
- package/lib/interceptor/redirect.js +63 -56
- package/lib/interceptor/request-id.js +1 -1
- package/lib/interceptor/response-content.js +48 -31
- package/lib/interceptor/response-error.js +67 -76
- package/lib/interceptor/response-retry.js +186 -80
- package/lib/utils.js +36 -33
- package/package.json +1 -1
- package/lib/interceptor/response-retry-body.js +0 -193
package/lib/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import assert from 'node:assert'
|
|
2
2
|
import createError from 'http-errors'
|
|
3
3
|
import undici from 'undici'
|
|
4
|
-
import {
|
|
5
|
-
import { BodyReadable
|
|
4
|
+
import { parseHeaders, AbortError, headerNameToString, isStream } from './utils.js'
|
|
5
|
+
import { BodyReadable } from './readable.js'
|
|
6
6
|
|
|
7
7
|
const dispatcherCache = new WeakMap()
|
|
8
8
|
|
|
@@ -19,14 +19,13 @@ function genReqId() {
|
|
|
19
19
|
return `req-${nextReqId.toString(36)}`
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
const
|
|
22
|
+
export const interceptors = {
|
|
23
23
|
responseError: (await import('./interceptor/response-error.js')).default,
|
|
24
24
|
requestBodyFactory: (await import('./interceptor/request-body-factory.js')).default,
|
|
25
25
|
responseContent: (await import('./interceptor/response-content.js')).default,
|
|
26
26
|
log: (await import('./interceptor/log.js')).default,
|
|
27
27
|
redirect: (await import('./interceptor/redirect.js')).default,
|
|
28
28
|
responseRetry: (await import('./interceptor/response-retry.js')).default,
|
|
29
|
-
responseRetryBody: (await import('./interceptor/response-retry-body.js')).default,
|
|
30
29
|
proxy: (await import('./interceptor/proxy.js')).default,
|
|
31
30
|
cache: (await import('./interceptor/cache.js')).default,
|
|
32
31
|
requestId: (await import('./interceptor/request-id.js')).default,
|
|
@@ -62,17 +61,18 @@ export async function request(url, opts) {
|
|
|
62
61
|
const method = opts.method ?? (opts.body ? 'POST' : 'GET')
|
|
63
62
|
const idempotent = opts.idempotent ?? (method === 'GET' || method === 'HEAD')
|
|
64
63
|
|
|
65
|
-
let headers
|
|
64
|
+
let headers = {}
|
|
66
65
|
if (Array.isArray(opts.headers)) {
|
|
67
66
|
headers = parseHeaders(opts.headers)
|
|
68
67
|
} else if (opts.headers != null) {
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
for (const [key, val] of Object.entries(opts.headers)) {
|
|
69
|
+
headers[headerNameToString(key)] = val
|
|
70
|
+
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
const userAgent = opts.userAgent ?? globalThis.userAgent
|
|
74
74
|
if (userAgent && headers?.['user-agent'] !== userAgent) {
|
|
75
|
-
headers
|
|
75
|
+
headers['user-agent'] = userAgent
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
if (method === 'CONNECT') {
|
|
@@ -87,6 +87,14 @@ export async function request(url, opts) {
|
|
|
87
87
|
throw new createError.BadRequest('HEAD and GET cannot have body')
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
if (
|
|
91
|
+
opts.body != null &&
|
|
92
|
+
(opts.body.size > 0 || opts.body.length > 0) &&
|
|
93
|
+
(method === 'HEAD' || method === 'GET')
|
|
94
|
+
) {
|
|
95
|
+
throw new createError.BadRequest('HEAD and GET cannot have body')
|
|
96
|
+
}
|
|
97
|
+
|
|
90
98
|
const expectsPayload = opts.method === 'PUT' || opts.method === 'POST' || opts.method === 'PATCH'
|
|
91
99
|
|
|
92
100
|
if (headers != null && headers['content-length'] === '0' && !expectsPayload) {
|
|
@@ -110,23 +118,22 @@ export async function request(url, opts) {
|
|
|
110
118
|
let dispatch = dispatcherCache.get(dispatcher)
|
|
111
119
|
if (dispatch == null) {
|
|
112
120
|
dispatch = (opts, handler) => dispatcher.dispatch(opts, handler)
|
|
113
|
-
dispatch =
|
|
114
|
-
dispatch =
|
|
115
|
-
dispatch =
|
|
116
|
-
dispatch =
|
|
117
|
-
dispatch =
|
|
118
|
-
dispatch =
|
|
119
|
-
dispatch =
|
|
120
|
-
dispatch =
|
|
121
|
-
dispatch =
|
|
122
|
-
dispatch = dispatchers.proxy(dispatch)
|
|
121
|
+
dispatch = interceptors.responseError(dispatch)
|
|
122
|
+
dispatch = interceptors.requestBodyFactory(dispatch)
|
|
123
|
+
dispatch = interceptors.log(dispatch)
|
|
124
|
+
dispatch = interceptors.requestId(dispatch)
|
|
125
|
+
dispatch = interceptors.responseRetry(dispatch)
|
|
126
|
+
dispatch = interceptors.responseContent(dispatch)
|
|
127
|
+
dispatch = interceptors.cache(dispatch)
|
|
128
|
+
dispatch = interceptors.redirect(dispatch)
|
|
129
|
+
dispatch = interceptors.proxy(dispatch)
|
|
123
130
|
dispatcherCache.set(dispatcher, dispatch)
|
|
124
131
|
}
|
|
125
132
|
|
|
126
133
|
return await new Promise((resolve, reject) =>
|
|
127
134
|
dispatch(
|
|
128
135
|
{
|
|
129
|
-
id: opts.id ??
|
|
136
|
+
id: opts.id ?? headers['request-id'] ?? headers['Request-Id'] ?? genReqId(),
|
|
130
137
|
url,
|
|
131
138
|
method,
|
|
132
139
|
body: opts.body,
|
|
@@ -189,10 +196,10 @@ export async function request(url, opts) {
|
|
|
189
196
|
) {
|
|
190
197
|
assert(statusCode >= 200)
|
|
191
198
|
|
|
192
|
-
const contentLength =
|
|
193
|
-
const contentType =
|
|
199
|
+
const contentLength = headers['content-length']
|
|
200
|
+
const contentType = headers['content-type']
|
|
194
201
|
|
|
195
|
-
this.body = new
|
|
202
|
+
this.body = new BodyReadable(this, {
|
|
196
203
|
resume,
|
|
197
204
|
abort: this.abort,
|
|
198
205
|
highWaterMark: this.highWaterMark,
|
package/lib/interceptor/cache.js
CHANGED
|
@@ -1,30 +1,35 @@
|
|
|
1
1
|
import assert from 'node:assert'
|
|
2
2
|
import { LRUCache } from 'lru-cache'
|
|
3
|
-
import {
|
|
3
|
+
import { parseHeaders, parseCacheControl } from '../utils.js'
|
|
4
|
+
import { DecoratorHandler } from 'undici'
|
|
5
|
+
|
|
6
|
+
class CacheHandler extends DecoratorHandler {
|
|
7
|
+
#handler
|
|
8
|
+
#store
|
|
9
|
+
#key
|
|
10
|
+
#value
|
|
4
11
|
|
|
5
|
-
class CacheHandler {
|
|
6
12
|
constructor({ key, handler, store }) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
this
|
|
10
|
-
this
|
|
13
|
+
super(handler)
|
|
14
|
+
|
|
15
|
+
this.#key = key
|
|
16
|
+
this.#handler = handler
|
|
17
|
+
this.#store = store
|
|
11
18
|
}
|
|
12
19
|
|
|
13
20
|
onConnect(abort) {
|
|
14
|
-
|
|
15
|
-
}
|
|
21
|
+
this.#value = null
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
return this.handler.onUpgrade(statusCode, rawHeaders, socket, headers)
|
|
23
|
+
return this.#handler.onConnect(abort)
|
|
19
24
|
}
|
|
20
25
|
|
|
21
|
-
onHeaders(statusCode, rawHeaders, resume, statusMessage, headers) {
|
|
26
|
+
onHeaders(statusCode, rawHeaders, resume, statusMessage, headers = parseHeaders(rawHeaders)) {
|
|
22
27
|
// NOTE: Only cache 307 respones for now...
|
|
23
28
|
if (statusCode !== 307) {
|
|
24
|
-
return this
|
|
29
|
+
return this.#handler.onHeaders(statusCode, rawHeaders, resume, statusMessage, headers)
|
|
25
30
|
}
|
|
26
31
|
|
|
27
|
-
const cacheControl = parseCacheControl(
|
|
32
|
+
const cacheControl = parseCacheControl(headers['cache-control'])
|
|
28
33
|
|
|
29
34
|
if (
|
|
30
35
|
cacheControl &&
|
|
@@ -44,7 +49,7 @@ class CacheHandler {
|
|
|
44
49
|
: Number(maxAge)
|
|
45
50
|
|
|
46
51
|
if (ttl > 0) {
|
|
47
|
-
this
|
|
52
|
+
this.#value = {
|
|
48
53
|
statusCode,
|
|
49
54
|
statusMessage,
|
|
50
55
|
rawHeaders,
|
|
@@ -56,31 +61,27 @@ class CacheHandler {
|
|
|
56
61
|
}
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
return this
|
|
64
|
+
return this.#handler.onHeaders(statusCode, rawHeaders, resume, statusMessage, headers)
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
onData(chunk) {
|
|
63
|
-
if (this
|
|
64
|
-
this
|
|
65
|
-
if (this
|
|
66
|
-
this
|
|
68
|
+
if (this.#value) {
|
|
69
|
+
this.#value.size += chunk.bodyLength
|
|
70
|
+
if (this.#value.size > this.#store.maxEntrySize) {
|
|
71
|
+
this.#value = null
|
|
67
72
|
} else {
|
|
68
|
-
this
|
|
73
|
+
this.#value.body.push(chunk)
|
|
69
74
|
}
|
|
70
75
|
}
|
|
71
|
-
return this
|
|
76
|
+
return this.#handler.onData(chunk)
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
onComplete(rawTrailers) {
|
|
75
|
-
if (this
|
|
76
|
-
this
|
|
77
|
-
this
|
|
80
|
+
if (this.#value) {
|
|
81
|
+
this.#value.rawTrailers = rawTrailers
|
|
82
|
+
this.#store.set(this.#key, this.#value, this.#value.ttl)
|
|
78
83
|
}
|
|
79
|
-
return this
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
onError(err) {
|
|
83
|
-
return this.handler.onError(err)
|
|
84
|
+
return this.#handler.onComplete(rawTrailers)
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
87
|
|
package/lib/interceptor/log.js
CHANGED
|
@@ -1,107 +1,116 @@
|
|
|
1
1
|
import { performance } from 'node:perf_hooks'
|
|
2
2
|
import { parseHeaders } from '../utils.js'
|
|
3
|
+
import { DecoratorHandler } from 'undici'
|
|
4
|
+
|
|
5
|
+
class Handler extends DecoratorHandler {
|
|
6
|
+
#handler
|
|
7
|
+
#opts
|
|
8
|
+
#abort
|
|
9
|
+
#aborted = false
|
|
10
|
+
#logger
|
|
11
|
+
#pos
|
|
12
|
+
#timing
|
|
13
|
+
#startTime
|
|
3
14
|
|
|
4
|
-
class Handler {
|
|
5
15
|
constructor(opts, { handler }) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
this
|
|
9
|
-
this
|
|
10
|
-
this
|
|
11
|
-
this.pos = 0
|
|
12
|
-
this.stats = {
|
|
13
|
-
start: -1,
|
|
14
|
-
end: -1,
|
|
15
|
-
headers: -1,
|
|
16
|
-
firstBodyReceived: -1,
|
|
17
|
-
lastBodyReceived: -1,
|
|
18
|
-
}
|
|
16
|
+
super(handler)
|
|
17
|
+
|
|
18
|
+
this.#handler = handler
|
|
19
|
+
this.#opts = opts
|
|
20
|
+
this.#logger = opts.logger.child({ ureq: { id: opts.id } })
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
onConnect(abort) {
|
|
22
|
-
this
|
|
23
|
-
this
|
|
24
|
-
this
|
|
24
|
+
this.#pos = 0
|
|
25
|
+
this.#abort = abort
|
|
26
|
+
this.#timing = {
|
|
27
|
+
headers: -1,
|
|
28
|
+
data: -1,
|
|
29
|
+
complete: -1,
|
|
30
|
+
error: -1,
|
|
31
|
+
}
|
|
32
|
+
this.#startTime = performance.now()
|
|
33
|
+
|
|
34
|
+
this.#logger.debug({ ureq: this.#opts }, 'upstream request started')
|
|
25
35
|
|
|
26
|
-
return this
|
|
27
|
-
this
|
|
28
|
-
this
|
|
36
|
+
return this.#handler.onConnect((reason) => {
|
|
37
|
+
this.#aborted = true
|
|
38
|
+
this.#abort(reason)
|
|
29
39
|
})
|
|
30
40
|
}
|
|
31
41
|
|
|
32
42
|
onUpgrade(statusCode, rawHeaders, socket, headers) {
|
|
33
|
-
this
|
|
43
|
+
this.#logger.debug('upstream request upgraded')
|
|
34
44
|
socket.on('close', () => {
|
|
35
|
-
this
|
|
45
|
+
this.#logger.debug('upstream request socket closed')
|
|
36
46
|
})
|
|
37
47
|
|
|
38
|
-
return this
|
|
48
|
+
return this.#handler.onUpgrade(statusCode, rawHeaders, socket, headers)
|
|
39
49
|
}
|
|
40
50
|
|
|
41
51
|
onHeaders(statusCode, rawHeaders, resume, statusMessage, headers = parseHeaders(rawHeaders)) {
|
|
42
|
-
this.
|
|
52
|
+
this.#timing.headers = performance.now() - this.#startTime
|
|
43
53
|
|
|
44
|
-
this
|
|
54
|
+
this.#logger.debug(
|
|
45
55
|
{
|
|
46
56
|
ures: { statusCode, headers },
|
|
47
|
-
elapsedTime: this.
|
|
57
|
+
elapsedTime: this.#timing.headers,
|
|
48
58
|
},
|
|
49
59
|
'upstream request response',
|
|
50
60
|
)
|
|
51
61
|
|
|
52
|
-
return this
|
|
62
|
+
return this.#handler.onHeaders(statusCode, rawHeaders, resume, statusMessage, headers)
|
|
53
63
|
}
|
|
54
64
|
|
|
55
65
|
onData(chunk) {
|
|
56
|
-
if (this.
|
|
57
|
-
this.
|
|
66
|
+
if (this.#timing.data === -1) {
|
|
67
|
+
this.#timing.data = performance.now() - this.#startTime
|
|
58
68
|
}
|
|
59
69
|
|
|
60
|
-
this
|
|
70
|
+
this.#pos += chunk.length
|
|
61
71
|
|
|
62
|
-
return this
|
|
72
|
+
return this.#handler.onData(chunk)
|
|
63
73
|
}
|
|
64
74
|
|
|
65
75
|
onComplete(rawTrailers) {
|
|
66
|
-
this.
|
|
67
|
-
this.stats.end = this.stats.lastBodyReceived
|
|
76
|
+
this.#timing.complete = performance.now() - this.#startTime
|
|
68
77
|
|
|
69
|
-
this
|
|
70
|
-
{
|
|
78
|
+
this.#logger.debug(
|
|
79
|
+
{ elapsedTime: this.#timing.complete, bytesRead: this.#pos, timing: this.#timing },
|
|
71
80
|
'upstream request completed',
|
|
72
81
|
)
|
|
73
82
|
|
|
74
|
-
return this
|
|
83
|
+
return this.#handler.onComplete(rawTrailers)
|
|
75
84
|
}
|
|
76
85
|
|
|
77
86
|
onError(err) {
|
|
78
|
-
this.
|
|
87
|
+
this.#timing.error = performance.now() - this.#startTime
|
|
79
88
|
|
|
80
|
-
if (this
|
|
81
|
-
this
|
|
89
|
+
if (this.#aborted) {
|
|
90
|
+
this.#logger.debug(
|
|
82
91
|
{
|
|
83
|
-
ureq: this
|
|
84
|
-
bytesRead: this
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
ureq: this.#opts,
|
|
93
|
+
bytesRead: this.#pos,
|
|
94
|
+
timing: this.#timing,
|
|
95
|
+
elapsedTime: this.#timing.error,
|
|
87
96
|
err,
|
|
88
97
|
},
|
|
89
98
|
'upstream request aborted',
|
|
90
99
|
)
|
|
91
100
|
} else {
|
|
92
|
-
this
|
|
101
|
+
this.#logger.error(
|
|
93
102
|
{
|
|
94
|
-
ureq: this
|
|
95
|
-
bytesRead: this
|
|
96
|
-
|
|
97
|
-
|
|
103
|
+
ureq: this.#opts,
|
|
104
|
+
bytesRead: this.#pos,
|
|
105
|
+
timing: this.#timing,
|
|
106
|
+
elapsedTime: this.#timing.error,
|
|
98
107
|
err,
|
|
99
108
|
},
|
|
100
109
|
'upstream request failed',
|
|
101
110
|
)
|
|
102
111
|
}
|
|
103
112
|
|
|
104
|
-
return this
|
|
113
|
+
return this.#handler.onError(err)
|
|
105
114
|
}
|
|
106
115
|
}
|
|
107
116
|
|
package/lib/interceptor/proxy.js
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
import net from 'node:net'
|
|
2
2
|
import createError from 'http-errors'
|
|
3
|
+
import { DecoratorHandler } from 'undici'
|
|
3
4
|
|
|
4
|
-
class Handler {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
5
|
+
class Handler extends DecoratorHandler {
|
|
6
|
+
#handler
|
|
7
|
+
#opts
|
|
8
|
+
|
|
9
|
+
constructor(proxyOpts, { handler }) {
|
|
10
|
+
super(handler)
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
this.#handler = handler
|
|
13
|
+
this.#opts = proxyOpts
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
onUpgrade(statusCode, rawHeaders, socket) {
|
|
15
|
-
return this
|
|
17
|
+
return this.#handler.onUpgrade(
|
|
16
18
|
statusCode,
|
|
17
19
|
reduceHeaders(
|
|
18
20
|
{
|
|
19
21
|
headers: rawHeaders,
|
|
20
|
-
httpVersion: this
|
|
22
|
+
httpVersion: this.#opts.httpVersion ?? this.#opts.req?.httpVersion,
|
|
21
23
|
socket: null,
|
|
22
|
-
proxyName: this
|
|
24
|
+
proxyName: this.#opts.name,
|
|
23
25
|
},
|
|
24
26
|
(acc, key, val) => {
|
|
25
27
|
acc.push(key, val)
|
|
@@ -32,14 +34,14 @@ class Handler {
|
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
onHeaders(statusCode, rawHeaders, resume, statusMessage) {
|
|
35
|
-
return this
|
|
37
|
+
return this.#handler.onHeaders(
|
|
36
38
|
statusCode,
|
|
37
39
|
reduceHeaders(
|
|
38
40
|
{
|
|
39
41
|
headers: rawHeaders,
|
|
40
|
-
httpVersion: this
|
|
42
|
+
httpVersion: this.#opts.httpVersion ?? this.#opts.req?.httpVersion,
|
|
41
43
|
socket: null,
|
|
42
|
-
proxyName: this
|
|
44
|
+
proxyName: this.#opts.name,
|
|
43
45
|
},
|
|
44
46
|
(acc, key, val) => {
|
|
45
47
|
acc.push(key, val)
|
|
@@ -51,18 +53,6 @@ class Handler {
|
|
|
51
53
|
statusMessage,
|
|
52
54
|
)
|
|
53
55
|
}
|
|
54
|
-
|
|
55
|
-
onData(chunk) {
|
|
56
|
-
return this.handler.onData(chunk)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
onComplete(rawTrailers) {
|
|
60
|
-
return this.handler.onComplete(rawTrailers)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
onError(err) {
|
|
64
|
-
return this.handler.onError(err)
|
|
65
|
-
}
|
|
66
56
|
}
|
|
67
57
|
|
|
68
58
|
export default (dispatch) => (opts, handler) => {
|
|
@@ -84,9 +74,7 @@ export default (dispatch) => (opts, handler) => {
|
|
|
84
74
|
{},
|
|
85
75
|
)
|
|
86
76
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return dispatch(opts, new Handler(opts, { handler }))
|
|
77
|
+
return dispatch({ ...opts, headers }, new Handler(opts.proxy, { handler }))
|
|
90
78
|
}
|
|
91
79
|
|
|
92
80
|
// This expression matches hop-by-hop headers.
|
|
@@ -1,94 +1,101 @@
|
|
|
1
1
|
import assert from 'node:assert'
|
|
2
|
-
import {
|
|
2
|
+
import { isDisturbed, parseHeaders, parseURL } from '../utils.js'
|
|
3
|
+
import { DecoratorHandler } from 'undici'
|
|
3
4
|
|
|
4
5
|
const redirectableStatusCodes = [300, 301, 302, 303, 307, 308]
|
|
5
6
|
|
|
6
|
-
class Handler {
|
|
7
|
+
class Handler extends DecoratorHandler {
|
|
8
|
+
#dispatch
|
|
9
|
+
#handler
|
|
10
|
+
|
|
11
|
+
#opts
|
|
12
|
+
#abort
|
|
13
|
+
#aborted = false
|
|
14
|
+
#reason
|
|
15
|
+
#maxCount
|
|
16
|
+
#headersSent = false
|
|
17
|
+
#count = 0
|
|
18
|
+
#location
|
|
19
|
+
#history = []
|
|
20
|
+
|
|
7
21
|
constructor(opts, { dispatch, handler }) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
this
|
|
11
|
-
this
|
|
12
|
-
this
|
|
13
|
-
this
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
this.location = null
|
|
20
|
-
this.history = []
|
|
21
|
-
|
|
22
|
-
this.handler.onConnect((reason) => {
|
|
23
|
-
this.aborted = true
|
|
24
|
-
if (this.abort) {
|
|
25
|
-
this.abort(reason)
|
|
22
|
+
super(handler)
|
|
23
|
+
|
|
24
|
+
this.#dispatch = dispatch
|
|
25
|
+
this.#handler = handler
|
|
26
|
+
this.#opts = opts
|
|
27
|
+
this.#maxCount = Number.isFinite(opts.follow) ? opts.follow : opts.follow?.count ?? 0
|
|
28
|
+
|
|
29
|
+
this.#handler.onConnect((reason) => {
|
|
30
|
+
this.#aborted = true
|
|
31
|
+
if (this.#abort) {
|
|
32
|
+
this.#abort(reason)
|
|
26
33
|
} else {
|
|
27
|
-
this
|
|
34
|
+
this.#reason = reason
|
|
28
35
|
}
|
|
29
36
|
})
|
|
30
37
|
}
|
|
31
38
|
|
|
32
39
|
onConnect(abort) {
|
|
33
|
-
if (this
|
|
34
|
-
abort(this
|
|
40
|
+
if (this.#aborted) {
|
|
41
|
+
abort(this.#reason)
|
|
35
42
|
} else {
|
|
36
|
-
this
|
|
43
|
+
this.#abort = abort
|
|
37
44
|
}
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
onUpgrade(statusCode, rawHeaders, socket, headers) {
|
|
41
|
-
return this
|
|
48
|
+
return this.#handler.onUpgrade(statusCode, rawHeaders, socket, headers)
|
|
42
49
|
}
|
|
43
50
|
|
|
44
|
-
onHeaders(statusCode, rawHeaders, resume, statusText, headers) {
|
|
51
|
+
onHeaders(statusCode, rawHeaders, resume, statusText, headers = parseHeaders(rawHeaders)) {
|
|
45
52
|
if (redirectableStatusCodes.indexOf(statusCode) === -1) {
|
|
46
|
-
assert(!this
|
|
47
|
-
this
|
|
48
|
-
return this
|
|
53
|
+
assert(!this.#headersSent)
|
|
54
|
+
this.#headersSent = true
|
|
55
|
+
return this.#handler.onHeaders(statusCode, rawHeaders, resume, statusText, headers)
|
|
49
56
|
}
|
|
50
57
|
|
|
51
|
-
if (isDisturbed(this
|
|
58
|
+
if (isDisturbed(this.#opts.body)) {
|
|
52
59
|
throw new Error(`Disturbed request cannot be redirected.`)
|
|
53
60
|
}
|
|
54
61
|
|
|
55
|
-
this
|
|
62
|
+
this.#location = headers.location
|
|
56
63
|
|
|
57
|
-
if (!this
|
|
64
|
+
if (!this.#location) {
|
|
58
65
|
throw new Error(`Missing redirection location .`)
|
|
59
66
|
}
|
|
60
67
|
|
|
61
|
-
this
|
|
62
|
-
this
|
|
68
|
+
this.#history.push(this.#location)
|
|
69
|
+
this.#count += 1
|
|
63
70
|
|
|
64
|
-
if (typeof this
|
|
65
|
-
if (!this
|
|
66
|
-
assert(!this
|
|
67
|
-
this
|
|
68
|
-
return this
|
|
71
|
+
if (typeof this.#opts.follow === 'function') {
|
|
72
|
+
if (!this.#opts.follow(this.#location, this.#count)) {
|
|
73
|
+
assert(!this.#headersSent)
|
|
74
|
+
this.#headersSent = true
|
|
75
|
+
return this.#handler.onHeaders(statusCode, rawHeaders, resume, statusText, headers)
|
|
69
76
|
}
|
|
70
77
|
} else {
|
|
71
|
-
if (this
|
|
72
|
-
throw Object.assign(new Error(`Max redirections reached: ${this
|
|
73
|
-
history: this
|
|
78
|
+
if (this.#count >= this.#maxCount) {
|
|
79
|
+
throw Object.assign(new Error(`Max redirections reached: ${this.#maxCount}.`), {
|
|
80
|
+
history: this.#history,
|
|
74
81
|
})
|
|
75
82
|
}
|
|
76
83
|
}
|
|
77
84
|
|
|
78
85
|
const { origin, pathname, search } = parseURL(
|
|
79
|
-
new URL(this
|
|
86
|
+
new URL(this.#location, this.#opts.origin && new URL(this.#opts.path, this.#opts.origin)),
|
|
80
87
|
)
|
|
81
88
|
const path = search ? `${pathname}${search}` : pathname
|
|
82
89
|
|
|
83
90
|
// Remove headers referring to the original URL.
|
|
84
91
|
// By default it is Host only, unless it's a 303 (see below), which removes also all Content-* headers.
|
|
85
92
|
// https://tools.ietf.org/html/rfc7231#section-6.4
|
|
86
|
-
this
|
|
87
|
-
...this
|
|
93
|
+
this.#opts = {
|
|
94
|
+
...this.#opts,
|
|
88
95
|
headers: cleanRequestHeaders(
|
|
89
|
-
this
|
|
96
|
+
this.#opts.headers,
|
|
90
97
|
statusCode === 303,
|
|
91
|
-
this
|
|
98
|
+
this.#opts.origin !== origin,
|
|
92
99
|
),
|
|
93
100
|
path,
|
|
94
101
|
origin,
|
|
@@ -97,13 +104,13 @@ class Handler {
|
|
|
97
104
|
|
|
98
105
|
// https://tools.ietf.org/html/rfc7231#section-6.4.4
|
|
99
106
|
// In case of HTTP 303, always replace method to be either HEAD or GET
|
|
100
|
-
if (statusCode === 303 && this
|
|
101
|
-
this
|
|
107
|
+
if (statusCode === 303 && this.#opts.method !== 'HEAD') {
|
|
108
|
+
this.#opts = { ...this.#opts, method: 'GET', body: null }
|
|
102
109
|
}
|
|
103
110
|
}
|
|
104
111
|
|
|
105
112
|
onData(chunk) {
|
|
106
|
-
if (this
|
|
113
|
+
if (this.#location) {
|
|
107
114
|
/*
|
|
108
115
|
https://tools.ietf.org/html/rfc7231#section-6.4
|
|
109
116
|
|
|
@@ -122,12 +129,12 @@ class Handler {
|
|
|
122
129
|
servers and browsers implementors, we ignore the body as there is no specified way to eventually parse it.
|
|
123
130
|
*/
|
|
124
131
|
} else {
|
|
125
|
-
return this
|
|
132
|
+
return this.#handler.onData(chunk)
|
|
126
133
|
}
|
|
127
134
|
}
|
|
128
135
|
|
|
129
136
|
onComplete(trailers) {
|
|
130
|
-
if (this
|
|
137
|
+
if (this.#location) {
|
|
131
138
|
/*
|
|
132
139
|
https://tools.ietf.org/html/rfc7231#section-6.4
|
|
133
140
|
|
|
@@ -137,16 +144,16 @@ class Handler {
|
|
|
137
144
|
See comment on onData method above for more detailed informations.
|
|
138
145
|
*/
|
|
139
146
|
|
|
140
|
-
this
|
|
147
|
+
this.#location = null
|
|
141
148
|
|
|
142
|
-
this
|
|
149
|
+
this.#dispatch(this.#opts, this)
|
|
143
150
|
} else {
|
|
144
|
-
return this
|
|
151
|
+
return this.#handler.onComplete(trailers)
|
|
145
152
|
}
|
|
146
153
|
}
|
|
147
154
|
|
|
148
155
|
onError(error) {
|
|
149
|
-
return this
|
|
156
|
+
return this.#handler.onError(error)
|
|
150
157
|
}
|
|
151
158
|
}
|
|
152
159
|
|
|
@@ -12,7 +12,7 @@ function genReqId() {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export default (dispatch) => (opts, handler) => {
|
|
15
|
-
let id = opts.id ?? opts.headers?.['request-id']
|
|
15
|
+
let id = opts.id ?? opts.headers?.['request-id']
|
|
16
16
|
id = id ? `${id},${genReqId()}` : genReqId()
|
|
17
17
|
|
|
18
18
|
return dispatch(
|