@wiajs/request 3.0.3 → 3.0.6
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 +68 -66
- package/dist/request.mjs +62 -61
- package/index.js +1 -1
- package/lib/index.js +10 -7
- package/lib/request.js +75 -73
- package/package.json +1 -1
package/dist/request.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* wia request v3.0.
|
|
2
|
+
* wia request v3.0.5
|
|
3
3
|
* (c) 2022-2024 Sibyl Yu and contributors
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
@@ -14,6 +14,7 @@ const stream = require('node:stream');
|
|
|
14
14
|
const zlib = require('node:zlib');
|
|
15
15
|
const mime = require('mime-types');
|
|
16
16
|
|
|
17
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
17
18
|
class ZlibTransform extends stream.Transform {
|
|
18
19
|
__transform(chunk, encoding, callback) {
|
|
19
20
|
this.push(chunk);
|
|
@@ -42,12 +43,12 @@ class ZlibTransform extends stream.Transform {
|
|
|
42
43
|
* utils for request
|
|
43
44
|
*/
|
|
44
45
|
|
|
45
|
-
const {URL} = url;
|
|
46
|
+
const {URL: URL$1} = url;
|
|
46
47
|
|
|
47
48
|
// Whether to use the native URL object or the legacy url module
|
|
48
49
|
let useNativeURL = false;
|
|
49
50
|
try {
|
|
50
|
-
assert(new URL(''));
|
|
51
|
+
assert(new URL$1(''));
|
|
51
52
|
} catch (error) {
|
|
52
53
|
useNativeURL = error.code === 'ERR_INVALID_URL';
|
|
53
54
|
}
|
|
@@ -193,7 +194,7 @@ const noop = () => {};
|
|
|
193
194
|
* @returns
|
|
194
195
|
*/
|
|
195
196
|
function isURL(value) {
|
|
196
|
-
return URL && value instanceof URL
|
|
197
|
+
return URL$1 && value instanceof URL$1
|
|
197
198
|
}
|
|
198
199
|
|
|
199
200
|
function isReadStream(rs) {
|
|
@@ -235,7 +236,7 @@ function parseUrl(input) {
|
|
|
235
236
|
let parsed;
|
|
236
237
|
// istanbul ignore else
|
|
237
238
|
if (useNativeURL) {
|
|
238
|
-
parsed = new URL(input);
|
|
239
|
+
parsed = new URL$1(input);
|
|
239
240
|
} else {
|
|
240
241
|
// Ensure the URL is valid and absolute
|
|
241
242
|
parsed = validateUrl(url.parse(input));
|
|
@@ -269,7 +270,7 @@ function validateUrl(input) {
|
|
|
269
270
|
*/
|
|
270
271
|
function resolveUrl(relative, base) {
|
|
271
272
|
// istanbul ignore next
|
|
272
|
-
return useNativeURL ? new URL(relative, base) : parseUrl(url.resolve(base, relative))
|
|
273
|
+
return useNativeURL ? new URL$1(relative, base) : parseUrl(url.resolve(base, relative))
|
|
273
274
|
}
|
|
274
275
|
|
|
275
276
|
/**
|
|
@@ -405,7 +406,7 @@ class Caseless {
|
|
|
405
406
|
* https://github.com/follow-redirects/follow-redirects
|
|
406
407
|
*/
|
|
407
408
|
|
|
408
|
-
const log$1 = log$2.log({env: `wia:req:${log$2.name(__filename)}`});
|
|
409
|
+
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
|
|
409
410
|
|
|
410
411
|
const httpModules = {'http:': http, 'https:': https};
|
|
411
412
|
|
|
@@ -444,7 +445,7 @@ const writeEventEmit = Object.create(null);
|
|
|
444
445
|
for (const ev of writeEvents)
|
|
445
446
|
writeEventEmit[ev] = function (...args) {
|
|
446
447
|
const m = this; // 事件回调,this === clientRequest 实例
|
|
447
|
-
log$1
|
|
448
|
+
log$1('req event', {ev});
|
|
448
449
|
m.redirectReq.emit(ev, ...args); // req 事情映射到 redirectReq 上触发
|
|
449
450
|
};
|
|
450
451
|
|
|
@@ -455,7 +456,7 @@ const readEventEmit = Object.create(null);
|
|
|
455
456
|
for (const ev of readEvents)
|
|
456
457
|
readEventEmit[ev] = function (...args) {
|
|
457
458
|
const m = this; // 事件回调,this === clientRequest 实例
|
|
458
|
-
log$1
|
|
459
|
+
log$1('res event', {ev});
|
|
459
460
|
m.redirectReq.emit(ev, ...args); // 向上触发事件
|
|
460
461
|
};
|
|
461
462
|
|
|
@@ -502,19 +503,20 @@ class Request extends stream.Duplex {
|
|
|
502
503
|
|
|
503
504
|
/**
|
|
504
505
|
* responseCallback 原消息处理回调
|
|
505
|
-
* @param {*}
|
|
506
|
+
* @param {*} opts
|
|
506
507
|
* @param {*} resCallback
|
|
507
508
|
*/
|
|
508
|
-
constructor(
|
|
509
|
+
constructor(opts, resCallback) {
|
|
509
510
|
super();
|
|
510
511
|
const m = this;
|
|
511
512
|
|
|
512
513
|
// log({options}, 'constructor');
|
|
513
514
|
|
|
514
515
|
// Initialize the request
|
|
515
|
-
m.
|
|
516
|
-
m.
|
|
517
|
-
m.headers =
|
|
516
|
+
m.sanitizeOptions(opts);
|
|
517
|
+
m.opt = opts;
|
|
518
|
+
m.headers = opts.headers;
|
|
519
|
+
// log({opts}, 'constructor')
|
|
518
520
|
m._ended = false;
|
|
519
521
|
m._ending = false;
|
|
520
522
|
m._redirectCount = 0;
|
|
@@ -528,9 +530,9 @@ class Request extends stream.Duplex {
|
|
|
528
530
|
m.resCallback = resCallback;
|
|
529
531
|
// React to responses of native requests
|
|
530
532
|
// 接管 response 事件,非重定向,触发 response 事件
|
|
531
|
-
m._onResponse =
|
|
533
|
+
m._onResponse = res => {
|
|
532
534
|
try {
|
|
533
|
-
m.processResponse(
|
|
535
|
+
m.processResponse(res);
|
|
534
536
|
} catch (cause) {
|
|
535
537
|
m.emit('error', cause instanceof RedirectionError ? cause : new RedirectionError({cause: cause}));
|
|
536
538
|
}
|
|
@@ -556,7 +558,7 @@ class Request extends stream.Duplex {
|
|
|
556
558
|
}
|
|
557
559
|
|
|
558
560
|
// 流模式
|
|
559
|
-
if (
|
|
561
|
+
if (opts.stream) {
|
|
560
562
|
// 被 pipe 作为目标时触发,拷贝 src headers
|
|
561
563
|
m.on(
|
|
562
564
|
'pipe',
|
|
@@ -581,12 +583,11 @@ class Request extends stream.Duplex {
|
|
|
581
583
|
}
|
|
582
584
|
}
|
|
583
585
|
|
|
584
|
-
|
|
585
|
-
// m.method = src.method
|
|
586
|
-
// }
|
|
586
|
+
if (src.opt.method && !m.opt.method) m.opt.method = src.opt.method;
|
|
587
587
|
}
|
|
588
588
|
}
|
|
589
589
|
);
|
|
590
|
+
}
|
|
590
591
|
|
|
591
592
|
// Perform the first request
|
|
592
593
|
// m.request(); // 写入数据时执行,否则 pipe时无法写入header
|
|
@@ -598,10 +599,11 @@ class Request extends stream.Duplex {
|
|
|
598
599
|
*/
|
|
599
600
|
request() {
|
|
600
601
|
let R = null;
|
|
602
|
+
const m = this;
|
|
603
|
+
const {opt} = m;
|
|
601
604
|
|
|
602
605
|
try {
|
|
603
|
-
|
|
604
|
-
// read stream
|
|
606
|
+
// reset read stream
|
|
605
607
|
m.response = null;
|
|
606
608
|
m.responseStarted = false;
|
|
607
609
|
m.responseStream = null;
|
|
@@ -614,32 +616,29 @@ class Request extends stream.Duplex {
|
|
|
614
616
|
// m.httpModule = httpModules[protocol];
|
|
615
617
|
|
|
616
618
|
// Load the native protocol
|
|
617
|
-
let {protocol} =
|
|
618
|
-
const {agents} =
|
|
619
|
+
let {protocol} = opt;
|
|
620
|
+
const {agents} = opt;
|
|
619
621
|
|
|
620
622
|
// 代理以目的网址协议为准
|
|
621
623
|
// If specified, use the agent corresponding to the protocol
|
|
622
624
|
// (HTTP and HTTPS use different types of agents)
|
|
623
625
|
if (agents) {
|
|
624
626
|
const scheme = protocol.slice(0, -1);
|
|
625
|
-
|
|
626
|
-
}
|
|
627
|
+
opt.agent = agents[scheme];
|
|
627
628
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
629
|
+
// http 非隧道代理模式,模块以代理主机为准,其他以目的网址为准
|
|
630
|
+
// 代理内部会根据代理协议选择 http(s) 发起请求创建连接
|
|
631
|
+
if (protocol === 'http:' && agents.http) {
|
|
632
|
+
protocol = agents.http.proxy && !agents.http.tunnel ? agents.http.proxy.protocol : protocol;
|
|
633
|
+
}
|
|
632
634
|
}
|
|
633
635
|
|
|
634
636
|
const httpModule = httpModules[protocol];
|
|
635
637
|
if (!httpModule) throw TypeError(`Unsupported protocol: ${protocol}`)
|
|
636
638
|
|
|
637
|
-
log$1
|
|
638
|
-
|
|
639
|
-
debugger
|
|
640
|
-
|
|
639
|
+
log$1({opt, protocol}, 'request');
|
|
641
640
|
// Create the native request and set up its event handlers
|
|
642
|
-
const req = httpModule.request(
|
|
641
|
+
const req = httpModule.request(opt, m._onResponse);
|
|
643
642
|
m._currentRequest = req;
|
|
644
643
|
|
|
645
644
|
req.redirectReq = m;
|
|
@@ -650,7 +649,7 @@ class Request extends stream.Duplex {
|
|
|
650
649
|
// a client MUST send only the absolute path […] as the request-target.
|
|
651
650
|
// When making a request to a proxy, […]
|
|
652
651
|
// a client MUST send the target URI in absolute-form […].
|
|
653
|
-
m._currentUrl = /^\//.test(
|
|
652
|
+
m._currentUrl = /^\//.test(opt.path) ? url.format(opt) : opt.path;
|
|
654
653
|
|
|
655
654
|
// End a redirected request
|
|
656
655
|
// (The first request must be ended explicitly with RedirectableRequest#end)
|
|
@@ -745,7 +744,7 @@ class Request extends stream.Duplex {
|
|
|
745
744
|
}
|
|
746
745
|
|
|
747
746
|
// Only write when we don't exceed the maximum body length
|
|
748
|
-
if (m._requestBodyLength + chunk.length <= m.
|
|
747
|
+
if (m._requestBodyLength + chunk.length <= m.opt.maxBodyLength) {
|
|
749
748
|
m._requestBodyLength += chunk.length;
|
|
750
749
|
m._requestBodyBuffers.push({data: chunk, encoding});
|
|
751
750
|
m._currentRequest.write(chunk, encoding, cb);
|
|
@@ -801,7 +800,7 @@ class Request extends stream.Duplex {
|
|
|
801
800
|
* @returns
|
|
802
801
|
*/
|
|
803
802
|
hasHeader(name) {
|
|
804
|
-
return this.
|
|
803
|
+
return this.opt.headers.includes(name)
|
|
805
804
|
}
|
|
806
805
|
|
|
807
806
|
/**
|
|
@@ -810,7 +809,7 @@ class Request extends stream.Duplex {
|
|
|
810
809
|
* @returns {string}
|
|
811
810
|
*/
|
|
812
811
|
getHeader(name) {
|
|
813
|
-
return this.
|
|
812
|
+
return this.opt.headers[name]
|
|
814
813
|
}
|
|
815
814
|
|
|
816
815
|
/**
|
|
@@ -818,7 +817,7 @@ class Request extends stream.Duplex {
|
|
|
818
817
|
* @param {string} name
|
|
819
818
|
*/
|
|
820
819
|
setHeader(name, value) {
|
|
821
|
-
this.
|
|
820
|
+
this.opt.headers[name] = value;
|
|
822
821
|
this._currentRequest?.setHeader(name, value);
|
|
823
822
|
}
|
|
824
823
|
|
|
@@ -827,7 +826,7 @@ class Request extends stream.Duplex {
|
|
|
827
826
|
* @param {string} name
|
|
828
827
|
*/
|
|
829
828
|
removeHeader(name) {
|
|
830
|
-
delete this.
|
|
829
|
+
delete this.opt.headers[name];
|
|
831
830
|
this._currentRequest?.removeHeader(name);
|
|
832
831
|
}
|
|
833
832
|
|
|
@@ -914,7 +913,7 @@ class Request extends stream.Duplex {
|
|
|
914
913
|
return m
|
|
915
914
|
}
|
|
916
915
|
|
|
917
|
-
|
|
916
|
+
sanitizeOptions(options) {
|
|
918
917
|
// Ensure headers are always present
|
|
919
918
|
if (!options.headers) options.headers = {};
|
|
920
919
|
|
|
@@ -951,7 +950,7 @@ class Request extends stream.Duplex {
|
|
|
951
950
|
|
|
952
951
|
// Store the redirected response
|
|
953
952
|
const {statusCode} = response;
|
|
954
|
-
if (m.
|
|
953
|
+
if (m.opt.trackRedirects) {
|
|
955
954
|
m._redirects.push({
|
|
956
955
|
url: m._currentUrl,
|
|
957
956
|
headers: response.headers,
|
|
@@ -971,7 +970,7 @@ class Request extends stream.Duplex {
|
|
|
971
970
|
|
|
972
971
|
log$1('processResponse', {statusCode, headers: response.headers});
|
|
973
972
|
|
|
974
|
-
if (!location || m.
|
|
973
|
+
if (!location || m.opt.followRedirects === false || statusCode < 300 || statusCode >= 400) {
|
|
975
974
|
// 非重定向,返回给原始回调处理
|
|
976
975
|
response.responseUrl = m._currentUrl;
|
|
977
976
|
response.redirects = m._redirects;
|
|
@@ -1010,16 +1009,16 @@ class Request extends stream.Duplex {
|
|
|
1010
1009
|
|
|
1011
1010
|
// RFC7231§6.4: A client SHOULD detect and intervene
|
|
1012
1011
|
// in cyclical redirections (i.e., "infinite" redirection loops).
|
|
1013
|
-
if (++m._redirectCount > m.
|
|
1012
|
+
if (++m._redirectCount > m.opt.maxRedirects) throw new TooManyRedirectsError()
|
|
1014
1013
|
|
|
1015
1014
|
// Store the request headers if applicable
|
|
1016
1015
|
let requestHeaders;
|
|
1017
|
-
const {beforeRedirect} = m.
|
|
1016
|
+
const {beforeRedirect} = m.opt;
|
|
1018
1017
|
if (beforeRedirect) {
|
|
1019
1018
|
requestHeaders = {
|
|
1020
1019
|
// The Host header was set by nativeProtocol.request
|
|
1021
1020
|
Host: response.req.getHeader('host'),
|
|
1022
|
-
...m.
|
|
1021
|
+
...m.opt.headers,
|
|
1023
1022
|
};
|
|
1024
1023
|
}
|
|
1025
1024
|
|
|
@@ -1027,23 +1026,23 @@ class Request extends stream.Duplex {
|
|
|
1027
1026
|
// care for methods not known to be safe, […]
|
|
1028
1027
|
// RFC7231§6.4.2–3: For historical reasons, a user agent MAY change
|
|
1029
1028
|
// the request method from POST to GET for the subsequent request.
|
|
1030
|
-
const {method} = m.
|
|
1029
|
+
const {method} = m.opt;
|
|
1031
1030
|
if (
|
|
1032
|
-
((statusCode === 301 || statusCode === 302) && m.
|
|
1031
|
+
((statusCode === 301 || statusCode === 302) && m.opt.method === 'POST') ||
|
|
1033
1032
|
// RFC7231§6.4.4: The 303 (See Other) status code indicates that
|
|
1034
1033
|
// the server is redirecting the user agent to a different resource […]
|
|
1035
1034
|
// A user agent can perform a retrieval request targeting that URI
|
|
1036
1035
|
// (a GET or HEAD request if using HTTP) […]
|
|
1037
|
-
(statusCode === 303 && !/^(?:GET|HEAD)$/.test(m.
|
|
1036
|
+
(statusCode === 303 && !/^(?:GET|HEAD)$/.test(m.opt.method))
|
|
1038
1037
|
) {
|
|
1039
|
-
m.
|
|
1038
|
+
m.opt.method = 'GET';
|
|
1040
1039
|
// Drop a possible entity and headers related to it
|
|
1041
1040
|
m._requestBodyBuffers = [];
|
|
1042
|
-
removeMatchingHeaders(/^content-/i, m.
|
|
1041
|
+
removeMatchingHeaders(/^content-/i, m.opt.headers);
|
|
1043
1042
|
}
|
|
1044
1043
|
|
|
1045
1044
|
// Drop the Host header, as the redirect might lead to a different host
|
|
1046
|
-
const currentHostHeader = removeMatchingHeaders(/^host$/i, m.
|
|
1045
|
+
const currentHostHeader = removeMatchingHeaders(/^host$/i, m.opt.headers);
|
|
1047
1046
|
|
|
1048
1047
|
// If the redirect is relative, carry over the host of the last request
|
|
1049
1048
|
const currentUrlParts = utils.parseUrl(m._currentUrl);
|
|
@@ -1057,7 +1056,7 @@ class Request extends stream.Duplex {
|
|
|
1057
1056
|
log$1({redirectUrl}, 'redirecting to');
|
|
1058
1057
|
m._isRedirect = true;
|
|
1059
1058
|
// 覆盖原 url 解析部分,包括 protocol、hostname、port等
|
|
1060
|
-
utils.spreadUrlObject(redirectUrl, m.
|
|
1059
|
+
utils.spreadUrlObject(redirectUrl, m.opt);
|
|
1061
1060
|
|
|
1062
1061
|
// Drop confidential headers when redirecting to a less secure protocol
|
|
1063
1062
|
// or to a different domain that is not a superdomain
|
|
@@ -1065,7 +1064,7 @@ class Request extends stream.Duplex {
|
|
|
1065
1064
|
(redirectUrl.protocol !== currentUrlParts.protocol && redirectUrl.protocol !== 'https:') ||
|
|
1066
1065
|
(redirectUrl.host !== currentHost && !isSubdomain(redirectUrl.host, currentHost))
|
|
1067
1066
|
) {
|
|
1068
|
-
removeMatchingHeaders(/^(?:(?:proxy-)?authorization|cookie)$/i, this.
|
|
1067
|
+
removeMatchingHeaders(/^(?:(?:proxy-)?authorization|cookie)$/i, this.opt.headers);
|
|
1069
1068
|
}
|
|
1070
1069
|
|
|
1071
1070
|
// Evaluate the beforeRedirect callback
|
|
@@ -1080,8 +1079,8 @@ class Request extends stream.Duplex {
|
|
|
1080
1079
|
headers: requestHeaders,
|
|
1081
1080
|
};
|
|
1082
1081
|
|
|
1083
|
-
beforeRedirect(m.
|
|
1084
|
-
m.
|
|
1082
|
+
beforeRedirect(m.opt, responseDetails, requestDetails);
|
|
1083
|
+
m.sanitizeOptions(m.opt);
|
|
1085
1084
|
}
|
|
1086
1085
|
|
|
1087
1086
|
// Perform the redirected request
|
|
@@ -1095,7 +1094,7 @@ class Request extends stream.Duplex {
|
|
|
1095
1094
|
*/
|
|
1096
1095
|
processStream(res) {
|
|
1097
1096
|
const m = this;
|
|
1098
|
-
const {
|
|
1097
|
+
const {opt: opts} = m;
|
|
1099
1098
|
|
|
1100
1099
|
const streams = [res];
|
|
1101
1100
|
|
|
@@ -1260,7 +1259,7 @@ class Request extends stream.Duplex {
|
|
|
1260
1259
|
*/
|
|
1261
1260
|
pipeDest(dest) {
|
|
1262
1261
|
const m = this;
|
|
1263
|
-
const {response
|
|
1262
|
+
const {response} = m;
|
|
1264
1263
|
|
|
1265
1264
|
// Called after the response is received
|
|
1266
1265
|
if (response?.headers && dest.headers && !dest.headersSent) {
|
|
@@ -1291,9 +1290,9 @@ class Request extends stream.Duplex {
|
|
|
1291
1290
|
dest.statusCode = response.statusCode;
|
|
1292
1291
|
}
|
|
1293
1292
|
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1293
|
+
if (m.pipefilter) {
|
|
1294
|
+
m.pipefilter(response, dest);
|
|
1295
|
+
}
|
|
1297
1296
|
}
|
|
1298
1297
|
|
|
1299
1298
|
/**
|
|
@@ -1362,16 +1361,16 @@ function isSubdomain(subdomain, domain) {
|
|
|
1362
1361
|
*/
|
|
1363
1362
|
|
|
1364
1363
|
|
|
1365
|
-
const log = log$2.log({env: `wia:req:${log$2.name(__filename)}`})
|
|
1364
|
+
const log = 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
|
|
1366
1365
|
|
|
1367
1366
|
// Preventive platform detection
|
|
1368
|
-
// istanbul ignore
|
|
1367
|
+
// istanbul ignore
|
|
1369
1368
|
;(function detectUnsupportedEnvironment() {
|
|
1370
1369
|
const looksLikeNode = typeof process !== 'undefined';
|
|
1371
1370
|
const looksLikeBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
1372
1371
|
const looksLikeV8 = utils.isFunction(Error.captureStackTrace);
|
|
1373
1372
|
if (!looksLikeNode && (looksLikeBrowser || !looksLikeV8)) {
|
|
1374
|
-
|
|
1373
|
+
console.warn('The follow-redirects package should be excluded from browser builds.');
|
|
1375
1374
|
}
|
|
1376
1375
|
})();
|
|
1377
1376
|
|
|
@@ -1389,6 +1388,7 @@ const log = log$2.log({env: `wia:req:${log$2.name(__filename)}`})
|
|
|
1389
1388
|
* @returns
|
|
1390
1389
|
*/
|
|
1391
1390
|
function init(uri, options, callback) {
|
|
1391
|
+
let R;
|
|
1392
1392
|
try {
|
|
1393
1393
|
// Parse parameters, ensuring that input is an object
|
|
1394
1394
|
if (utils.isURL(uri)) uri = utils.spreadUrlObject(uri);
|
|
@@ -1413,6 +1413,7 @@ function init(uri, options, callback) {
|
|
|
1413
1413
|
if (!utils.isString(options.host) && !utils.isString(options.hostname)) options.hostname = '::1';
|
|
1414
1414
|
|
|
1415
1415
|
R = {opts: options, cb: callback};
|
|
1416
|
+
// log({R}, 'init')
|
|
1416
1417
|
} catch (e) {
|
|
1417
1418
|
log.err(e, 'init');
|
|
1418
1419
|
}
|
|
@@ -1436,7 +1437,6 @@ function request(uri, options, callback) {
|
|
|
1436
1437
|
|
|
1437
1438
|
try {
|
|
1438
1439
|
const {opts, cb} = init(uri, options, callback);
|
|
1439
|
-
// log.debug('request', {options})
|
|
1440
1440
|
R = new Request(opts, cb);
|
|
1441
1441
|
} catch (e) {
|
|
1442
1442
|
log.err(e, 'request');
|
|
@@ -1454,6 +1454,8 @@ function request(uri, options, callback) {
|
|
|
1454
1454
|
*/
|
|
1455
1455
|
function fn(verb) {
|
|
1456
1456
|
const method = verb.toUpperCase();
|
|
1457
|
+
|
|
1458
|
+
// @ts-ignore
|
|
1457
1459
|
return (uri, options, callback) => {
|
|
1458
1460
|
const {opts, cb} = init(uri, options, callback);
|
|
1459
1461
|
opts.method = method;
|
package/dist/request.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* wia request v3.0.
|
|
2
|
+
* wia request v3.0.5
|
|
3
3
|
* (c) 2022-2024 Sibyl Yu and contributors
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
@@ -403,7 +403,7 @@ class Caseless {
|
|
|
403
403
|
* https://github.com/follow-redirects/follow-redirects
|
|
404
404
|
*/
|
|
405
405
|
|
|
406
|
-
const log$1 = log$2({env: `wia:req:${name(
|
|
406
|
+
const log$1 = log$2({env: `wia:req:${name(import.meta.url)}`}); // __filename
|
|
407
407
|
|
|
408
408
|
const httpModules = {'http:': http, 'https:': https};
|
|
409
409
|
|
|
@@ -442,7 +442,7 @@ const writeEventEmit = Object.create(null);
|
|
|
442
442
|
for (const ev of writeEvents)
|
|
443
443
|
writeEventEmit[ev] = function (...args) {
|
|
444
444
|
const m = this; // 事件回调,this === clientRequest 实例
|
|
445
|
-
log$1
|
|
445
|
+
log$1('req event', {ev});
|
|
446
446
|
m.redirectReq.emit(ev, ...args); // req 事情映射到 redirectReq 上触发
|
|
447
447
|
};
|
|
448
448
|
|
|
@@ -453,7 +453,7 @@ const readEventEmit = Object.create(null);
|
|
|
453
453
|
for (const ev of readEvents)
|
|
454
454
|
readEventEmit[ev] = function (...args) {
|
|
455
455
|
const m = this; // 事件回调,this === clientRequest 实例
|
|
456
|
-
log$1
|
|
456
|
+
log$1('res event', {ev});
|
|
457
457
|
m.redirectReq.emit(ev, ...args); // 向上触发事件
|
|
458
458
|
};
|
|
459
459
|
|
|
@@ -500,19 +500,20 @@ class Request extends Duplex {
|
|
|
500
500
|
|
|
501
501
|
/**
|
|
502
502
|
* responseCallback 原消息处理回调
|
|
503
|
-
* @param {*}
|
|
503
|
+
* @param {*} opts
|
|
504
504
|
* @param {*} resCallback
|
|
505
505
|
*/
|
|
506
|
-
constructor(
|
|
506
|
+
constructor(opts, resCallback) {
|
|
507
507
|
super();
|
|
508
508
|
const m = this;
|
|
509
509
|
|
|
510
510
|
// log({options}, 'constructor');
|
|
511
511
|
|
|
512
512
|
// Initialize the request
|
|
513
|
-
m.
|
|
514
|
-
m.
|
|
515
|
-
m.headers =
|
|
513
|
+
m.sanitizeOptions(opts);
|
|
514
|
+
m.opt = opts;
|
|
515
|
+
m.headers = opts.headers;
|
|
516
|
+
// log({opts}, 'constructor')
|
|
516
517
|
m._ended = false;
|
|
517
518
|
m._ending = false;
|
|
518
519
|
m._redirectCount = 0;
|
|
@@ -526,9 +527,9 @@ class Request extends Duplex {
|
|
|
526
527
|
m.resCallback = resCallback;
|
|
527
528
|
// React to responses of native requests
|
|
528
529
|
// 接管 response 事件,非重定向,触发 response 事件
|
|
529
|
-
m._onResponse =
|
|
530
|
+
m._onResponse = res => {
|
|
530
531
|
try {
|
|
531
|
-
m.processResponse(
|
|
532
|
+
m.processResponse(res);
|
|
532
533
|
} catch (cause) {
|
|
533
534
|
m.emit('error', cause instanceof RedirectionError ? cause : new RedirectionError({cause: cause}));
|
|
534
535
|
}
|
|
@@ -554,7 +555,7 @@ class Request extends Duplex {
|
|
|
554
555
|
}
|
|
555
556
|
|
|
556
557
|
// 流模式
|
|
557
|
-
if (
|
|
558
|
+
if (opts.stream) {
|
|
558
559
|
// 被 pipe 作为目标时触发,拷贝 src headers
|
|
559
560
|
m.on(
|
|
560
561
|
'pipe',
|
|
@@ -579,12 +580,11 @@ class Request extends Duplex {
|
|
|
579
580
|
}
|
|
580
581
|
}
|
|
581
582
|
|
|
582
|
-
|
|
583
|
-
// m.method = src.method
|
|
584
|
-
// }
|
|
583
|
+
if (src.opt.method && !m.opt.method) m.opt.method = src.opt.method;
|
|
585
584
|
}
|
|
586
585
|
}
|
|
587
586
|
);
|
|
587
|
+
}
|
|
588
588
|
|
|
589
589
|
// Perform the first request
|
|
590
590
|
// m.request(); // 写入数据时执行,否则 pipe时无法写入header
|
|
@@ -596,10 +596,11 @@ class Request extends Duplex {
|
|
|
596
596
|
*/
|
|
597
597
|
request() {
|
|
598
598
|
let R = null;
|
|
599
|
+
const m = this;
|
|
600
|
+
const {opt} = m;
|
|
599
601
|
|
|
600
602
|
try {
|
|
601
|
-
|
|
602
|
-
// read stream
|
|
603
|
+
// reset read stream
|
|
603
604
|
m.response = null;
|
|
604
605
|
m.responseStarted = false;
|
|
605
606
|
m.responseStream = null;
|
|
@@ -612,32 +613,29 @@ class Request extends Duplex {
|
|
|
612
613
|
// m.httpModule = httpModules[protocol];
|
|
613
614
|
|
|
614
615
|
// Load the native protocol
|
|
615
|
-
let {protocol} =
|
|
616
|
-
const {agents} =
|
|
616
|
+
let {protocol} = opt;
|
|
617
|
+
const {agents} = opt;
|
|
617
618
|
|
|
618
619
|
// 代理以目的网址协议为准
|
|
619
620
|
// If specified, use the agent corresponding to the protocol
|
|
620
621
|
// (HTTP and HTTPS use different types of agents)
|
|
621
622
|
if (agents) {
|
|
622
623
|
const scheme = protocol.slice(0, -1);
|
|
623
|
-
|
|
624
|
-
}
|
|
624
|
+
opt.agent = agents[scheme];
|
|
625
625
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
626
|
+
// http 非隧道代理模式,模块以代理主机为准,其他以目的网址为准
|
|
627
|
+
// 代理内部会根据代理协议选择 http(s) 发起请求创建连接
|
|
628
|
+
if (protocol === 'http:' && agents.http) {
|
|
629
|
+
protocol = agents.http.proxy && !agents.http.tunnel ? agents.http.proxy.protocol : protocol;
|
|
630
|
+
}
|
|
630
631
|
}
|
|
631
632
|
|
|
632
633
|
const httpModule = httpModules[protocol];
|
|
633
634
|
if (!httpModule) throw TypeError(`Unsupported protocol: ${protocol}`)
|
|
634
635
|
|
|
635
|
-
log$1
|
|
636
|
-
|
|
637
|
-
debugger
|
|
638
|
-
|
|
636
|
+
log$1({opt, protocol}, 'request');
|
|
639
637
|
// Create the native request and set up its event handlers
|
|
640
|
-
const req = httpModule.request(
|
|
638
|
+
const req = httpModule.request(opt, m._onResponse);
|
|
641
639
|
m._currentRequest = req;
|
|
642
640
|
|
|
643
641
|
req.redirectReq = m;
|
|
@@ -648,7 +646,7 @@ class Request extends Duplex {
|
|
|
648
646
|
// a client MUST send only the absolute path […] as the request-target.
|
|
649
647
|
// When making a request to a proxy, […]
|
|
650
648
|
// a client MUST send the target URI in absolute-form […].
|
|
651
|
-
m._currentUrl = /^\//.test(
|
|
649
|
+
m._currentUrl = /^\//.test(opt.path) ? url.format(opt) : opt.path;
|
|
652
650
|
|
|
653
651
|
// End a redirected request
|
|
654
652
|
// (The first request must be ended explicitly with RedirectableRequest#end)
|
|
@@ -743,7 +741,7 @@ class Request extends Duplex {
|
|
|
743
741
|
}
|
|
744
742
|
|
|
745
743
|
// Only write when we don't exceed the maximum body length
|
|
746
|
-
if (m._requestBodyLength + chunk.length <= m.
|
|
744
|
+
if (m._requestBodyLength + chunk.length <= m.opt.maxBodyLength) {
|
|
747
745
|
m._requestBodyLength += chunk.length;
|
|
748
746
|
m._requestBodyBuffers.push({data: chunk, encoding});
|
|
749
747
|
m._currentRequest.write(chunk, encoding, cb);
|
|
@@ -799,7 +797,7 @@ class Request extends Duplex {
|
|
|
799
797
|
* @returns
|
|
800
798
|
*/
|
|
801
799
|
hasHeader(name) {
|
|
802
|
-
return this.
|
|
800
|
+
return this.opt.headers.includes(name)
|
|
803
801
|
}
|
|
804
802
|
|
|
805
803
|
/**
|
|
@@ -808,7 +806,7 @@ class Request extends Duplex {
|
|
|
808
806
|
* @returns {string}
|
|
809
807
|
*/
|
|
810
808
|
getHeader(name) {
|
|
811
|
-
return this.
|
|
809
|
+
return this.opt.headers[name]
|
|
812
810
|
}
|
|
813
811
|
|
|
814
812
|
/**
|
|
@@ -816,7 +814,7 @@ class Request extends Duplex {
|
|
|
816
814
|
* @param {string} name
|
|
817
815
|
*/
|
|
818
816
|
setHeader(name, value) {
|
|
819
|
-
this.
|
|
817
|
+
this.opt.headers[name] = value;
|
|
820
818
|
this._currentRequest?.setHeader(name, value);
|
|
821
819
|
}
|
|
822
820
|
|
|
@@ -825,7 +823,7 @@ class Request extends Duplex {
|
|
|
825
823
|
* @param {string} name
|
|
826
824
|
*/
|
|
827
825
|
removeHeader(name) {
|
|
828
|
-
delete this.
|
|
826
|
+
delete this.opt.headers[name];
|
|
829
827
|
this._currentRequest?.removeHeader(name);
|
|
830
828
|
}
|
|
831
829
|
|
|
@@ -912,7 +910,7 @@ class Request extends Duplex {
|
|
|
912
910
|
return m
|
|
913
911
|
}
|
|
914
912
|
|
|
915
|
-
|
|
913
|
+
sanitizeOptions(options) {
|
|
916
914
|
// Ensure headers are always present
|
|
917
915
|
if (!options.headers) options.headers = {};
|
|
918
916
|
|
|
@@ -949,7 +947,7 @@ class Request extends Duplex {
|
|
|
949
947
|
|
|
950
948
|
// Store the redirected response
|
|
951
949
|
const {statusCode} = response;
|
|
952
|
-
if (m.
|
|
950
|
+
if (m.opt.trackRedirects) {
|
|
953
951
|
m._redirects.push({
|
|
954
952
|
url: m._currentUrl,
|
|
955
953
|
headers: response.headers,
|
|
@@ -969,7 +967,7 @@ class Request extends Duplex {
|
|
|
969
967
|
|
|
970
968
|
log$1('processResponse', {statusCode, headers: response.headers});
|
|
971
969
|
|
|
972
|
-
if (!location || m.
|
|
970
|
+
if (!location || m.opt.followRedirects === false || statusCode < 300 || statusCode >= 400) {
|
|
973
971
|
// 非重定向,返回给原始回调处理
|
|
974
972
|
response.responseUrl = m._currentUrl;
|
|
975
973
|
response.redirects = m._redirects;
|
|
@@ -1008,16 +1006,16 @@ class Request extends Duplex {
|
|
|
1008
1006
|
|
|
1009
1007
|
// RFC7231§6.4: A client SHOULD detect and intervene
|
|
1010
1008
|
// in cyclical redirections (i.e., "infinite" redirection loops).
|
|
1011
|
-
if (++m._redirectCount > m.
|
|
1009
|
+
if (++m._redirectCount > m.opt.maxRedirects) throw new TooManyRedirectsError()
|
|
1012
1010
|
|
|
1013
1011
|
// Store the request headers if applicable
|
|
1014
1012
|
let requestHeaders;
|
|
1015
|
-
const {beforeRedirect} = m.
|
|
1013
|
+
const {beforeRedirect} = m.opt;
|
|
1016
1014
|
if (beforeRedirect) {
|
|
1017
1015
|
requestHeaders = {
|
|
1018
1016
|
// The Host header was set by nativeProtocol.request
|
|
1019
1017
|
Host: response.req.getHeader('host'),
|
|
1020
|
-
...m.
|
|
1018
|
+
...m.opt.headers,
|
|
1021
1019
|
};
|
|
1022
1020
|
}
|
|
1023
1021
|
|
|
@@ -1025,23 +1023,23 @@ class Request extends Duplex {
|
|
|
1025
1023
|
// care for methods not known to be safe, […]
|
|
1026
1024
|
// RFC7231§6.4.2–3: For historical reasons, a user agent MAY change
|
|
1027
1025
|
// the request method from POST to GET for the subsequent request.
|
|
1028
|
-
const {method} = m.
|
|
1026
|
+
const {method} = m.opt;
|
|
1029
1027
|
if (
|
|
1030
|
-
((statusCode === 301 || statusCode === 302) && m.
|
|
1028
|
+
((statusCode === 301 || statusCode === 302) && m.opt.method === 'POST') ||
|
|
1031
1029
|
// RFC7231§6.4.4: The 303 (See Other) status code indicates that
|
|
1032
1030
|
// the server is redirecting the user agent to a different resource […]
|
|
1033
1031
|
// A user agent can perform a retrieval request targeting that URI
|
|
1034
1032
|
// (a GET or HEAD request if using HTTP) […]
|
|
1035
|
-
(statusCode === 303 && !/^(?:GET|HEAD)$/.test(m.
|
|
1033
|
+
(statusCode === 303 && !/^(?:GET|HEAD)$/.test(m.opt.method))
|
|
1036
1034
|
) {
|
|
1037
|
-
m.
|
|
1035
|
+
m.opt.method = 'GET';
|
|
1038
1036
|
// Drop a possible entity and headers related to it
|
|
1039
1037
|
m._requestBodyBuffers = [];
|
|
1040
|
-
removeMatchingHeaders(/^content-/i, m.
|
|
1038
|
+
removeMatchingHeaders(/^content-/i, m.opt.headers);
|
|
1041
1039
|
}
|
|
1042
1040
|
|
|
1043
1041
|
// Drop the Host header, as the redirect might lead to a different host
|
|
1044
|
-
const currentHostHeader = removeMatchingHeaders(/^host$/i, m.
|
|
1042
|
+
const currentHostHeader = removeMatchingHeaders(/^host$/i, m.opt.headers);
|
|
1045
1043
|
|
|
1046
1044
|
// If the redirect is relative, carry over the host of the last request
|
|
1047
1045
|
const currentUrlParts = utils.parseUrl(m._currentUrl);
|
|
@@ -1055,7 +1053,7 @@ class Request extends Duplex {
|
|
|
1055
1053
|
log$1({redirectUrl}, 'redirecting to');
|
|
1056
1054
|
m._isRedirect = true;
|
|
1057
1055
|
// 覆盖原 url 解析部分,包括 protocol、hostname、port等
|
|
1058
|
-
utils.spreadUrlObject(redirectUrl, m.
|
|
1056
|
+
utils.spreadUrlObject(redirectUrl, m.opt);
|
|
1059
1057
|
|
|
1060
1058
|
// Drop confidential headers when redirecting to a less secure protocol
|
|
1061
1059
|
// or to a different domain that is not a superdomain
|
|
@@ -1063,7 +1061,7 @@ class Request extends Duplex {
|
|
|
1063
1061
|
(redirectUrl.protocol !== currentUrlParts.protocol && redirectUrl.protocol !== 'https:') ||
|
|
1064
1062
|
(redirectUrl.host !== currentHost && !isSubdomain(redirectUrl.host, currentHost))
|
|
1065
1063
|
) {
|
|
1066
|
-
removeMatchingHeaders(/^(?:(?:proxy-)?authorization|cookie)$/i, this.
|
|
1064
|
+
removeMatchingHeaders(/^(?:(?:proxy-)?authorization|cookie)$/i, this.opt.headers);
|
|
1067
1065
|
}
|
|
1068
1066
|
|
|
1069
1067
|
// Evaluate the beforeRedirect callback
|
|
@@ -1078,8 +1076,8 @@ class Request extends Duplex {
|
|
|
1078
1076
|
headers: requestHeaders,
|
|
1079
1077
|
};
|
|
1080
1078
|
|
|
1081
|
-
beforeRedirect(m.
|
|
1082
|
-
m.
|
|
1079
|
+
beforeRedirect(m.opt, responseDetails, requestDetails);
|
|
1080
|
+
m.sanitizeOptions(m.opt);
|
|
1083
1081
|
}
|
|
1084
1082
|
|
|
1085
1083
|
// Perform the redirected request
|
|
@@ -1093,7 +1091,7 @@ class Request extends Duplex {
|
|
|
1093
1091
|
*/
|
|
1094
1092
|
processStream(res) {
|
|
1095
1093
|
const m = this;
|
|
1096
|
-
const {
|
|
1094
|
+
const {opt: opts} = m;
|
|
1097
1095
|
|
|
1098
1096
|
const streams = [res];
|
|
1099
1097
|
|
|
@@ -1258,7 +1256,7 @@ class Request extends Duplex {
|
|
|
1258
1256
|
*/
|
|
1259
1257
|
pipeDest(dest) {
|
|
1260
1258
|
const m = this;
|
|
1261
|
-
const {response
|
|
1259
|
+
const {response} = m;
|
|
1262
1260
|
|
|
1263
1261
|
// Called after the response is received
|
|
1264
1262
|
if (response?.headers && dest.headers && !dest.headersSent) {
|
|
@@ -1289,9 +1287,9 @@ class Request extends Duplex {
|
|
|
1289
1287
|
dest.statusCode = response.statusCode;
|
|
1290
1288
|
}
|
|
1291
1289
|
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1290
|
+
if (m.pipefilter) {
|
|
1291
|
+
m.pipefilter(response, dest);
|
|
1292
|
+
}
|
|
1295
1293
|
}
|
|
1296
1294
|
|
|
1297
1295
|
/**
|
|
@@ -1360,16 +1358,16 @@ function isSubdomain(subdomain, domain) {
|
|
|
1360
1358
|
*/
|
|
1361
1359
|
|
|
1362
1360
|
|
|
1363
|
-
const log = log$2({env: `wia:req:${name(
|
|
1361
|
+
const log = log$2({env: `wia:req:${name(import.meta.url)}`}) // __filename
|
|
1364
1362
|
|
|
1365
1363
|
// Preventive platform detection
|
|
1366
|
-
// istanbul ignore
|
|
1364
|
+
// istanbul ignore
|
|
1367
1365
|
;(function detectUnsupportedEnvironment() {
|
|
1368
1366
|
const looksLikeNode = typeof process !== 'undefined';
|
|
1369
1367
|
const looksLikeBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
1370
1368
|
const looksLikeV8 = utils.isFunction(Error.captureStackTrace);
|
|
1371
1369
|
if (!looksLikeNode && (looksLikeBrowser || !looksLikeV8)) {
|
|
1372
|
-
|
|
1370
|
+
console.warn('The follow-redirects package should be excluded from browser builds.');
|
|
1373
1371
|
}
|
|
1374
1372
|
})();
|
|
1375
1373
|
|
|
@@ -1387,6 +1385,7 @@ const log = log$2({env: `wia:req:${name(__filename)}`})
|
|
|
1387
1385
|
* @returns
|
|
1388
1386
|
*/
|
|
1389
1387
|
function init(uri, options, callback) {
|
|
1388
|
+
let R;
|
|
1390
1389
|
try {
|
|
1391
1390
|
// Parse parameters, ensuring that input is an object
|
|
1392
1391
|
if (utils.isURL(uri)) uri = utils.spreadUrlObject(uri);
|
|
@@ -1411,6 +1410,7 @@ function init(uri, options, callback) {
|
|
|
1411
1410
|
if (!utils.isString(options.host) && !utils.isString(options.hostname)) options.hostname = '::1';
|
|
1412
1411
|
|
|
1413
1412
|
R = {opts: options, cb: callback};
|
|
1413
|
+
// log({R}, 'init')
|
|
1414
1414
|
} catch (e) {
|
|
1415
1415
|
log.err(e, 'init');
|
|
1416
1416
|
}
|
|
@@ -1434,7 +1434,6 @@ function request(uri, options, callback) {
|
|
|
1434
1434
|
|
|
1435
1435
|
try {
|
|
1436
1436
|
const {opts, cb} = init(uri, options, callback);
|
|
1437
|
-
// log.debug('request', {options})
|
|
1438
1437
|
R = new Request(opts, cb);
|
|
1439
1438
|
} catch (e) {
|
|
1440
1439
|
log.err(e, 'request');
|
|
@@ -1452,6 +1451,8 @@ function request(uri, options, callback) {
|
|
|
1452
1451
|
*/
|
|
1453
1452
|
function fn(verb) {
|
|
1454
1453
|
const method = verb.toUpperCase();
|
|
1454
|
+
|
|
1455
|
+
// @ts-ignore
|
|
1455
1456
|
return (uri, options, callback) => {
|
|
1456
1457
|
const {opts, cb} = init(uri, options, callback);
|
|
1457
1458
|
opts.method = method;
|
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {default} from './lib'
|
|
1
|
+
export {default} from './lib/index.js'
|
package/lib/index.js
CHANGED
|
@@ -6,14 +6,15 @@
|
|
|
6
6
|
import Request from './request.js';
|
|
7
7
|
import utils from './utils.js';
|
|
8
8
|
const log = Log({
|
|
9
|
-
env: `wia:req:${name(
|
|
10
|
-
})
|
|
9
|
+
env: `wia:req:${name(import.meta.url)}`
|
|
10
|
+
}) // __filename
|
|
11
|
+
;
|
|
11
12
|
(function detectUnsupportedEnvironment() {
|
|
12
13
|
const looksLikeNode = typeof process !== 'undefined';
|
|
13
14
|
const looksLikeBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
14
15
|
const looksLikeV8 = utils.isFunction(Error.captureStackTrace);
|
|
15
16
|
if (!looksLikeNode && (looksLikeBrowser || !looksLikeV8)) {
|
|
16
|
-
|
|
17
|
+
console.warn('The follow-redirects package should be excluded from browser builds.');
|
|
17
18
|
}
|
|
18
19
|
})();
|
|
19
20
|
/**
|
|
@@ -27,6 +28,7 @@ const log = Log({
|
|
|
27
28
|
* @param {*} callback
|
|
28
29
|
* @returns
|
|
29
30
|
*/ function init(uri, options, callback) {
|
|
31
|
+
let R;
|
|
30
32
|
try {
|
|
31
33
|
// Parse parameters, ensuring that input is an object
|
|
32
34
|
if (utils.isURL(uri)) uri = utils.spreadUrlObject(uri);
|
|
@@ -50,6 +52,7 @@ const log = Log({
|
|
|
50
52
|
opts: options,
|
|
51
53
|
cb: callback
|
|
52
54
|
};
|
|
55
|
+
// log({R}, 'init')
|
|
53
56
|
} catch (e) {
|
|
54
57
|
log.err(e, 'init');
|
|
55
58
|
}
|
|
@@ -66,15 +69,14 @@ const log = Log({
|
|
|
66
69
|
* @param {*} callback/null
|
|
67
70
|
* @returns
|
|
68
71
|
*/ function request(uri, options, callback) {
|
|
69
|
-
let
|
|
72
|
+
let R = null;
|
|
70
73
|
try {
|
|
71
74
|
const { opts, cb } = init(uri, options, callback);
|
|
72
|
-
|
|
73
|
-
R1 = new Request(opts, cb);
|
|
75
|
+
R = new Request(opts, cb);
|
|
74
76
|
} catch (e) {
|
|
75
77
|
log.err(e, 'request');
|
|
76
78
|
}
|
|
77
|
-
return
|
|
79
|
+
return R;
|
|
78
80
|
}
|
|
79
81
|
/**
|
|
80
82
|
* 执行简单的非stream数据请求
|
|
@@ -84,6 +86,7 @@ const log = Log({
|
|
|
84
86
|
* @returns {Request} Duplex 流
|
|
85
87
|
*/ function fn(verb) {
|
|
86
88
|
const method = verb.toUpperCase();
|
|
89
|
+
// @ts-ignore
|
|
87
90
|
return (uri, options, callback)=>{
|
|
88
91
|
const { opts, cb } = init(uri, options, callback);
|
|
89
92
|
opts.method = method;
|
package/lib/request.js
CHANGED
|
@@ -14,8 +14,9 @@ import ZlibTransform from './ZlibTransform.js';
|
|
|
14
14
|
import utils from './utils.js';
|
|
15
15
|
import Caseless from './caseless.js';
|
|
16
16
|
const log = Log({
|
|
17
|
-
env: `wia:req:${name(
|
|
18
|
-
})
|
|
17
|
+
env: `wia:req:${name(import.meta.url)}`
|
|
18
|
+
}) // __filename
|
|
19
|
+
;
|
|
19
20
|
const httpModules = {
|
|
20
21
|
'http:': http,
|
|
21
22
|
'https:': https
|
|
@@ -51,7 +52,7 @@ const writeEventEmit = Object.create(null);
|
|
|
51
52
|
for (const ev of writeEvents)writeEventEmit[ev] = function(...args) {
|
|
52
53
|
const m = this // 事件回调,this === clientRequest 实例
|
|
53
54
|
;
|
|
54
|
-
log
|
|
55
|
+
log('req event', {
|
|
55
56
|
ev
|
|
56
57
|
});
|
|
57
58
|
m.redirectReq.emit(ev, ...args) // req 事情映射到 redirectReq 上触发
|
|
@@ -71,7 +72,7 @@ const readEventEmit = Object.create(null);
|
|
|
71
72
|
for (const ev of readEvents)readEventEmit[ev] = function(...args) {
|
|
72
73
|
const m = this // 事件回调,this === clientRequest 实例
|
|
73
74
|
;
|
|
74
|
-
log
|
|
75
|
+
log('res event', {
|
|
75
76
|
ev
|
|
76
77
|
});
|
|
77
78
|
m.redirectReq.emit(ev, ...args) // 向上触发事件
|
|
@@ -88,18 +89,19 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
88
89
|
*/ export default class Request extends Duplex {
|
|
89
90
|
/**
|
|
90
91
|
* responseCallback 原消息处理回调
|
|
91
|
-
* @param {*}
|
|
92
|
+
* @param {*} opts
|
|
92
93
|
* @param {*} resCallback
|
|
93
|
-
*/ constructor(
|
|
94
|
+
*/ constructor(opts, resCallback){
|
|
94
95
|
super(), this._timeout = 0, /** @type {*} */ this.socket = null, /** @type {http.ClientRequest} */ this._currentRequest = null, /** @type {stream.Readable} */ this.response = null, /** @type {stream.Readable} */ this.responseStream = null, this.timing = false, this.responseStarted = false, this.responseStartTime = 0, this._destdata = false, this._paused = false, this._respended = false, /** @type {stream.Readable} */ this.pipesrc = null // 被 pipe 时的 src stream
|
|
95
96
|
, /** @type {stream.Writable[]} */ this.pipedests = [] // pipe dest
|
|
96
97
|
;
|
|
97
98
|
const m = this;
|
|
98
99
|
// log({options}, 'constructor');
|
|
99
100
|
// Initialize the request
|
|
100
|
-
m.
|
|
101
|
-
m.
|
|
102
|
-
m.headers =
|
|
101
|
+
m.sanitizeOptions(opts);
|
|
102
|
+
m.opt = opts;
|
|
103
|
+
m.headers = opts.headers;
|
|
104
|
+
// log({opts}, 'constructor')
|
|
103
105
|
m._ended = false;
|
|
104
106
|
m._ending = false;
|
|
105
107
|
m._redirectCount = 0;
|
|
@@ -110,9 +112,9 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
110
112
|
m.resCallback = resCallback;
|
|
111
113
|
// React to responses of native requests
|
|
112
114
|
// 接管 response 事件,非重定向,触发 response 事件
|
|
113
|
-
m._onResponse = (
|
|
115
|
+
m._onResponse = (res)=>{
|
|
114
116
|
try {
|
|
115
|
-
m.processResponse(
|
|
117
|
+
m.processResponse(res);
|
|
116
118
|
} catch (cause) {
|
|
117
119
|
m.emit('error', cause instanceof RedirectionError ? cause : new RedirectionError({
|
|
118
120
|
cause: cause
|
|
@@ -150,30 +152,30 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
150
152
|
});
|
|
151
153
|
}
|
|
152
154
|
// 流模式
|
|
153
|
-
if (
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
m.pipesrc = src;
|
|
160
|
-
if (utils.isReadStream(src)) {
|
|
161
|
-
if (!m.hasHeader('content-type')) {
|
|
162
|
-
m.setHeader('content-type', mime.lookup(src.path));
|
|
155
|
+
if (opts.stream) {
|
|
156
|
+
// 被 pipe 作为目标时触发,拷贝 src headers
|
|
157
|
+
m.on('pipe', /** @type {stream.Readable} */ (src)=>{
|
|
158
|
+
// m.ntick &&
|
|
159
|
+
if (m._currentRequest) {
|
|
160
|
+
m.emit('error', new Error('You cannot pipe to this stream after the outbound request has started.'));
|
|
163
161
|
}
|
|
164
|
-
|
|
165
|
-
if (src
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
162
|
+
m.pipesrc = src;
|
|
163
|
+
if (utils.isReadStream(src)) {
|
|
164
|
+
if (!m.hasHeader('content-type')) {
|
|
165
|
+
m.setHeader('content-type', mime.lookup(src.path));
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
if (src.headers) {
|
|
169
|
+
for (const h of src.headers){
|
|
170
|
+
if (!m.hasHeader(h)) {
|
|
171
|
+
m.setHeader(h, src.headers[h]);
|
|
172
|
+
}
|
|
169
173
|
}
|
|
170
174
|
}
|
|
175
|
+
if (src.opt.method && !m.opt.method) m.opt.method = src.opt.method;
|
|
171
176
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
// }
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
+
});
|
|
178
|
+
}
|
|
177
179
|
// Perform the first request
|
|
178
180
|
// m.request(); // 写入数据时执行,否则 pipe时无法写入header
|
|
179
181
|
}
|
|
@@ -182,9 +184,10 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
182
184
|
* @returns http(s) 实例
|
|
183
185
|
*/ request() {
|
|
184
186
|
let R = null;
|
|
187
|
+
const m = this;
|
|
188
|
+
const { opt } = m;
|
|
185
189
|
try {
|
|
186
|
-
|
|
187
|
-
// read stream
|
|
190
|
+
// reset read stream
|
|
188
191
|
m.response = null;
|
|
189
192
|
m.responseStarted = false;
|
|
190
193
|
m.responseStream = null;
|
|
@@ -195,29 +198,28 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
195
198
|
m._respended = false;
|
|
196
199
|
// m.httpModule = httpModules[protocol];
|
|
197
200
|
// Load the native protocol
|
|
198
|
-
let { protocol } =
|
|
199
|
-
const { agents } =
|
|
201
|
+
let { protocol } = opt;
|
|
202
|
+
const { agents } = opt;
|
|
200
203
|
// 代理以目的网址协议为准
|
|
201
204
|
// If specified, use the agent corresponding to the protocol
|
|
202
205
|
// (HTTP and HTTPS use different types of agents)
|
|
203
206
|
if (agents) {
|
|
204
207
|
const scheme = protocol.slice(0, -1);
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
208
|
+
opt.agent = agents[scheme];
|
|
209
|
+
// http 非隧道代理模式,模块以代理主机为准,其他以目的网址为准
|
|
210
|
+
// 代理内部会根据代理协议选择 http(s) 发起请求创建连接
|
|
211
|
+
if (protocol === 'http:' && agents.http) {
|
|
212
|
+
protocol = agents.http.proxy && !agents.http.tunnel ? agents.http.proxy.protocol : protocol;
|
|
213
|
+
}
|
|
211
214
|
}
|
|
212
215
|
const httpModule = httpModules[protocol];
|
|
213
216
|
if (!httpModule) throw TypeError(`Unsupported protocol: ${protocol}`);
|
|
214
|
-
log
|
|
215
|
-
|
|
217
|
+
log({
|
|
218
|
+
opt,
|
|
216
219
|
protocol
|
|
217
|
-
});
|
|
218
|
-
debugger;
|
|
220
|
+
}, 'request');
|
|
219
221
|
// Create the native request and set up its event handlers
|
|
220
|
-
const req = httpModule.request(
|
|
222
|
+
const req = httpModule.request(opt, m._onResponse);
|
|
221
223
|
m._currentRequest = req;
|
|
222
224
|
req.redirectReq = m;
|
|
223
225
|
// 接收req事件,转发 到 redirectReq 发射
|
|
@@ -226,7 +228,7 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
226
228
|
// a client MUST send only the absolute path […] as the request-target.
|
|
227
229
|
// When making a request to a proxy, […]
|
|
228
230
|
// a client MUST send the target URI in absolute-form […].
|
|
229
|
-
m._currentUrl = /^\//.test(
|
|
231
|
+
m._currentUrl = /^\//.test(opt.path) ? url.format(opt) : opt.path;
|
|
230
232
|
// End a redirected request
|
|
231
233
|
// (The first request must be ended explicitly with RedirectableRequest#end)
|
|
232
234
|
if (m._isRedirect) {
|
|
@@ -302,7 +304,7 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
302
304
|
return;
|
|
303
305
|
}
|
|
304
306
|
// Only write when we don't exceed the maximum body length
|
|
305
|
-
if (m._requestBodyLength + chunk.length <= m.
|
|
307
|
+
if (m._requestBodyLength + chunk.length <= m.opt.maxBodyLength) {
|
|
306
308
|
m._requestBodyLength += chunk.length;
|
|
307
309
|
m._requestBodyBuffers.push({
|
|
308
310
|
data: chunk,
|
|
@@ -351,27 +353,27 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
351
353
|
* @param {string} name
|
|
352
354
|
* @returns
|
|
353
355
|
*/ hasHeader(name) {
|
|
354
|
-
return this.
|
|
356
|
+
return this.opt.headers.includes(name);
|
|
355
357
|
}
|
|
356
358
|
/**
|
|
357
359
|
*
|
|
358
360
|
* @param {string} name
|
|
359
361
|
* @returns {string}
|
|
360
362
|
*/ getHeader(name) {
|
|
361
|
-
return this.
|
|
363
|
+
return this.opt.headers[name];
|
|
362
364
|
}
|
|
363
365
|
/**
|
|
364
366
|
* Sets a header value on the current native request
|
|
365
367
|
* @param {string} name
|
|
366
368
|
*/ setHeader(name, value) {
|
|
367
|
-
this.
|
|
369
|
+
this.opt.headers[name] = value;
|
|
368
370
|
this._currentRequest?.setHeader(name, value);
|
|
369
371
|
}
|
|
370
372
|
/**
|
|
371
373
|
* Clears a header value on the current native request
|
|
372
374
|
* @param {string} name
|
|
373
375
|
*/ removeHeader(name) {
|
|
374
|
-
delete this.
|
|
376
|
+
delete this.opt.headers[name];
|
|
375
377
|
this._currentRequest?.removeHeader(name);
|
|
376
378
|
}
|
|
377
379
|
/**
|
|
@@ -443,7 +445,7 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
443
445
|
m.on('close', clearTimer);
|
|
444
446
|
return m;
|
|
445
447
|
}
|
|
446
|
-
|
|
448
|
+
sanitizeOptions(options) {
|
|
447
449
|
// Ensure headers are always present
|
|
448
450
|
if (!options.headers) options.headers = {};
|
|
449
451
|
// Since http.request treats host as an alias of hostname,
|
|
@@ -475,7 +477,7 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
475
477
|
const m = this;
|
|
476
478
|
// Store the redirected response
|
|
477
479
|
const { statusCode } = response;
|
|
478
|
-
if (m.
|
|
480
|
+
if (m.opt.trackRedirects) {
|
|
479
481
|
m._redirects.push({
|
|
480
482
|
url: m._currentUrl,
|
|
481
483
|
headers: response.headers,
|
|
@@ -494,7 +496,7 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
494
496
|
statusCode,
|
|
495
497
|
headers: response.headers
|
|
496
498
|
});
|
|
497
|
-
if (!location || m.
|
|
499
|
+
if (!location || m.opt.followRedirects === false || statusCode < 300 || statusCode >= 400) {
|
|
498
500
|
// 非重定向,返回给原始回调处理
|
|
499
501
|
response.responseUrl = m._currentUrl;
|
|
500
502
|
response.redirects = m._redirects;
|
|
@@ -526,34 +528,34 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
526
528
|
response.destroy();
|
|
527
529
|
// RFC7231§6.4: A client SHOULD detect and intervene
|
|
528
530
|
// in cyclical redirections (i.e., "infinite" redirection loops).
|
|
529
|
-
if (++m._redirectCount > m.
|
|
531
|
+
if (++m._redirectCount > m.opt.maxRedirects) throw new TooManyRedirectsError();
|
|
530
532
|
// Store the request headers if applicable
|
|
531
533
|
let requestHeaders;
|
|
532
|
-
const { beforeRedirect } = m.
|
|
534
|
+
const { beforeRedirect } = m.opt;
|
|
533
535
|
if (beforeRedirect) {
|
|
534
536
|
requestHeaders = {
|
|
535
537
|
// The Host header was set by nativeProtocol.request
|
|
536
538
|
Host: response.req.getHeader('host'),
|
|
537
|
-
...m.
|
|
539
|
+
...m.opt.headers
|
|
538
540
|
};
|
|
539
541
|
}
|
|
540
542
|
// RFC7231§6.4: Automatic redirection needs to done with
|
|
541
543
|
// care for methods not known to be safe, […]
|
|
542
544
|
// RFC7231§6.4.2–3: For historical reasons, a user agent MAY change
|
|
543
545
|
// the request method from POST to GET for the subsequent request.
|
|
544
|
-
const { method } = m.
|
|
545
|
-
if ((statusCode === 301 || statusCode === 302) && m.
|
|
546
|
+
const { method } = m.opt;
|
|
547
|
+
if ((statusCode === 301 || statusCode === 302) && m.opt.method === 'POST' || // RFC7231§6.4.4: The 303 (See Other) status code indicates that
|
|
546
548
|
// the server is redirecting the user agent to a different resource […]
|
|
547
549
|
// A user agent can perform a retrieval request targeting that URI
|
|
548
550
|
// (a GET or HEAD request if using HTTP) […]
|
|
549
|
-
statusCode === 303 && !/^(?:GET|HEAD)$/.test(m.
|
|
550
|
-
m.
|
|
551
|
+
statusCode === 303 && !/^(?:GET|HEAD)$/.test(m.opt.method)) {
|
|
552
|
+
m.opt.method = 'GET';
|
|
551
553
|
// Drop a possible entity and headers related to it
|
|
552
554
|
m._requestBodyBuffers = [];
|
|
553
|
-
removeMatchingHeaders(/^content-/i, m.
|
|
555
|
+
removeMatchingHeaders(/^content-/i, m.opt.headers);
|
|
554
556
|
}
|
|
555
557
|
// Drop the Host header, as the redirect might lead to a different host
|
|
556
|
-
const currentHostHeader = removeMatchingHeaders(/^host$/i, m.
|
|
558
|
+
const currentHostHeader = removeMatchingHeaders(/^host$/i, m.opt.headers);
|
|
557
559
|
// If the redirect is relative, carry over the host of the last request
|
|
558
560
|
const currentUrlParts = utils.parseUrl(m._currentUrl);
|
|
559
561
|
const currentHost = currentHostHeader || currentUrlParts.host;
|
|
@@ -567,11 +569,11 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
567
569
|
}, 'redirecting to');
|
|
568
570
|
m._isRedirect = true;
|
|
569
571
|
// 覆盖原 url 解析部分,包括 protocol、hostname、port等
|
|
570
|
-
utils.spreadUrlObject(redirectUrl, m.
|
|
572
|
+
utils.spreadUrlObject(redirectUrl, m.opt);
|
|
571
573
|
// Drop confidential headers when redirecting to a less secure protocol
|
|
572
574
|
// or to a different domain that is not a superdomain
|
|
573
575
|
if (redirectUrl.protocol !== currentUrlParts.protocol && redirectUrl.protocol !== 'https:' || redirectUrl.host !== currentHost && !isSubdomain(redirectUrl.host, currentHost)) {
|
|
574
|
-
removeMatchingHeaders(/^(?:(?:proxy-)?authorization|cookie)$/i, this.
|
|
576
|
+
removeMatchingHeaders(/^(?:(?:proxy-)?authorization|cookie)$/i, this.opt.headers);
|
|
575
577
|
}
|
|
576
578
|
// Evaluate the beforeRedirect callback
|
|
577
579
|
if (utils.isFunction(beforeRedirect)) {
|
|
@@ -584,8 +586,8 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
584
586
|
method,
|
|
585
587
|
headers: requestHeaders
|
|
586
588
|
};
|
|
587
|
-
beforeRedirect(m.
|
|
588
|
-
m.
|
|
589
|
+
beforeRedirect(m.opt, responseDetails, requestDetails);
|
|
590
|
+
m.sanitizeOptions(m.opt);
|
|
589
591
|
}
|
|
590
592
|
// Perform the redirected request
|
|
591
593
|
m.request() // 重新执行请求
|
|
@@ -597,7 +599,7 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
597
599
|
* @param {*} res
|
|
598
600
|
*/ processStream(res) {
|
|
599
601
|
const m = this;
|
|
600
|
-
const {
|
|
602
|
+
const { opt: opts } = m;
|
|
601
603
|
const streams = [
|
|
602
604
|
res
|
|
603
605
|
];
|
|
@@ -746,7 +748,7 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
746
748
|
* @param {*} dest
|
|
747
749
|
*/ pipeDest(dest) {
|
|
748
750
|
const m = this;
|
|
749
|
-
const { response
|
|
751
|
+
const { response } = m;
|
|
750
752
|
// Called after the response is received
|
|
751
753
|
if (response?.headers && dest.headers && !dest.headersSent) {
|
|
752
754
|
const caseless = new Caseless(response.headers);
|
|
@@ -773,9 +775,9 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
773
775
|
}
|
|
774
776
|
dest.statusCode = response.statusCode;
|
|
775
777
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
778
|
+
if (m.pipefilter) {
|
|
779
|
+
m.pipefilter(response, dest);
|
|
780
|
+
}
|
|
779
781
|
}
|
|
780
782
|
/**
|
|
781
783
|
* 暂停read流
|