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