@wiajs/request 3.0.18 → 3.0.19
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/dist/request.cjs +230 -104
- package/dist/request.mjs +230 -104
- package/lib/ZlibTransform.js +12 -2
- package/lib/caseless.js +27 -5
- package/lib/request.js +141 -88
- package/lib/utils.js +21 -8
- package/package.json +7 -8
package/dist/request.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* wia request v3.0.
|
|
2
|
+
* wia request v3.0.19
|
|
3
3
|
* (c) 2022-2024 Sibyl Yu and contributors
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
@@ -13,11 +13,23 @@ import zlib from 'node:zlib';
|
|
|
13
13
|
import mime from 'mime-types';
|
|
14
14
|
|
|
15
15
|
class ZlibTransform extends stream.Transform {
|
|
16
|
+
/**
|
|
17
|
+
*
|
|
18
|
+
* @param {*} chunk
|
|
19
|
+
* @param {*} encoding
|
|
20
|
+
* @param {*} callback
|
|
21
|
+
*/
|
|
16
22
|
__transform(chunk, encoding, callback) {
|
|
17
23
|
this.push(chunk);
|
|
18
24
|
callback();
|
|
19
25
|
}
|
|
20
26
|
|
|
27
|
+
/**
|
|
28
|
+
*
|
|
29
|
+
* @param {*} chunk
|
|
30
|
+
* @param {*} encoding
|
|
31
|
+
* @param {*} callback
|
|
32
|
+
*/
|
|
21
33
|
_transform(chunk, encoding, callback) {
|
|
22
34
|
if (chunk.length !== 0) {
|
|
23
35
|
this._transform = this.__transform;
|
|
@@ -66,14 +78,22 @@ const preservedUrlFields = [
|
|
|
66
78
|
];
|
|
67
79
|
|
|
68
80
|
/**
|
|
69
|
-
*
|
|
70
|
-
* @param {
|
|
71
|
-
* @param {
|
|
72
|
-
* @param {
|
|
73
|
-
* @returns
|
|
81
|
+
* Create a custom error type.
|
|
82
|
+
* @param {string} code - The error code.
|
|
83
|
+
* @param {string} message - The error message.
|
|
84
|
+
* @param {typeof Error} [baseClass] - The base error class to extend from. Defaults to `Error`.
|
|
85
|
+
* @returns {typeof Error & { new(properties?: object): CustomErrorInstance }} A custom error constructor.
|
|
86
|
+
* new(properties?: object) 为构造函数语法,返回 CustomErrorInstance 类型
|
|
87
|
+
* @typedef {object} CustomErrorInstance
|
|
88
|
+
* @property {string} code - The error code.
|
|
89
|
+
* @property {string} message - The error message.
|
|
90
|
+
* @property {Error | undefined} cause - The optional error cause.
|
|
74
91
|
*/
|
|
75
|
-
function createErrorType(code, message, baseClass) {
|
|
76
|
-
|
|
92
|
+
function createErrorType(code, message, baseClass) {
|
|
93
|
+
/**
|
|
94
|
+
* Create constructor
|
|
95
|
+
* @param {*} properties
|
|
96
|
+
*/
|
|
77
97
|
function CustomError(properties) {
|
|
78
98
|
// istanbul ignore else
|
|
79
99
|
if (isFunction(Error.captureStackTrace)) {
|
|
@@ -81,6 +101,7 @@ function createErrorType(code, message, baseClass) {
|
|
|
81
101
|
}
|
|
82
102
|
Object.assign(this, properties || {});
|
|
83
103
|
this.code = code;
|
|
104
|
+
// @ts-ignore
|
|
84
105
|
this.message = this.cause ? `${message}: ${this.cause.message}` : message;
|
|
85
106
|
}
|
|
86
107
|
|
|
@@ -96,6 +117,8 @@ function createErrorType(code, message, baseClass) {
|
|
|
96
117
|
enumerable: false,
|
|
97
118
|
},
|
|
98
119
|
});
|
|
120
|
+
|
|
121
|
+
// @ts-ignore
|
|
99
122
|
return CustomError
|
|
100
123
|
}
|
|
101
124
|
|
|
@@ -194,6 +217,11 @@ function isURL(value) {
|
|
|
194
217
|
return URL && value instanceof URL
|
|
195
218
|
}
|
|
196
219
|
|
|
220
|
+
/**
|
|
221
|
+
*
|
|
222
|
+
* @param {*} rs
|
|
223
|
+
* @returns
|
|
224
|
+
*/
|
|
197
225
|
function isReadStream(rs) {
|
|
198
226
|
return rs.readable && rs.path && rs.mode
|
|
199
227
|
}
|
|
@@ -383,6 +411,11 @@ class Caseless {
|
|
|
383
411
|
delete this.dict[has];
|
|
384
412
|
}
|
|
385
413
|
|
|
414
|
+
/**
|
|
415
|
+
*
|
|
416
|
+
* @param {string} name
|
|
417
|
+
* @returns
|
|
418
|
+
*/
|
|
386
419
|
del(name) {
|
|
387
420
|
name = String(name).toLowerCase();
|
|
388
421
|
let deleted = false;
|
|
@@ -405,6 +438,34 @@ class Caseless {
|
|
|
405
438
|
|
|
406
439
|
const log$1 = log$2({env: `wia:req:${name(import.meta.url)}`}); // __filename
|
|
407
440
|
|
|
441
|
+
/**
|
|
442
|
+
* @typedef {object} Opts
|
|
443
|
+
* @prop {Object.<string,string>} headers
|
|
444
|
+
* @prop {string} host
|
|
445
|
+
* @prop {string} method
|
|
446
|
+
* @prop {string} family
|
|
447
|
+
* @prop {string} path
|
|
448
|
+
* @prop {'http:' | 'https:'} protocol
|
|
449
|
+
* @prop {*} agent
|
|
450
|
+
* @prop {*} agents
|
|
451
|
+
* @prop {boolean} [stream]
|
|
452
|
+
* @prop {boolean} [decompress=true]
|
|
453
|
+
* @prop {*} [transformStream]
|
|
454
|
+
* @prop {*} [beforeRedirect]
|
|
455
|
+
* @prop {boolean} [followRedirects]
|
|
456
|
+
* @prop {number} [maxRedirects=21]
|
|
457
|
+
* @prop {number} [maxBodyLength = 0]
|
|
458
|
+
* @prop {*} [trackRedirects]
|
|
459
|
+
*/
|
|
460
|
+
|
|
461
|
+
/** @typedef {object} ResponseExt
|
|
462
|
+
* @prop {*[]} [redirects]
|
|
463
|
+
* @prop {string} [responseUrl]
|
|
464
|
+
* @prop {number} [responseStartTime]
|
|
465
|
+
*/
|
|
466
|
+
|
|
467
|
+
/** @typedef { http.IncomingMessage & ResponseExt} Response*/
|
|
468
|
+
|
|
408
469
|
const httpModules = {'http:': http, 'https:': https};
|
|
409
470
|
|
|
410
471
|
const zlibOptions = {
|
|
@@ -465,8 +526,9 @@ const writeEvents = [
|
|
|
465
526
|
];
|
|
466
527
|
|
|
467
528
|
const writeEventEmit = Object.create(null);
|
|
529
|
+
|
|
468
530
|
for (const ev of writeEvents)
|
|
469
|
-
writeEventEmit[ev] = function (...args) {
|
|
531
|
+
writeEventEmit[ev] = /** @param {...any} args */ function (...args) {
|
|
470
532
|
const m = this; // 事件回调,this === clientRequest 实例
|
|
471
533
|
log$1('req event', {ev});
|
|
472
534
|
m.redirectReq.emit(ev, ...args); // req 事情映射到 redirectReq 上触发
|
|
@@ -477,14 +539,17 @@ for (const ev of writeEvents)
|
|
|
477
539
|
const readEvents = ['close', 'end', 'error', 'pause', 'readable', 'resume'];
|
|
478
540
|
const readEventEmit = Object.create(null);
|
|
479
541
|
for (const ev of readEvents)
|
|
480
|
-
readEventEmit[ev] = function (...args) {
|
|
542
|
+
readEventEmit[ev] = /** @param {...any} args */ function (...args) {
|
|
481
543
|
const m = this; // 事件回调,this === clientRequest 实例
|
|
482
544
|
log$1('res event', {ev});
|
|
483
545
|
m.redirectReq.emit(ev, ...args); // 向上触发事件
|
|
484
546
|
};
|
|
485
547
|
|
|
486
548
|
// Error types with codes
|
|
487
|
-
const RedirectionError = utils.createErrorType(
|
|
549
|
+
const RedirectionError = utils.createErrorType(
|
|
550
|
+
'ERR_FR_REDIRECTION_FAILURE',
|
|
551
|
+
'Redirected request failed'
|
|
552
|
+
);
|
|
488
553
|
|
|
489
554
|
const TooManyRedirectsError = utils.createErrorType(
|
|
490
555
|
'ERR_FR_TOO_MANY_REDIRECTS',
|
|
@@ -504,12 +569,13 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
504
569
|
* wrap http.ClientRequest
|
|
505
570
|
*/
|
|
506
571
|
class Request extends Duplex {
|
|
507
|
-
|
|
572
|
+
/** @type {NodeJS.Timeout} */
|
|
573
|
+
_timeout = null
|
|
508
574
|
/** @type {*} */
|
|
509
575
|
socket = null
|
|
510
576
|
/** @type {http.ClientRequest} */
|
|
511
577
|
_currentRequest = null
|
|
512
|
-
/** @type {
|
|
578
|
+
/** @type {Response} */
|
|
513
579
|
response = null
|
|
514
580
|
/** @type {stream.Readable} */
|
|
515
581
|
responseStream = null
|
|
@@ -525,10 +591,16 @@ class Request extends Duplex {
|
|
|
525
591
|
pipedests = [] // pipe dest
|
|
526
592
|
/** @type {*} */
|
|
527
593
|
startTimer = null
|
|
594
|
+
/** @type {Opts} */
|
|
595
|
+
opt
|
|
596
|
+
/** @type {*} */
|
|
597
|
+
pipefilter
|
|
598
|
+
/** @type {string} */
|
|
599
|
+
_currentUrl
|
|
528
600
|
|
|
529
601
|
/**
|
|
530
602
|
* responseCallback 原消息处理回调
|
|
531
|
-
* @param {
|
|
603
|
+
* @param {Opts} opts
|
|
532
604
|
* @param {*} resCallback
|
|
533
605
|
*/
|
|
534
606
|
constructor(opts, resCallback) {
|
|
@@ -555,13 +627,20 @@ class Request extends Duplex {
|
|
|
555
627
|
|
|
556
628
|
// save the callback if passed
|
|
557
629
|
m.resCallback = resCallback;
|
|
558
|
-
|
|
559
|
-
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* React to responses of native requests
|
|
633
|
+
* 接管 response 事件,非重定向,触发 response 事件
|
|
634
|
+
* @param {Response} res
|
|
635
|
+
*/
|
|
560
636
|
m._onResponse = res => {
|
|
561
637
|
try {
|
|
562
638
|
m.processResponse(res);
|
|
563
639
|
} catch (cause) {
|
|
564
|
-
m.emit(
|
|
640
|
+
m.emit(
|
|
641
|
+
'error',
|
|
642
|
+
cause instanceof RedirectionError ? cause : new RedirectionError({cause: cause})
|
|
643
|
+
);
|
|
565
644
|
}
|
|
566
645
|
};
|
|
567
646
|
|
|
@@ -593,27 +672,31 @@ class Request extends Duplex {
|
|
|
593
672
|
// 被 pipe 作为目标时触发,拷贝 src headers
|
|
594
673
|
m.on(
|
|
595
674
|
'pipe',
|
|
596
|
-
/** @
|
|
675
|
+
/** @param {stream.Readable & {headers?: Object.<string, string>}} src */ src => {
|
|
597
676
|
// m.ntick &&
|
|
598
677
|
if (m._currentRequest) {
|
|
599
|
-
m.emit(
|
|
678
|
+
m.emit(
|
|
679
|
+
'error',
|
|
680
|
+
new Error('You cannot pipe to this stream after the outbound request has started.')
|
|
681
|
+
);
|
|
600
682
|
}
|
|
601
683
|
|
|
602
684
|
m.pipesrc = src;
|
|
603
685
|
|
|
604
686
|
if (utils.isReadStream(src)) {
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
}
|
|
687
|
+
// @ts-ignore
|
|
688
|
+
if (!m.hasHeader('content-type')) m.setHeader('content-type', mime.lookup(src.path));
|
|
608
689
|
} else {
|
|
690
|
+
// 拷贝请求头
|
|
609
691
|
if (src.headers) {
|
|
610
|
-
for (const
|
|
611
|
-
if (!m.hasHeader(
|
|
612
|
-
m.setHeader(
|
|
692
|
+
for (const k of Object.keys(src.headers)) {
|
|
693
|
+
if (!m.hasHeader(k)) {
|
|
694
|
+
m.setHeader(k, src.headers[k]);
|
|
613
695
|
}
|
|
614
696
|
}
|
|
615
697
|
}
|
|
616
698
|
|
|
699
|
+
// @ts-ignore
|
|
617
700
|
if (src.opt.method && !m.opt.method) m.opt.method = src.opt.method;
|
|
618
701
|
}
|
|
619
702
|
}
|
|
@@ -660,7 +743,8 @@ class Request extends Duplex {
|
|
|
660
743
|
// http 非隧道代理模式,模块以代理主机为准,其他以目的网址为准
|
|
661
744
|
// 代理内部会根据代理协议选择 http(s) 发起请求创建连接
|
|
662
745
|
if (protocol === 'http:' && agents.http) {
|
|
663
|
-
protocol =
|
|
746
|
+
protocol =
|
|
747
|
+
agents.http.proxy && !agents.http.tunnel ? agents.http.proxy.protocol : protocol;
|
|
664
748
|
}
|
|
665
749
|
}
|
|
666
750
|
|
|
@@ -669,8 +753,10 @@ class Request extends Duplex {
|
|
|
669
753
|
|
|
670
754
|
log$1({opt, protocol}, 'request');
|
|
671
755
|
// Create the native request and set up its event handlers
|
|
756
|
+
// @ts-ignore
|
|
672
757
|
const req = httpModule.request(opt, m._onResponse);
|
|
673
758
|
m._currentRequest = req;
|
|
759
|
+
// @ts-ignore
|
|
674
760
|
req.redirectReq = m;
|
|
675
761
|
|
|
676
762
|
// 启动 startTimer
|
|
@@ -690,8 +776,13 @@ class Request extends Duplex {
|
|
|
690
776
|
if (m._isRedirect) {
|
|
691
777
|
// Write the request entity and end
|
|
692
778
|
let i = 0;
|
|
693
|
-
const buffers = m._requestBodyBuffers
|
|
694
|
-
|
|
779
|
+
const buffers = m._requestBodyBuffers;
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
*
|
|
783
|
+
* @param {*} error
|
|
784
|
+
*/
|
|
785
|
+
function writeNext(error) {
|
|
695
786
|
// Only write if this request has not been redirected yet
|
|
696
787
|
/* istanbul ignore else */
|
|
697
788
|
if (req === m._currentRequest) {
|
|
@@ -707,7 +798,8 @@ class Request extends Duplex {
|
|
|
707
798
|
// End the request if `end` has been called on us
|
|
708
799
|
else if (m._ended) req.end();
|
|
709
800
|
}
|
|
710
|
-
}
|
|
801
|
+
}
|
|
802
|
+
writeNext();
|
|
711
803
|
}
|
|
712
804
|
|
|
713
805
|
R = req;
|
|
@@ -745,15 +837,15 @@ class Request extends Duplex {
|
|
|
745
837
|
/**
|
|
746
838
|
* Writes buffered data to the current native request
|
|
747
839
|
* 如 request 不存在,则创建连接,pipe 时可写入 header
|
|
748
|
-
* @
|
|
749
|
-
* @param {
|
|
750
|
-
* @param {(error: Error) => void} [
|
|
751
|
-
* @
|
|
840
|
+
* @override - 重写父类方法
|
|
841
|
+
* @param {*} chunk - The data chunk to write.
|
|
842
|
+
* @param {BufferEncoding | ((error: Error | null) => void)} [encodingOrCallback] - Encoding for string data, or the callback if no encoding is provided.
|
|
843
|
+
* @param {(error: Error | null) => void} [cb] - Callback to signal the end of the write operation.
|
|
844
|
+
* @returns {boolean} True if the write was successful, false otherwise.
|
|
752
845
|
*/
|
|
753
|
-
write(chunk,
|
|
846
|
+
write(chunk, encodingOrCallback, cb) {
|
|
754
847
|
const m = this;
|
|
755
|
-
|
|
756
|
-
log$1({data: chunk, encoding, callback: cb}, 'write');
|
|
848
|
+
log$1({data: chunk, encoding: encodingOrCallback, callback: cb}, 'write');
|
|
757
849
|
|
|
758
850
|
// Writing is not allowed if end has been called
|
|
759
851
|
if (m._ending) throw new WriteAfterEndError()
|
|
@@ -765,23 +857,25 @@ class Request extends Duplex {
|
|
|
765
857
|
if (!utils.isString(chunk) && !utils.isBuffer(chunk))
|
|
766
858
|
throw new TypeError('data should be a string, Buffer or Uint8Array')
|
|
767
859
|
|
|
768
|
-
if (utils.isFunction(
|
|
769
|
-
|
|
770
|
-
|
|
860
|
+
if (utils.isFunction(encodingOrCallback)) {
|
|
861
|
+
// @ts-ignore
|
|
862
|
+
cb = encodingOrCallback;
|
|
863
|
+
encodingOrCallback = null;
|
|
771
864
|
}
|
|
772
865
|
|
|
773
866
|
// Ignore empty buffers, since writing them doesn't invoke the callback
|
|
774
867
|
// https://github.com/nodejs/node/issues/22066
|
|
775
868
|
if (chunk.length === 0) {
|
|
776
|
-
if (cb) cb();
|
|
869
|
+
if (cb) cb(null);
|
|
777
870
|
return
|
|
778
871
|
}
|
|
779
872
|
|
|
780
873
|
// Only write when we don't exceed the maximum body length
|
|
781
874
|
if (m._requestBodyLength + chunk.length <= m.opt.maxBodyLength) {
|
|
782
875
|
m._requestBodyLength += chunk.length;
|
|
783
|
-
m._requestBodyBuffers.push({data: chunk, encoding});
|
|
784
|
-
|
|
876
|
+
m._requestBodyBuffers.push({data: chunk, encoding: encodingOrCallback});
|
|
877
|
+
// @ts-ignore
|
|
878
|
+
m._currentRequest.write(chunk, encodingOrCallback, cb);
|
|
785
879
|
}
|
|
786
880
|
// Error when we exceed the maximum body length
|
|
787
881
|
else {
|
|
@@ -792,20 +886,23 @@ class Request extends Duplex {
|
|
|
792
886
|
|
|
793
887
|
/**
|
|
794
888
|
* Ends the current native request
|
|
795
|
-
* @
|
|
796
|
-
* @param {*}
|
|
797
|
-
* @param {
|
|
889
|
+
* @override - 重写父类方法
|
|
890
|
+
* @param {*} [chunk] - Optional data to write before ending the stream.
|
|
891
|
+
* @param {BufferEncoding | (() => void)} [encoding] - Encoding for string data, or the callback if no encoding is provided.
|
|
892
|
+
* @param {() => void} [cb] - Optional callback to signal completion.
|
|
893
|
+
* @returns {this} The current stream instance, to allow chaining.
|
|
798
894
|
*/
|
|
799
|
-
end(
|
|
895
|
+
end(chunk, encoding, cb) {
|
|
800
896
|
const m = this;
|
|
801
897
|
|
|
802
898
|
// Shift parameters if necessary
|
|
803
|
-
if (utils.isFunction(
|
|
804
|
-
|
|
805
|
-
|
|
899
|
+
if (utils.isFunction(chunk)) {
|
|
900
|
+
cb = chunk;
|
|
901
|
+
chunk = null;
|
|
806
902
|
encoding = null;
|
|
807
903
|
} else if (utils.isFunction(encoding)) {
|
|
808
|
-
|
|
904
|
+
// @ts-ignore
|
|
905
|
+
cb = encoding;
|
|
809
906
|
encoding = null;
|
|
810
907
|
}
|
|
811
908
|
|
|
@@ -813,19 +910,21 @@ class Request extends Duplex {
|
|
|
813
910
|
if (!m._currentRequest) m.request();
|
|
814
911
|
|
|
815
912
|
// Write data if needed and end
|
|
816
|
-
if (!
|
|
913
|
+
if (!chunk) {
|
|
817
914
|
m._ended = true;
|
|
818
915
|
m._ending = true;
|
|
819
|
-
m._currentRequest.end(null, null,
|
|
916
|
+
m._currentRequest.end(null, null, cb);
|
|
820
917
|
} else {
|
|
821
918
|
const currentRequest = m._currentRequest;
|
|
822
|
-
m.write(
|
|
919
|
+
m.write(chunk, encoding, () => {
|
|
823
920
|
m._ended = true;
|
|
824
|
-
currentRequest.end(null, null,
|
|
921
|
+
currentRequest.end(null, null, cb);
|
|
825
922
|
});
|
|
826
923
|
|
|
827
924
|
m._ending = true;
|
|
828
925
|
}
|
|
926
|
+
|
|
927
|
+
return m
|
|
829
928
|
}
|
|
830
929
|
|
|
831
930
|
/**
|
|
@@ -834,7 +933,7 @@ class Request extends Duplex {
|
|
|
834
933
|
* @returns
|
|
835
934
|
*/
|
|
836
935
|
hasHeader(name) {
|
|
837
|
-
return this.opt.headers.includes(name)
|
|
936
|
+
return Object.keys(this.opt.headers).includes(name)
|
|
838
937
|
}
|
|
839
938
|
|
|
840
939
|
/**
|
|
@@ -849,6 +948,7 @@ class Request extends Duplex {
|
|
|
849
948
|
/**
|
|
850
949
|
* Sets a header value on the current native request
|
|
851
950
|
* @param {string} name
|
|
951
|
+
* @param {string} value
|
|
852
952
|
*/
|
|
853
953
|
setHeader(name, value) {
|
|
854
954
|
this.opt.headers[name] = value;
|
|
@@ -946,6 +1046,10 @@ class Request extends Duplex {
|
|
|
946
1046
|
return m
|
|
947
1047
|
}
|
|
948
1048
|
|
|
1049
|
+
/**
|
|
1050
|
+
*
|
|
1051
|
+
* @param {*} options
|
|
1052
|
+
*/
|
|
949
1053
|
sanitizeOptions(options) {
|
|
950
1054
|
// Ensure headers are always present
|
|
951
1055
|
if (!options.headers) options.headers = {};
|
|
@@ -975,7 +1079,7 @@ class Request extends Duplex {
|
|
|
975
1079
|
|
|
976
1080
|
/**
|
|
977
1081
|
* Processes a response from the current native request
|
|
978
|
-
* @param {
|
|
1082
|
+
* @param {Response} response
|
|
979
1083
|
* @returns
|
|
980
1084
|
*/
|
|
981
1085
|
processResponse(response) {
|
|
@@ -984,7 +1088,7 @@ class Request extends Duplex {
|
|
|
984
1088
|
|
|
985
1089
|
// Store the redirected response
|
|
986
1090
|
const {statusCode} = response;
|
|
987
|
-
if (
|
|
1091
|
+
if (opt.trackRedirects) {
|
|
988
1092
|
m._redirects.push({
|
|
989
1093
|
url: m._currentUrl,
|
|
990
1094
|
headers: response.headers,
|
|
@@ -1004,7 +1108,7 @@ class Request extends Duplex {
|
|
|
1004
1108
|
|
|
1005
1109
|
log$1({statusCode, headers: response.headers}, 'processResponse');
|
|
1006
1110
|
|
|
1007
|
-
if (!location ||
|
|
1111
|
+
if (!location || opt.followRedirects === false || statusCode < 300 || statusCode >= 400) {
|
|
1008
1112
|
// 非重定向,返回给原始回调处理
|
|
1009
1113
|
response.responseUrl = m._currentUrl;
|
|
1010
1114
|
response.redirects = m._redirects;
|
|
@@ -1035,7 +1139,7 @@ class Request extends Duplex {
|
|
|
1035
1139
|
|
|
1036
1140
|
// Clean up
|
|
1037
1141
|
m._requestBodyBuffers = [];
|
|
1038
|
-
return
|
|
1142
|
+
return // 退出,不继续处理
|
|
1039
1143
|
}
|
|
1040
1144
|
|
|
1041
1145
|
// The response is a redirect, so abort the current request
|
|
@@ -1045,16 +1149,17 @@ class Request extends Duplex {
|
|
|
1045
1149
|
|
|
1046
1150
|
// RFC7231§6.4: A client SHOULD detect and intervene
|
|
1047
1151
|
// in cyclical redirections (i.e., "infinite" redirection loops).
|
|
1048
|
-
if (++m._redirectCount >
|
|
1152
|
+
if (++m._redirectCount > opt.maxRedirects) throw new TooManyRedirectsError()
|
|
1049
1153
|
|
|
1050
1154
|
// Store the request headers if applicable
|
|
1051
1155
|
let requestHeaders;
|
|
1052
|
-
const {beforeRedirect} =
|
|
1156
|
+
const {beforeRedirect} = opt;
|
|
1053
1157
|
if (beforeRedirect) {
|
|
1054
1158
|
requestHeaders = {
|
|
1055
1159
|
// The Host header was set by nativeProtocol.request
|
|
1160
|
+
// @ts-ignore
|
|
1056
1161
|
Host: response.req.getHeader('host'),
|
|
1057
|
-
...
|
|
1162
|
+
...opt.headers,
|
|
1058
1163
|
};
|
|
1059
1164
|
}
|
|
1060
1165
|
|
|
@@ -1062,23 +1167,23 @@ class Request extends Duplex {
|
|
|
1062
1167
|
// care for methods not known to be safe, […]
|
|
1063
1168
|
// RFC7231§6.4.2–3: For historical reasons, a user agent MAY change
|
|
1064
1169
|
// the request method from POST to GET for the subsequent request.
|
|
1065
|
-
const {method} =
|
|
1170
|
+
const {method} = opt;
|
|
1066
1171
|
if (
|
|
1067
|
-
((statusCode === 301 || statusCode === 302) &&
|
|
1172
|
+
((statusCode === 301 || statusCode === 302) && opt.method === 'POST') ||
|
|
1068
1173
|
// RFC7231§6.4.4: The 303 (See Other) status code indicates that
|
|
1069
1174
|
// the server is redirecting the user agent to a different resource […]
|
|
1070
1175
|
// A user agent can perform a retrieval request targeting that URI
|
|
1071
1176
|
// (a GET or HEAD request if using HTTP) […]
|
|
1072
|
-
(statusCode === 303 && !/^(?:GET|HEAD)$/.test(
|
|
1177
|
+
(statusCode === 303 && !/^(?:GET|HEAD)$/.test(opt.method))
|
|
1073
1178
|
) {
|
|
1074
1179
|
m.opt.method = 'GET';
|
|
1075
1180
|
// Drop a possible entity and headers related to it
|
|
1076
1181
|
m._requestBodyBuffers = [];
|
|
1077
|
-
removeMatchingHeaders(/^content-/i,
|
|
1182
|
+
removeMatchingHeaders(/^content-/i, opt.headers);
|
|
1078
1183
|
}
|
|
1079
1184
|
|
|
1080
1185
|
// Drop the Host header, as the redirect might lead to a different host
|
|
1081
|
-
const currentHostHeader = removeMatchingHeaders(/^host$/i,
|
|
1186
|
+
const currentHostHeader = removeMatchingHeaders(/^host$/i, opt.headers);
|
|
1082
1187
|
|
|
1083
1188
|
// If the redirect is relative, carry over the host of the last request
|
|
1084
1189
|
const currentUrlParts = utils.parseUrl(m._currentUrl);
|
|
@@ -1115,8 +1220,8 @@ class Request extends Duplex {
|
|
|
1115
1220
|
headers: requestHeaders,
|
|
1116
1221
|
};
|
|
1117
1222
|
|
|
1118
|
-
beforeRedirect(
|
|
1119
|
-
m.sanitizeOptions(
|
|
1223
|
+
beforeRedirect(opt, responseDetails, requestDetails);
|
|
1224
|
+
m.sanitizeOptions(opt);
|
|
1120
1225
|
}
|
|
1121
1226
|
|
|
1122
1227
|
// Perform the redirected request
|
|
@@ -1125,12 +1230,12 @@ class Request extends Duplex {
|
|
|
1125
1230
|
|
|
1126
1231
|
/**
|
|
1127
1232
|
* 处理响应stream
|
|
1128
|
-
*
|
|
1233
|
+
* 自动解压,透传流,需设置 decompress = false,避免解压数据
|
|
1129
1234
|
* @param {*} res
|
|
1130
1235
|
*/
|
|
1131
1236
|
processStream(res) {
|
|
1132
1237
|
const m = this;
|
|
1133
|
-
const {opt
|
|
1238
|
+
const {opt} = m;
|
|
1134
1239
|
|
|
1135
1240
|
const streams = [res];
|
|
1136
1241
|
|
|
@@ -1143,14 +1248,14 @@ class Request extends Duplex {
|
|
|
1143
1248
|
headers: res.headers,
|
|
1144
1249
|
});
|
|
1145
1250
|
|
|
1146
|
-
if (
|
|
1147
|
-
|
|
1148
|
-
streams.push(
|
|
1251
|
+
if (opt.transformStream) {
|
|
1252
|
+
opt.transformStream.responseLength = responseLength;
|
|
1253
|
+
streams.push(opt.transformStream);
|
|
1149
1254
|
}
|
|
1150
1255
|
|
|
1151
|
-
const empty = utils.noBody(
|
|
1256
|
+
const empty = utils.noBody(opt.method, res.statusCode);
|
|
1152
1257
|
// decompress the response body transparently if required
|
|
1153
|
-
if (
|
|
1258
|
+
if (opt.decompress !== false && res.headers['content-encoding']) {
|
|
1154
1259
|
// if decompress disabled we should not decompress
|
|
1155
1260
|
// 压缩内容,加入 解压 stream,自动解压,axios v1.2 存在bug,不能自动解压
|
|
1156
1261
|
// if no content, but headers still say that it is encoded,
|
|
@@ -1191,6 +1296,7 @@ class Request extends Duplex {
|
|
|
1191
1296
|
}
|
|
1192
1297
|
}
|
|
1193
1298
|
|
|
1299
|
+
// 响应流,用于读
|
|
1194
1300
|
const responseStream = streams.length > 1 ? stream.pipeline(streams, utils.noop) : streams[0];
|
|
1195
1301
|
// 将内部 responseStream 可读流 映射到 redirectReq
|
|
1196
1302
|
|
|
@@ -1198,7 +1304,7 @@ class Request extends Duplex {
|
|
|
1198
1304
|
responseStream.redirectReq = m; // 事情触发时引用
|
|
1199
1305
|
|
|
1200
1306
|
// stream 模式,事件透传到 请求类
|
|
1201
|
-
if (
|
|
1307
|
+
if (opt.stream) {
|
|
1202
1308
|
if (m._paused) responseStream.pause();
|
|
1203
1309
|
// 写入目的流
|
|
1204
1310
|
for (const dest of m.pipedests) m.pipeDest(dest);
|
|
@@ -1229,20 +1335,26 @@ class Request extends Duplex {
|
|
|
1229
1335
|
// Read Stream API
|
|
1230
1336
|
|
|
1231
1337
|
/**
|
|
1338
|
+
* 建立读取流管道
|
|
1232
1339
|
* read stream to write stream
|
|
1233
1340
|
* pipe 只是建立连接管道,后续自动传输数据
|
|
1234
|
-
* @
|
|
1235
|
-
* @
|
|
1236
|
-
* @
|
|
1341
|
+
* @override - 重写父类方法
|
|
1342
|
+
* @template T - 需要模板
|
|
1343
|
+
* @param {T & stream.Writable} dest - The writable stream to which data is written.
|
|
1344
|
+
* @param {Object} [opts] - Optional configuration object.
|
|
1345
|
+
* @param {boolean} [opts.end=true] - Whether to end the writable stream when the readable stream ends.
|
|
1346
|
+
* @returns {T} The destination stream.
|
|
1237
1347
|
*/
|
|
1238
|
-
pipe(dest, opts) {
|
|
1348
|
+
pipe(dest, opts = {}) {
|
|
1239
1349
|
const m = this;
|
|
1240
|
-
|
|
1350
|
+
// m.pipe()
|
|
1241
1351
|
// 请求已响应
|
|
1242
1352
|
if (m.responseStream) {
|
|
1243
1353
|
// 已有数据,不可pipe
|
|
1244
|
-
if (m._destdata)
|
|
1245
|
-
|
|
1354
|
+
if (m._destdata)
|
|
1355
|
+
m.emit('error', new Error('You cannot pipe after data has been emitted from the response.'));
|
|
1356
|
+
else if (m._respended)
|
|
1357
|
+
m.emit('error', new Error('You cannot pipe after the response has been ended.'));
|
|
1246
1358
|
else {
|
|
1247
1359
|
// stream.Stream.prototype.pipe.call(self, dest, opts);
|
|
1248
1360
|
super.pipe(dest, opts); // 建立连接管道,自动传输数据
|
|
@@ -1269,8 +1381,13 @@ class Request extends Duplex {
|
|
|
1269
1381
|
// 请求已响应
|
|
1270
1382
|
if (m.responseStream) {
|
|
1271
1383
|
// 已有数据,不可 unpipe
|
|
1272
|
-
if (m._destdata)
|
|
1273
|
-
|
|
1384
|
+
if (m._destdata)
|
|
1385
|
+
m.emit(
|
|
1386
|
+
'error',
|
|
1387
|
+
new Error('You cannot unpipe after data has been emitted from the response.')
|
|
1388
|
+
);
|
|
1389
|
+
else if (m._respended)
|
|
1390
|
+
m.emit('error', new Error('You cannot unpipe after the response has been ended.'));
|
|
1274
1391
|
else {
|
|
1275
1392
|
// stream.Stream.prototype.pipe.call(self, dest, opts);
|
|
1276
1393
|
super.unpipe(dest); // 建立连接管道,自动传输数据
|
|
@@ -1301,7 +1418,7 @@ class Request extends Duplex {
|
|
|
1301
1418
|
if (response?.headers && dest.headers && !dest.headersSent) {
|
|
1302
1419
|
const caseless = new Caseless(response.headers);
|
|
1303
1420
|
if (caseless.has('content-type')) {
|
|
1304
|
-
const ctname = caseless.has('content-type');
|
|
1421
|
+
const ctname = /** @type {string} */ (caseless.has('content-type'));
|
|
1305
1422
|
if (dest.setHeader) {
|
|
1306
1423
|
dest.setHeader(ctname, response.headers[ctname]);
|
|
1307
1424
|
} else {
|
|
@@ -1310,7 +1427,7 @@ class Request extends Duplex {
|
|
|
1310
1427
|
}
|
|
1311
1428
|
|
|
1312
1429
|
if (caseless.has('content-length')) {
|
|
1313
|
-
const clname = caseless.has('content-length');
|
|
1430
|
+
const clname = /** @type {string} */ (caseless.has('content-length'));
|
|
1314
1431
|
if (dest.setHeader) {
|
|
1315
1432
|
dest.setHeader(clname, response.headers[clname]);
|
|
1316
1433
|
} else {
|
|
@@ -1320,37 +1437,32 @@ class Request extends Duplex {
|
|
|
1320
1437
|
}
|
|
1321
1438
|
|
|
1322
1439
|
if (response?.headers && dest.setHeader && !dest.headersSent) {
|
|
1323
|
-
for (const
|
|
1324
|
-
|
|
1325
|
-
}
|
|
1440
|
+
for (const k of Object.keys(response.headers)) dest.setHeader(k, response.headers[k]);
|
|
1441
|
+
|
|
1326
1442
|
dest.statusCode = response.statusCode;
|
|
1327
1443
|
}
|
|
1328
1444
|
|
|
1329
|
-
if (m.pipefilter)
|
|
1330
|
-
m.pipefilter(response, dest);
|
|
1331
|
-
}
|
|
1445
|
+
if (m.pipefilter) m.pipefilter(response, dest);
|
|
1332
1446
|
}
|
|
1333
1447
|
|
|
1334
1448
|
/**
|
|
1335
1449
|
* 暂停read流
|
|
1336
|
-
* @param {...any} args
|
|
1337
1450
|
*/
|
|
1338
|
-
pause(
|
|
1451
|
+
pause() {
|
|
1339
1452
|
const m = this;
|
|
1340
1453
|
// 没有流
|
|
1341
1454
|
if (!m.responseStream) m._paused = true;
|
|
1342
|
-
else m.responseStream.pause(
|
|
1455
|
+
else m.responseStream.pause();
|
|
1343
1456
|
return m
|
|
1344
1457
|
}
|
|
1345
1458
|
|
|
1346
1459
|
/**
|
|
1347
|
-
* 继续read
|
|
1348
|
-
* @param {...any} args
|
|
1460
|
+
* 继续read响应流
|
|
1349
1461
|
*/
|
|
1350
|
-
resume(
|
|
1462
|
+
resume() {
|
|
1351
1463
|
const m = this;
|
|
1352
1464
|
if (!m.responseStream) m._paused = false;
|
|
1353
|
-
else m.responseStream.resume(
|
|
1465
|
+
else m.responseStream.resume();
|
|
1354
1466
|
return m
|
|
1355
1467
|
}
|
|
1356
1468
|
|
|
@@ -1372,18 +1484,32 @@ function destroyRequest(request, error) {
|
|
|
1372
1484
|
request.destroy(error);
|
|
1373
1485
|
}
|
|
1374
1486
|
|
|
1487
|
+
/**
|
|
1488
|
+
*
|
|
1489
|
+
* @param {RegExp} regex
|
|
1490
|
+
* @param {Object.<string, string>} headers
|
|
1491
|
+
* @returns
|
|
1492
|
+
*/
|
|
1375
1493
|
function removeMatchingHeaders(regex, headers) {
|
|
1376
1494
|
let lastValue;
|
|
1377
|
-
Object.keys(headers)
|
|
1495
|
+
for (const k of Object.keys(headers)) {
|
|
1378
1496
|
if (regex.test(k)) {
|
|
1379
1497
|
lastValue = headers[k];
|
|
1380
1498
|
delete headers[k];
|
|
1381
1499
|
}
|
|
1382
|
-
}
|
|
1500
|
+
}
|
|
1383
1501
|
|
|
1384
|
-
return lastValue === null || typeof lastValue === 'undefined'
|
|
1502
|
+
return lastValue === null || typeof lastValue === 'undefined'
|
|
1503
|
+
? undefined
|
|
1504
|
+
: String(lastValue).trim()
|
|
1385
1505
|
}
|
|
1386
1506
|
|
|
1507
|
+
/**
|
|
1508
|
+
*
|
|
1509
|
+
* @param {string} subdomain
|
|
1510
|
+
* @param {string} domain
|
|
1511
|
+
* @returns
|
|
1512
|
+
*/
|
|
1387
1513
|
function isSubdomain(subdomain, domain) {
|
|
1388
1514
|
assert(utils.isString(subdomain) && utils.isString(domain));
|
|
1389
1515
|
const dot = subdomain.length - domain.length - 1;
|