@nxtedition/nxt-undici 2.0.14 → 2.0.15
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/interceptor/redirect.js +48 -39
- package/package.json +1 -1
|
@@ -4,16 +4,34 @@ import { findHeader, isDisturbed, parseURL } from '../utils.js'
|
|
|
4
4
|
const redirectableStatusCodes = [300, 301, 302, 303, 307, 308]
|
|
5
5
|
|
|
6
6
|
class Handler {
|
|
7
|
-
constructor(opts, { dispatch, handler
|
|
7
|
+
constructor(opts, { dispatch, handler }) {
|
|
8
8
|
this.dispatch = dispatch
|
|
9
9
|
this.handler = handler
|
|
10
10
|
this.opts = opts
|
|
11
|
-
this.
|
|
12
|
-
this.
|
|
11
|
+
this.abort = null
|
|
12
|
+
this.aborted = false
|
|
13
|
+
this.reason = null
|
|
14
|
+
this.maxCount = Number.isFinite(opts.follow) ? opts.follow : opts.follow?.count ?? 0
|
|
15
|
+
|
|
16
|
+
this.count = 0
|
|
17
|
+
this.location = null
|
|
18
|
+
|
|
19
|
+
this.handler.onConnect((reason) => {
|
|
20
|
+
this.aborted = true
|
|
21
|
+
if (this.abort) {
|
|
22
|
+
this.abort(reason)
|
|
23
|
+
} else {
|
|
24
|
+
this.reason = reason
|
|
25
|
+
}
|
|
26
|
+
})
|
|
13
27
|
}
|
|
14
28
|
|
|
15
29
|
onConnect(abort) {
|
|
16
|
-
|
|
30
|
+
if (this.aborted) {
|
|
31
|
+
abort(this.reason)
|
|
32
|
+
} else {
|
|
33
|
+
this.abort = abort
|
|
34
|
+
}
|
|
17
35
|
}
|
|
18
36
|
|
|
19
37
|
onUpgrade(statusCode, headers, socket) {
|
|
@@ -33,42 +51,37 @@ class Handler {
|
|
|
33
51
|
return this.handler.onHeaders(statusCode, headers, resume, statusText)
|
|
34
52
|
}
|
|
35
53
|
|
|
36
|
-
|
|
54
|
+
if (isDisturbed(this.opts.body)) {
|
|
55
|
+
throw new Error(`Disturbed request cannot be redirected.`)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
this.location = findHeader(headers, 'location')
|
|
37
59
|
|
|
38
|
-
if (!location) {
|
|
39
|
-
// TODO (perf): Consume body?
|
|
60
|
+
if (!this.location) {
|
|
40
61
|
throw new Error(`Missing redirection location .`)
|
|
41
62
|
}
|
|
42
63
|
|
|
43
64
|
this.count += 1
|
|
44
65
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
:
|
|
52
|
-
|
|
53
|
-
if (this.count >= maxCount) {
|
|
54
|
-
// TODO (perf): Consume body?
|
|
55
|
-
throw new Error(`Max redirections reached: ${maxCount}.`)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (isDisturbed(this.opts.body)) {
|
|
59
|
-
// TODO (perf): Consume body?
|
|
60
|
-
throw new Error(`Disturbed request cannot be redirected.`)
|
|
66
|
+
if (typeof this.opts.follow === 'function') {
|
|
67
|
+
if (!this.opts.follow(this.location, this.count)) {
|
|
68
|
+
return this.handler.onHeaders(statusCode, headers, resume, statusText)
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
if (this.count >= this.maxCount) {
|
|
72
|
+
throw new Error(`Max redirections reached: ${this.maxCount}.`)
|
|
73
|
+
}
|
|
61
74
|
}
|
|
62
75
|
|
|
63
76
|
const { origin, pathname, search } = parseURL(
|
|
64
|
-
new URL(location, this.opts.origin && new URL(this.opts.path, this.opts.origin)),
|
|
77
|
+
new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)),
|
|
65
78
|
)
|
|
66
79
|
const path = search ? `${pathname}${search}` : pathname
|
|
67
80
|
|
|
68
81
|
// Remove headers referring to the original URL.
|
|
69
82
|
// By default it is Host only, unless it's a 303 (see below), which removes also all Content-* headers.
|
|
70
83
|
// https://tools.ietf.org/html/rfc7231#section-6.4
|
|
71
|
-
this.
|
|
84
|
+
this.opts = {
|
|
72
85
|
...this.opts,
|
|
73
86
|
headers: cleanRequestHeaders(
|
|
74
87
|
this.opts.headers,
|
|
@@ -77,17 +90,18 @@ class Handler {
|
|
|
77
90
|
),
|
|
78
91
|
path,
|
|
79
92
|
origin,
|
|
93
|
+
query: null,
|
|
80
94
|
}
|
|
81
95
|
|
|
82
96
|
// https://tools.ietf.org/html/rfc7231#section-6.4.4
|
|
83
97
|
// In case of HTTP 303, always replace method to be either HEAD or GET
|
|
84
|
-
if (statusCode === 303 && this.
|
|
85
|
-
this.
|
|
98
|
+
if (statusCode === 303 && this.opts.method !== 'HEAD') {
|
|
99
|
+
this.opts = { ...this.opts, method: 'GET', body: null }
|
|
86
100
|
}
|
|
87
101
|
}
|
|
88
102
|
|
|
89
103
|
onData(chunk) {
|
|
90
|
-
if (this.
|
|
104
|
+
if (this.location) {
|
|
91
105
|
/*
|
|
92
106
|
https://tools.ietf.org/html/rfc7231#section-6.4
|
|
93
107
|
|
|
@@ -111,7 +125,7 @@ class Handler {
|
|
|
111
125
|
}
|
|
112
126
|
|
|
113
127
|
onComplete(trailers) {
|
|
114
|
-
if (this.
|
|
128
|
+
if (this.location) {
|
|
115
129
|
/*
|
|
116
130
|
https://tools.ietf.org/html/rfc7231#section-6.4
|
|
117
131
|
|
|
@@ -120,15 +134,10 @@ class Handler {
|
|
|
120
134
|
|
|
121
135
|
See comment on onData method above for more detailed informations.
|
|
122
136
|
*/
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
dispatch: this.dispatch,
|
|
128
|
-
count: this.count,
|
|
129
|
-
}),
|
|
130
|
-
)
|
|
131
|
-
this.handler = null
|
|
137
|
+
|
|
138
|
+
this.location = null
|
|
139
|
+
|
|
140
|
+
this.dispatch(this.opts, this)
|
|
132
141
|
} else {
|
|
133
142
|
return this.handler.onComplete(trailers)
|
|
134
143
|
}
|
|
@@ -169,5 +178,5 @@ function cleanRequestHeaders(headers, removeContent, unknownOrigin) {
|
|
|
169
178
|
|
|
170
179
|
export default (dispatch) => (opts, handler) =>
|
|
171
180
|
opts.follow != null
|
|
172
|
-
? dispatch(opts, new Handler(opts, { handler, dispatch
|
|
181
|
+
? dispatch(opts, new Handler(opts, { handler, dispatch }))
|
|
173
182
|
: dispatch(opts, handler)
|