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