@superhero/http-request 4.0.8 → 4.0.9
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/index.js +96 -39
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -83,16 +83,19 @@ export default class Request
|
|
|
83
83
|
|
|
84
84
|
const url = new URL(authority || this.config.base)
|
|
85
85
|
authority = url.protocol + '//' + url.host
|
|
86
|
-
options = Object.assign(
|
|
86
|
+
options = Object.assign(this.config, options)
|
|
87
87
|
|
|
88
|
+
this.config.base = authority
|
|
89
|
+
this.config.url = url.pathname + url.search
|
|
88
90
|
this.http2Session = http2.connect(authority, options, () =>
|
|
89
91
|
{
|
|
90
|
-
this.config.base = authority
|
|
91
|
-
this.config.url = url.pathname + url.search
|
|
92
92
|
this.http2Session.removeAllListeners('error')
|
|
93
|
+
this.http2Session.on('error', console.error)
|
|
94
|
+
|
|
93
95
|
accept()
|
|
94
96
|
})
|
|
95
97
|
|
|
98
|
+
// If there is a error on connection, reject the promise.
|
|
96
99
|
this.http2Session.once('error', (reason) =>
|
|
97
100
|
{
|
|
98
101
|
const error = new Error(`Failed to connect to server over HTTP2 using authority: ${authority}`)
|
|
@@ -100,12 +103,6 @@ export default class Request
|
|
|
100
103
|
error.cause = reason
|
|
101
104
|
reject(error)
|
|
102
105
|
})
|
|
103
|
-
|
|
104
|
-
this.http2Session.once('close', () =>
|
|
105
|
-
{
|
|
106
|
-
this.http2Session.removeAllListeners()
|
|
107
|
-
delete this.http2Session
|
|
108
|
-
})
|
|
109
106
|
})
|
|
110
107
|
}
|
|
111
108
|
|
|
@@ -118,23 +115,43 @@ export default class Request
|
|
|
118
115
|
{
|
|
119
116
|
return new Promise((accept, reject) =>
|
|
120
117
|
{
|
|
121
|
-
|
|
122
|
-
|
|
118
|
+
const http2Session = this.http2Session
|
|
119
|
+
|
|
120
|
+
if(http2Session)
|
|
123
121
|
{
|
|
124
|
-
|
|
122
|
+
http2Session.removeAllListeners()
|
|
123
|
+
|
|
124
|
+
if(false === http2Session.closed)
|
|
125
125
|
{
|
|
126
|
-
error
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
126
|
+
http2Session.close((error) =>
|
|
127
|
+
{
|
|
128
|
+
delete this.http2Session
|
|
129
|
+
|
|
130
|
+
error
|
|
131
|
+
? reject(error)
|
|
132
|
+
: accept()
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
return // await the close event
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
delete this.http2Session
|
|
134
139
|
}
|
|
140
|
+
|
|
141
|
+
// fallback to accept if nothing to close
|
|
142
|
+
accept()
|
|
135
143
|
})
|
|
136
144
|
}
|
|
137
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Reonnects to a HTTP/2 server using the last used configurations.
|
|
148
|
+
*/
|
|
149
|
+
async reconnect()
|
|
150
|
+
{
|
|
151
|
+
await this.close()
|
|
152
|
+
await this.connect()
|
|
153
|
+
}
|
|
154
|
+
|
|
138
155
|
/**
|
|
139
156
|
* GET – Read a resource or a collection of resources.
|
|
140
157
|
* Used to retrieve data without modifying it.
|
|
@@ -245,7 +262,18 @@ export default class Request
|
|
|
245
262
|
*
|
|
246
263
|
* @param {string} method
|
|
247
264
|
* @param {RequestOptions} options
|
|
265
|
+
*
|
|
248
266
|
* @returns {RequestResponse}
|
|
267
|
+
*
|
|
268
|
+
* @throws {Error} E_HTTP_REQUEST_CLIENT_ERROR
|
|
269
|
+
* @throws {Error} E_HTTP_REQUEST_CLIENT_TIMEOUT
|
|
270
|
+
* @throws {Error} E_HTTP_REQUEST_DOWNSTREAM_ERROR
|
|
271
|
+
* @throws {Error} E_HTTP_REQUEST_INVALID_RESPONSE_BODY_FORMAT
|
|
272
|
+
* @throws {Error} E_HTTP_REQUEST_INVALID_RESPONSE_STATUS
|
|
273
|
+
* @throws {Error} E_HTTP_REQUEST_HTTP2_SESSION_DESTROYED
|
|
274
|
+
* @throws {Error} E_HTTP_REQUEST_HTTP2_SESSION_CLOSED
|
|
275
|
+
* @throws {Error} E_HTTP_REQUEST_RETRY_HTTP2_RECONNECT
|
|
276
|
+
* @throws {Error} E_HTTP_REQUEST_RETRY_ERROR
|
|
249
277
|
*/
|
|
250
278
|
#fetch(method, options)
|
|
251
279
|
{
|
|
@@ -284,11 +312,6 @@ export default class Request
|
|
|
284
312
|
* @param {RequestOptions} options
|
|
285
313
|
*
|
|
286
314
|
* @returns {RequestResponse}
|
|
287
|
-
*
|
|
288
|
-
* @throws {Error} E_HTTP_REQUEST_CLIENT_TIMEOUT
|
|
289
|
-
* @throws {Error} E_HTTP_REQUEST_CLIENT_ERROR
|
|
290
|
-
* @throws {Error} E_HTTP_REQUEST_DOWNSTREAM_ERROR
|
|
291
|
-
* @throws {Error} E_HTTP_REQUEST_INVALID_RESPONSE_BODY_FORMAT
|
|
292
315
|
*/
|
|
293
316
|
#resolve(options)
|
|
294
317
|
{
|
|
@@ -315,6 +338,22 @@ export default class Request
|
|
|
315
338
|
|
|
316
339
|
#resolveHttp2Client(options, method, headers, url, accept, reject)
|
|
317
340
|
{
|
|
341
|
+
if(true === this.http2Session.destroyed)
|
|
342
|
+
{
|
|
343
|
+
const error = new Error('Session destroyed')
|
|
344
|
+
error.code = 'E_HTTP_REQUEST_HTTP2_SESSION_DESTROYED'
|
|
345
|
+
error.cause = `Can not perform request over a destroyed HTTP2 session to: ${url}`
|
|
346
|
+
return reject(error)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if(true === this.http2Session.closed)
|
|
350
|
+
{
|
|
351
|
+
const error = new Error('Session closed')
|
|
352
|
+
error.code = 'E_HTTP_REQUEST_HTTP2_SESSION_CLOSED'
|
|
353
|
+
error.cause = `Can not perform request over a closed HTTP2 session to: ${url}`
|
|
354
|
+
return reject(error)
|
|
355
|
+
}
|
|
356
|
+
|
|
318
357
|
delete headers['transfer-encoding']
|
|
319
358
|
|
|
320
359
|
const { pathname, search } = new URL(url)
|
|
@@ -369,7 +408,7 @@ export default class Request
|
|
|
369
408
|
#connectionClosed(upstream, reject)
|
|
370
409
|
{
|
|
371
410
|
upstream.removeAllListeners()
|
|
372
|
-
const error = new Error('
|
|
411
|
+
const error = new Error('Connection was closed unexpectedly')
|
|
373
412
|
error.code = 'E_HTTP_REQUEST_CLIENT_ERROR'
|
|
374
413
|
setImmediate(() => reject(error)) // this error is a fallback if the promise is not already resolved
|
|
375
414
|
}
|
|
@@ -419,18 +458,14 @@ export default class Request
|
|
|
419
458
|
|
|
420
459
|
/**
|
|
421
460
|
* Resolves the request in a retry loop.
|
|
422
|
-
*
|
|
423
461
|
* @param {RequestOptions} options
|
|
424
|
-
*
|
|
425
462
|
* @returns {RequestResponse}
|
|
426
|
-
*
|
|
427
|
-
* @throws {Error} E_HTTP_REQUEST_RETRY
|
|
428
463
|
*/
|
|
429
464
|
async #resolveRetryLoop(options)
|
|
430
465
|
{
|
|
431
466
|
const errorTrace = []
|
|
432
467
|
|
|
433
|
-
let retry = Math.abs(Math.floor(options.retry))
|
|
468
|
+
let retry = Math.abs(Math.floor(options.retry)) + 1
|
|
434
469
|
|
|
435
470
|
while(retry--)
|
|
436
471
|
{
|
|
@@ -458,11 +493,23 @@ export default class Request
|
|
|
458
493
|
}
|
|
459
494
|
else
|
|
460
495
|
{
|
|
461
|
-
this.#resolveRetryLoopError(options, errorTrace, error)
|
|
496
|
+
await this.#resolveRetryLoopError(options, errorTrace, error)
|
|
462
497
|
await wait(options.retryDelay)
|
|
463
498
|
}
|
|
464
499
|
}
|
|
465
500
|
}
|
|
501
|
+
|
|
502
|
+
if(errorTrace.every((error) => error.code === errorTrace[0].code))
|
|
503
|
+
{
|
|
504
|
+
throw errorTrace.pop()
|
|
505
|
+
}
|
|
506
|
+
else
|
|
507
|
+
{
|
|
508
|
+
const error = new Error('Multiple types of errors occurred during the retry loop')
|
|
509
|
+
error.code = 'E_HTTP_REQUEST_RETRY_ERROR'
|
|
510
|
+
error.cause = errorTrace
|
|
511
|
+
throw error
|
|
512
|
+
}
|
|
466
513
|
}
|
|
467
514
|
|
|
468
515
|
/**
|
|
@@ -473,14 +520,8 @@ export default class Request
|
|
|
473
520
|
* @param {Error} error
|
|
474
521
|
*
|
|
475
522
|
* @returns {Void}
|
|
476
|
-
*
|
|
477
|
-
* @throws {Error} E_HTTP_REQUEST_CLIENT_ERROR
|
|
478
|
-
* @throws {Error} E_HTTP_REQUEST_CLIENT_TIMEOUT
|
|
479
|
-
* @throws {Error} E_HTTP_REQUEST_DOWNSTREAM_ERROR
|
|
480
|
-
* @throws {Error} E_HTTP_REQUEST_INVALID_RESPONSE_BODY_FORMAT
|
|
481
|
-
* @throws {Error} E_HTTP_REQUEST_INVALID_RESPONSE_STATUS
|
|
482
523
|
*/
|
|
483
|
-
#resolveRetryLoopError(options, errorTrace, error)
|
|
524
|
+
async #resolveRetryLoopError(options, errorTrace, error)
|
|
484
525
|
{
|
|
485
526
|
switch(error.code)
|
|
486
527
|
{
|
|
@@ -488,6 +529,22 @@ export default class Request
|
|
|
488
529
|
{
|
|
489
530
|
return errorTrace.push(error)
|
|
490
531
|
}
|
|
532
|
+
case 'E_HTTP_REQUEST_HTTP2_SESSION_DESTROYED':
|
|
533
|
+
case 'E_HTTP_REQUEST_HTTP2_SESSION_CLOSED':
|
|
534
|
+
{
|
|
535
|
+
try
|
|
536
|
+
{
|
|
537
|
+
await this.reconnect()
|
|
538
|
+
}
|
|
539
|
+
catch(reason)
|
|
540
|
+
{
|
|
541
|
+
const reconnectError = new Error(`${reason.message}, retry to reconnect to the server failed`)
|
|
542
|
+
reconnectError.code = 'E_HTTP_REQUEST_RETRY_HTTP2_RECONNECT'
|
|
543
|
+
reconnectError.cause = reason
|
|
544
|
+
throw reason
|
|
545
|
+
}
|
|
546
|
+
return errorTrace.push(error)
|
|
547
|
+
}
|
|
491
548
|
case 'E_HTTP_REQUEST_CLIENT_TIMEOUT':
|
|
492
549
|
{
|
|
493
550
|
if(options.retryOnClientTimeout)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@superhero/http-request",
|
|
3
|
-
"version": "4.0.
|
|
4
|
-
"description": "HTTP request component supporting HTTP 1.1 and HTTP 2.0",
|
|
3
|
+
"version": "4.0.9",
|
|
4
|
+
"description": "HTTP(S) request component supporting HTTP 1.1 and HTTP 2.0",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"http request",
|
|
7
7
|
"http client",
|