@nxtedition/nxt-undici 2.0.13 → 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/index.js CHANGED
@@ -278,6 +278,7 @@ export async function request(url, opts) {
278
278
  this.abort(createError(statusCode, { headers }))
279
279
  } else {
280
280
  this.resolve({ headers, socket })
281
+ this.resolve = null
281
282
  }
282
283
  },
283
284
  onBodySent(chunk) {},
@@ -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, count = 0 }) {
7
+ constructor(opts, { dispatch, handler }) {
8
8
  this.dispatch = dispatch
9
9
  this.handler = handler
10
10
  this.opts = opts
11
- this.redirectOpts = null
12
- this.count = count
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
- return this.handler.onConnect(abort)
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
- const location = findHeader(headers, 'location')
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
- // TODO (feat): follow as function...
46
-
47
- const maxCount = Number.isFinite(this.opts.follow)
48
- ? this.opts.follow
49
- : Number.isFinite(this.opts.follow?.count)
50
- ? this.opts.follow?.count
51
- : 0
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.redirectOpts = {
84
+ this.opts = {
72
85
  ...this.opts,
73
86
  headers: cleanRequestHeaders(
74
87
  this.opts.headers,
@@ -77,19 +90,18 @@ class Handler {
77
90
  ),
78
91
  path,
79
92
  origin,
93
+ query: null,
80
94
  }
81
95
 
82
- this.opts.logger?.debug({ location }, 'following redirect')
83
-
84
96
  // https://tools.ietf.org/html/rfc7231#section-6.4.4
85
97
  // In case of HTTP 303, always replace method to be either HEAD or GET
86
- if (statusCode === 303 && this.redirectOpts.method !== 'HEAD') {
87
- this.redirectOpts = { ...this.redirectOpts, method: 'GET', body: null }
98
+ if (statusCode === 303 && this.opts.method !== 'HEAD') {
99
+ this.opts = { ...this.opts, method: 'GET', body: null }
88
100
  }
89
101
  }
90
102
 
91
103
  onData(chunk) {
92
- if (this.redirectOpts) {
104
+ if (this.location) {
93
105
  /*
94
106
  https://tools.ietf.org/html/rfc7231#section-6.4
95
107
 
@@ -113,7 +125,7 @@ class Handler {
113
125
  }
114
126
 
115
127
  onComplete(trailers) {
116
- if (this.redirectOpts) {
128
+ if (this.location) {
117
129
  /*
118
130
  https://tools.ietf.org/html/rfc7231#section-6.4
119
131
 
@@ -122,15 +134,10 @@ class Handler {
122
134
 
123
135
  See comment on onData method above for more detailed informations.
124
136
  */
125
- this.dispatch(
126
- this.redirectOpts,
127
- new Handler(this.redirectOpts, {
128
- handler: this.handler,
129
- dispatch: this.dispatch,
130
- count: this.count,
131
- }),
132
- )
133
- this.handler = null
137
+
138
+ this.location = null
139
+
140
+ this.dispatch(this.opts, this)
134
141
  } else {
135
142
  return this.handler.onComplete(trailers)
136
143
  }
@@ -171,5 +178,5 @@ function cleanRequestHeaders(headers, removeContent, unknownOrigin) {
171
178
 
172
179
  export default (dispatch) => (opts, handler) =>
173
180
  opts.follow != null
174
- ? dispatch(opts, new Handler(opts, { handler, dispatch, count: 0 }))
181
+ ? dispatch(opts, new Handler(opts, { handler, dispatch }))
175
182
  : dispatch(opts, handler)
@@ -1,61 +1,19 @@
1
- class Handler {
2
- constructor(opts, { handler, dispatch }) {
3
- this.handler = handler
4
- this.dispatch = dispatch
5
- this.ac = new AbortController()
6
-
7
- const signal = opts.signal ? AbortSignal.any([this.ac.signal, opts.signal]) : this.ac.signal
8
-
9
- const body = opts.body({ signal })
10
-
11
- if (typeof body.then === 'function') {
12
- body.then(
13
- (body) => this.dispatch({ ...opts, body }, handler),
14
- (err) => this.handler.onError(err),
15
- )
16
- } else {
17
- this.dispatch({ ...opts, body }, handler)
18
- }
19
- }
20
-
21
- onConnect(abort) {
22
- this.abort = (err) => {
23
- this.ac.abort(err)
24
- abort(err)
25
- }
26
- return this.handler.onConnect(abort)
27
- }
28
-
29
- onUpgrade(statusCode, rawHeaders, socket) {
30
- return this.handler.onUpgrade(statusCode, rawHeaders, socket)
31
- }
32
-
33
- onBodySent(chunk) {
34
- return this.handler.onBodySent(chunk)
35
- }
36
-
37
- onRequestSent() {
38
- return this.handler.onRequestSent()
39
- }
40
-
41
- onHeaders(statusCode, rawHeaders, resume, statusMessage) {
42
- return this.handler.onHeaders(statusCode, rawHeaders, resume, statusMessage)
1
+ export default (dispatch) => (opts, handler) => {
2
+ if (typeof opts.body !== 'function') {
3
+ return dispatch(opts, handler)
43
4
  }
44
5
 
45
- onData(chunk) {
46
- return this.handler.onData(chunk)
47
- }
6
+ // TODO (fix): Can we do signal in a better way using
7
+ // a handler?
48
8
 
49
- onComplete(rawTrailers) {
50
- return this.handler.onComplete(rawTrailers)
51
- }
9
+ const body = opts.body({ signal: opts.signal })
52
10
 
53
- onError(err) {
54
- return this.handler.onError(err)
11
+ if (typeof body.then === 'function') {
12
+ body.then(
13
+ (body) => dispatch({ ...opts, body }, handler),
14
+ (err) => handler.onError(err),
15
+ )
16
+ } else {
17
+ dispatch({ ...opts, body }, handler)
55
18
  }
56
19
  }
57
-
58
- export default (dispatch) => (opts, handler) =>
59
- typeof opts.body === 'function'
60
- ? dispatch(opts, new Handler(opts, { handler, dispatch }))
61
- : dispatch(opts, handler)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/nxt-undici",
3
- "version": "2.0.13",
3
+ "version": "2.0.15",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "main": "lib/index.js",