@nxtedition/lib 14.1.0 → 14.1.2

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/undici.js +108 -17
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/lib",
3
- "version": "14.1.0",
3
+ "version": "14.1.2",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "files": [
package/undici.js CHANGED
@@ -1,29 +1,120 @@
1
- import * as undici from 'undici'
2
- import xuid from 'xuid'
1
+ const assert = require('assert')
2
+ const tp = require('timers/promises')
3
+ const xuid = require('xuid')
4
+ const { isReadableNodeStream } = require('./stream')
5
+ const undici = require('undici')
6
+ const stream = require('stream')
3
7
 
4
- export async function request(url, { logger, ...options }) {
8
+ module.exports.request = async function request(
9
+ url,
10
+ {
11
+ logger,
12
+ id = xuid(),
13
+ retry: { count: maxRetries = 8 } = {},
14
+ redirect: { count: maxRedirections = 3 } = {},
15
+ dispatcher,
16
+ signal,
17
+ headersTimeout,
18
+ bodyTimeout,
19
+ reset = false,
20
+ method = 'GET',
21
+ body,
22
+ userAgent,
23
+ headers,
24
+ }
25
+ ) {
5
26
  const ureq = {
6
27
  url,
7
- maxRedirections: 3,
28
+ method,
29
+ body,
8
30
  headers: {
9
- 'req-id': xuid(),
10
- 'user-agent': options.userAgent,
11
- ...options.headers,
31
+ 'req-id': id,
32
+ 'user-agent': userAgent,
33
+ ...headers,
12
34
  },
13
- ...options,
14
- throwOnError: true,
15
35
  }
16
36
 
17
- logger?.debug({ ureq }, 'upstream request started')
37
+ const upstreamLogger = logger?.child({ ureq })
18
38
 
19
- const ures = await undici.request(url, options)
39
+ upstreamLogger?.debug({ ureq }, 'upstream request started')
20
40
 
21
- logger?.debug({ ureq, ures }, 'upstream request response')
41
+ try {
42
+ /* eslint-disable no-unreachable-loop */
43
+ for (let retryCount = 0; true; retryCount++) {
44
+ try {
45
+ const ures = await undici.request(url, {
46
+ method,
47
+ reset,
48
+ body,
49
+ headers,
50
+ signal,
51
+ dispatcher,
52
+ maxRedirections,
53
+ throwOnError: true,
54
+ headersTimeout,
55
+ bodyTimeout,
56
+ })
22
57
 
23
- if (ures.statusCode >= 300 && ures.statusCode < 400) {
24
- await ures.body.dump()
25
- throw new Error('maxRedirections exceeded')
26
- }
58
+ upstreamLogger?.debug({ ureq, ures }, 'upstream request response')
59
+
60
+ if (ures.statusCode >= 300 && ures.statusCode < 400) {
61
+ await ures.body.dump()
62
+ throw new Error('maxRedirections exceeded')
63
+ }
64
+
65
+ assert(ures.statusCode >= 200 && ures.statusCode < 300)
66
+
67
+ // TODO (fix): Wrap response to handle error that can continue with range request...
68
+
69
+ return ures
70
+ } catch (err) {
71
+ if (retryCount >= maxRetries) {
72
+ throw err
73
+ }
27
74
 
28
- return ures
75
+ if (
76
+ body != null &&
77
+ typeof body !== 'string' &&
78
+ !Buffer.isBuffer(body) &&
79
+ (!isReadableNodeStream(body) || stream.isDisturbed(body))
80
+ ) {
81
+ throw err
82
+ }
83
+
84
+ if (method === 'HEAD' || method === 'GET') {
85
+ if (
86
+ err.code !== 'ECONNRESET' &&
87
+ err.code !== 'ECONNREFUSED' &&
88
+ err.code !== 'ENOTFOUND' &&
89
+ err.code !== 'ENETDOWN' &&
90
+ err.code !== 'ENETUNREACH' &&
91
+ err.code !== 'EHOSTDOWN' &&
92
+ err.code !== 'EHOSTUNREACH' &&
93
+ err.code !== 'EPIPE' &&
94
+ err.message !== 'other side closed' &&
95
+ err.statusCode !== 420 &&
96
+ err.statusCode !== 429 &&
97
+ err.statusCode !== 502 &&
98
+ err.statusCode !== 503 &&
99
+ err.statusCode !== 504
100
+ ) {
101
+ throw err
102
+ }
103
+ } else {
104
+ // TODO (fix): What to do?
105
+ throw err
106
+ }
107
+
108
+ const delay =
109
+ parseInt(err.headers?.['Retry-After']) * 1e3 || Math.min(10e3, retryCount * 1e3 + 1e3)
110
+
111
+ logger?.warn({ err, retryCount, delay }, 'upstream request retrying')
112
+
113
+ return tp.setTimeout(delay, undefined, { signal })
114
+ }
115
+ }
116
+ } catch (err) {
117
+ logger?.error({ err }, 'upstream request failed')
118
+ throw err
119
+ }
29
120
  }