@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/lib/ZlibTransform.js
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import stream from 'node:stream';
|
|
2
2
|
let ZlibTransform = class ZlibTransform extends stream.Transform {
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {*} chunk
|
|
6
|
+
* @param {*} encoding
|
|
7
|
+
* @param {*} callback
|
|
8
|
+
*/ __transform(chunk, encoding, callback) {
|
|
4
9
|
this.push(chunk);
|
|
5
10
|
callback();
|
|
6
11
|
}
|
|
7
|
-
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @param {*} chunk
|
|
15
|
+
* @param {*} encoding
|
|
16
|
+
* @param {*} callback
|
|
17
|
+
*/ _transform(chunk, encoding, callback) {
|
|
8
18
|
if (chunk.length !== 0) {
|
|
9
19
|
this._transform = this.__transform;
|
|
10
20
|
// Add Default Compression headers if no zlib headers are present
|
package/lib/caseless.js
CHANGED
|
@@ -61,7 +61,11 @@ export default class Caseless {
|
|
|
61
61
|
this.dict[name] = this.dict[has];
|
|
62
62
|
delete this.dict[has];
|
|
63
63
|
}
|
|
64
|
-
|
|
64
|
+
/**
|
|
65
|
+
*
|
|
66
|
+
* @param {string} name
|
|
67
|
+
* @returns
|
|
68
|
+
*/ del(name) {
|
|
65
69
|
name = String(name).toLowerCase();
|
|
66
70
|
let deleted = false;
|
|
67
71
|
let changed = 0;
|
|
@@ -82,17 +86,35 @@ export default class Caseless {
|
|
|
82
86
|
* @returns
|
|
83
87
|
*/ export function httpify(resp, headers) {
|
|
84
88
|
const c = new Caseless(headers);
|
|
85
|
-
|
|
89
|
+
/**
|
|
90
|
+
*
|
|
91
|
+
* @param {string} key
|
|
92
|
+
* @param {*} value
|
|
93
|
+
* @param {*} clobber
|
|
94
|
+
* @returns
|
|
95
|
+
*/ resp.setHeader = (key, value, clobber)=>{
|
|
86
96
|
if (typeof value === 'undefined') return;
|
|
87
97
|
return c.set(key, value, clobber);
|
|
88
98
|
};
|
|
89
|
-
|
|
99
|
+
/**
|
|
100
|
+
*
|
|
101
|
+
* @param {string} key
|
|
102
|
+
* @returns {boolean|string}
|
|
103
|
+
*/ resp.hasHeader = (key)=>{
|
|
90
104
|
return c.has(key);
|
|
91
105
|
};
|
|
92
|
-
|
|
106
|
+
/**
|
|
107
|
+
*
|
|
108
|
+
* @param {string} key
|
|
109
|
+
* @returns {*}
|
|
110
|
+
*/ resp.getHeader = (key)=>{
|
|
93
111
|
return c.get(key);
|
|
94
112
|
};
|
|
95
|
-
|
|
113
|
+
/**
|
|
114
|
+
*
|
|
115
|
+
* @param {string} key
|
|
116
|
+
* @returns {boolean}
|
|
117
|
+
*/ resp.removeHeader = (key)=>{
|
|
96
118
|
return c.del(key);
|
|
97
119
|
};
|
|
98
120
|
resp.headers = c.dict;
|
package/lib/request.js
CHANGED
|
@@ -17,7 +17,29 @@ const log = Log({
|
|
|
17
17
|
env: `wia:req:${name(import.meta.url)}`
|
|
18
18
|
}) // __filename
|
|
19
19
|
;
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* @typedef {object} Opts
|
|
22
|
+
* @prop {Object.<string,string>} headers
|
|
23
|
+
* @prop {string} host
|
|
24
|
+
* @prop {string} method
|
|
25
|
+
* @prop {string} family
|
|
26
|
+
* @prop {string} path
|
|
27
|
+
* @prop {'http:' | 'https:'} protocol
|
|
28
|
+
* @prop {*} agent
|
|
29
|
+
* @prop {*} agents
|
|
30
|
+
* @prop {boolean} [stream]
|
|
31
|
+
* @prop {boolean} [decompress=true]
|
|
32
|
+
* @prop {*} [transformStream]
|
|
33
|
+
* @prop {*} [beforeRedirect]
|
|
34
|
+
* @prop {boolean} [followRedirects]
|
|
35
|
+
* @prop {number} [maxRedirects=21]
|
|
36
|
+
* @prop {number} [maxBodyLength = 0]
|
|
37
|
+
* @prop {*} [trackRedirects]
|
|
38
|
+
*/ /** @typedef {object} ResponseExt
|
|
39
|
+
* @prop {*[]} [redirects]
|
|
40
|
+
* @prop {string} [responseUrl]
|
|
41
|
+
* @prop {number} [responseStartTime]
|
|
42
|
+
*/ /** @typedef { http.IncomingMessage & ResponseExt} Response*/ const httpModules = {
|
|
21
43
|
'http:': http,
|
|
22
44
|
'https:': https
|
|
23
45
|
};
|
|
@@ -78,7 +100,7 @@ const writeEvents = [
|
|
|
78
100
|
'upgrade'
|
|
79
101
|
];
|
|
80
102
|
const writeEventEmit = Object.create(null);
|
|
81
|
-
for (const ev of writeEvents)writeEventEmit[ev] = function(...args) {
|
|
103
|
+
for (const ev of writeEvents)writeEventEmit[ev] = /** @param {...any} args */ function(...args) {
|
|
82
104
|
const m = this // 事件回调,this === clientRequest 实例
|
|
83
105
|
;
|
|
84
106
|
log('req event', {
|
|
@@ -98,7 +120,7 @@ const readEvents = [
|
|
|
98
120
|
'resume'
|
|
99
121
|
];
|
|
100
122
|
const readEventEmit = Object.create(null);
|
|
101
|
-
for (const ev of readEvents)readEventEmit[ev] = function(...args) {
|
|
123
|
+
for (const ev of readEvents)readEventEmit[ev] = /** @param {...any} args */ function(...args) {
|
|
102
124
|
const m = this // 事件回调,this === clientRequest 实例
|
|
103
125
|
;
|
|
104
126
|
log('res event', {
|
|
@@ -118,10 +140,10 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
118
140
|
*/ export default class Request extends Duplex {
|
|
119
141
|
/**
|
|
120
142
|
* responseCallback 原消息处理回调
|
|
121
|
-
* @param {
|
|
143
|
+
* @param {Opts} opts
|
|
122
144
|
* @param {*} resCallback
|
|
123
145
|
*/ constructor(opts, resCallback){
|
|
124
|
-
super(), this._timeout =
|
|
146
|
+
super(), /** @type {NodeJS.Timeout} */ this._timeout = null, /** @type {*} */ this.socket = null, /** @type {http.ClientRequest} */ this._currentRequest = null, /** @type {Response} */ 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
|
|
125
147
|
, /** @type {stream.Writable[]} */ this.pipedests = [] // pipe dest
|
|
126
148
|
, /** @type {*} */ this.startTimer = null;
|
|
127
149
|
const m = this;
|
|
@@ -139,9 +161,11 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
139
161
|
/** @type {any[]} */ m._requestBodyBuffers = [];
|
|
140
162
|
// save the callback if passed
|
|
141
163
|
m.resCallback = resCallback;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
164
|
+
/**
|
|
165
|
+
* React to responses of native requests
|
|
166
|
+
* 接管 response 事件,非重定向,触发 response 事件
|
|
167
|
+
* @param {Response} res
|
|
168
|
+
*/ m._onResponse = (res)=>{
|
|
145
169
|
try {
|
|
146
170
|
m.processResponse(res);
|
|
147
171
|
} catch (cause) {
|
|
@@ -179,24 +203,25 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
179
203
|
if (opts.stream) {
|
|
180
204
|
// 流模式
|
|
181
205
|
// 被 pipe 作为目标时触发,拷贝 src headers
|
|
182
|
-
m.on('pipe', /** @
|
|
206
|
+
m.on('pipe', /** @param {stream.Readable & {headers?: Object.<string, string>}} src */ (src)=>{
|
|
183
207
|
// m.ntick &&
|
|
184
208
|
if (m._currentRequest) {
|
|
185
209
|
m.emit('error', new Error('You cannot pipe to this stream after the outbound request has started.'));
|
|
186
210
|
}
|
|
187
211
|
m.pipesrc = src;
|
|
188
212
|
if (utils.isReadStream(src)) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
213
|
+
// @ts-ignore
|
|
214
|
+
if (!m.hasHeader('content-type')) m.setHeader('content-type', mime.lookup(src.path));
|
|
192
215
|
} else {
|
|
216
|
+
// 拷贝请求头
|
|
193
217
|
if (src.headers) {
|
|
194
|
-
for (const
|
|
195
|
-
if (!m.hasHeader(
|
|
196
|
-
m.setHeader(
|
|
218
|
+
for (const k of Object.keys(src.headers)){
|
|
219
|
+
if (!m.hasHeader(k)) {
|
|
220
|
+
m.setHeader(k, src.headers[k]);
|
|
197
221
|
}
|
|
198
222
|
}
|
|
199
223
|
}
|
|
224
|
+
// @ts-ignore
|
|
200
225
|
if (src.opt.method && !m.opt.method) m.opt.method = src.opt.method;
|
|
201
226
|
}
|
|
202
227
|
});
|
|
@@ -244,8 +269,10 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
244
269
|
protocol
|
|
245
270
|
}, 'request');
|
|
246
271
|
// Create the native request and set up its event handlers
|
|
272
|
+
// @ts-ignore
|
|
247
273
|
const req = httpModule.request(opt, m._onResponse);
|
|
248
274
|
m._currentRequest = req;
|
|
275
|
+
// @ts-ignore
|
|
249
276
|
req.redirectReq = m;
|
|
250
277
|
// 启动 startTimer
|
|
251
278
|
if (m.startTimer) m._currentRequest.once('socket', m.startTimer);
|
|
@@ -262,7 +289,10 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
262
289
|
// Write the request entity and end
|
|
263
290
|
let i = 0;
|
|
264
291
|
const buffers = m._requestBodyBuffers;
|
|
265
|
-
|
|
292
|
+
/**
|
|
293
|
+
*
|
|
294
|
+
* @param {*} error
|
|
295
|
+
*/ function writeNext(error) {
|
|
266
296
|
// Only write if this request has not been redirected yet
|
|
267
297
|
/* istanbul ignore else */ if (req === m._currentRequest) {
|
|
268
298
|
// Report any write errors
|
|
@@ -272,7 +302,8 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
272
302
|
/* istanbul ignore else */ if (!req.finished) req.write(buf.data, buf.encoding, writeNext);
|
|
273
303
|
} else if (m._ended) req.end();
|
|
274
304
|
}
|
|
275
|
-
}
|
|
305
|
+
}
|
|
306
|
+
writeNext();
|
|
276
307
|
}
|
|
277
308
|
R = req;
|
|
278
309
|
} catch (e) {
|
|
@@ -303,15 +334,16 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
303
334
|
/**
|
|
304
335
|
* Writes buffered data to the current native request
|
|
305
336
|
* 如 request 不存在,则创建连接,pipe 时可写入 header
|
|
306
|
-
* @
|
|
307
|
-
* @param {
|
|
308
|
-
* @param {(error: Error) => void} [
|
|
309
|
-
* @
|
|
310
|
-
|
|
337
|
+
* @override - 重写父类方法
|
|
338
|
+
* @param {*} chunk - The data chunk to write.
|
|
339
|
+
* @param {BufferEncoding | ((error: Error | null) => void)} [encodingOrCallback] - Encoding for string data, or the callback if no encoding is provided.
|
|
340
|
+
* @param {(error: Error | null) => void} [cb] - Callback to signal the end of the write operation.
|
|
341
|
+
* @returns {boolean} True if the write was successful, false otherwise.
|
|
342
|
+
*/ write(chunk, encodingOrCallback, cb) {
|
|
311
343
|
const m = this;
|
|
312
344
|
log({
|
|
313
345
|
data: chunk,
|
|
314
|
-
encoding,
|
|
346
|
+
encoding: encodingOrCallback,
|
|
315
347
|
callback: cb
|
|
316
348
|
}, 'write');
|
|
317
349
|
// Writing is not allowed if end has been called
|
|
@@ -320,14 +352,15 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
320
352
|
if (!m._currentRequest) m.request();
|
|
321
353
|
// Validate input and shift parameters if necessary
|
|
322
354
|
if (!utils.isString(chunk) && !utils.isBuffer(chunk)) throw new TypeError('data should be a string, Buffer or Uint8Array');
|
|
323
|
-
if (utils.isFunction(
|
|
324
|
-
|
|
325
|
-
|
|
355
|
+
if (utils.isFunction(encodingOrCallback)) {
|
|
356
|
+
// @ts-ignore
|
|
357
|
+
cb = encodingOrCallback;
|
|
358
|
+
encodingOrCallback = null;
|
|
326
359
|
}
|
|
327
360
|
// Ignore empty buffers, since writing them doesn't invoke the callback
|
|
328
361
|
// https://github.com/nodejs/node/issues/22066
|
|
329
362
|
if (chunk.length === 0) {
|
|
330
|
-
if (cb) cb();
|
|
363
|
+
if (cb) cb(null);
|
|
331
364
|
return;
|
|
332
365
|
}
|
|
333
366
|
// Only write when we don't exceed the maximum body length
|
|
@@ -335,9 +368,10 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
335
368
|
m._requestBodyLength += chunk.length;
|
|
336
369
|
m._requestBodyBuffers.push({
|
|
337
370
|
data: chunk,
|
|
338
|
-
encoding
|
|
371
|
+
encoding: encodingOrCallback
|
|
339
372
|
});
|
|
340
|
-
|
|
373
|
+
// @ts-ignore
|
|
374
|
+
m._currentRequest.write(chunk, encodingOrCallback, cb);
|
|
341
375
|
} else {
|
|
342
376
|
m.emit('error', new MaxBodyLengthExceededError());
|
|
343
377
|
m.abort();
|
|
@@ -345,42 +379,46 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
345
379
|
}
|
|
346
380
|
/**
|
|
347
381
|
* Ends the current native request
|
|
348
|
-
* @
|
|
349
|
-
* @param {*}
|
|
350
|
-
* @param {
|
|
351
|
-
|
|
382
|
+
* @override - 重写父类方法
|
|
383
|
+
* @param {*} [chunk] - Optional data to write before ending the stream.
|
|
384
|
+
* @param {BufferEncoding | (() => void)} [encoding] - Encoding for string data, or the callback if no encoding is provided.
|
|
385
|
+
* @param {() => void} [cb] - Optional callback to signal completion.
|
|
386
|
+
* @returns {this} The current stream instance, to allow chaining.
|
|
387
|
+
*/ end(chunk, encoding, cb) {
|
|
352
388
|
const m = this;
|
|
353
389
|
// Shift parameters if necessary
|
|
354
|
-
if (utils.isFunction(
|
|
355
|
-
|
|
356
|
-
|
|
390
|
+
if (utils.isFunction(chunk)) {
|
|
391
|
+
cb = chunk;
|
|
392
|
+
chunk = null;
|
|
357
393
|
encoding = null;
|
|
358
394
|
} else if (utils.isFunction(encoding)) {
|
|
359
|
-
|
|
395
|
+
// @ts-ignore
|
|
396
|
+
cb = encoding;
|
|
360
397
|
encoding = null;
|
|
361
398
|
}
|
|
362
399
|
// ! 数据写入时连接,pipe 时可设置 header
|
|
363
400
|
if (!m._currentRequest) m.request();
|
|
364
401
|
// Write data if needed and end
|
|
365
|
-
if (!
|
|
402
|
+
if (!chunk) {
|
|
366
403
|
m._ended = true;
|
|
367
404
|
m._ending = true;
|
|
368
|
-
m._currentRequest.end(null, null,
|
|
405
|
+
m._currentRequest.end(null, null, cb);
|
|
369
406
|
} else {
|
|
370
407
|
const currentRequest = m._currentRequest;
|
|
371
|
-
m.write(
|
|
408
|
+
m.write(chunk, encoding, ()=>{
|
|
372
409
|
m._ended = true;
|
|
373
|
-
currentRequest.end(null, null,
|
|
410
|
+
currentRequest.end(null, null, cb);
|
|
374
411
|
});
|
|
375
412
|
m._ending = true;
|
|
376
413
|
}
|
|
414
|
+
return m;
|
|
377
415
|
}
|
|
378
416
|
/**
|
|
379
417
|
*
|
|
380
418
|
* @param {string} name
|
|
381
419
|
* @returns
|
|
382
420
|
*/ hasHeader(name) {
|
|
383
|
-
return this.opt.headers.includes(name);
|
|
421
|
+
return Object.keys(this.opt.headers).includes(name);
|
|
384
422
|
}
|
|
385
423
|
/**
|
|
386
424
|
*
|
|
@@ -392,6 +430,7 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
392
430
|
/**
|
|
393
431
|
* Sets a header value on the current native request
|
|
394
432
|
* @param {string} name
|
|
433
|
+
* @param {string} value
|
|
395
434
|
*/ setHeader(name, value) {
|
|
396
435
|
this.opt.headers[name] = value;
|
|
397
436
|
this._currentRequest?.setHeader(name, value);
|
|
@@ -469,7 +508,10 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
469
508
|
m.on('close', clearTimer);
|
|
470
509
|
return m;
|
|
471
510
|
}
|
|
472
|
-
|
|
511
|
+
/**
|
|
512
|
+
*
|
|
513
|
+
* @param {*} options
|
|
514
|
+
*/ sanitizeOptions(options) {
|
|
473
515
|
// Ensure headers are always present
|
|
474
516
|
if (!options.headers) options.headers = {};
|
|
475
517
|
// Since http.request treats host as an alias of hostname,
|
|
@@ -495,14 +537,14 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
495
537
|
}
|
|
496
538
|
/**
|
|
497
539
|
* Processes a response from the current native request
|
|
498
|
-
* @param {
|
|
540
|
+
* @param {Response} response
|
|
499
541
|
* @returns
|
|
500
542
|
*/ processResponse(response) {
|
|
501
543
|
const m = this;
|
|
502
544
|
const { opt } = m;
|
|
503
545
|
// Store the redirected response
|
|
504
546
|
const { statusCode } = response;
|
|
505
|
-
if (
|
|
547
|
+
if (opt.trackRedirects) {
|
|
506
548
|
m._redirects.push({
|
|
507
549
|
url: m._currentUrl,
|
|
508
550
|
headers: response.headers,
|
|
@@ -521,7 +563,7 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
521
563
|
statusCode,
|
|
522
564
|
headers: response.headers
|
|
523
565
|
}, 'processResponse');
|
|
524
|
-
if (!location ||
|
|
566
|
+
if (!location || opt.followRedirects === false || statusCode < 300 || statusCode >= 400) {
|
|
525
567
|
// 非重定向,返回给原始回调处理
|
|
526
568
|
response.responseUrl = m._currentUrl;
|
|
527
569
|
response.redirects = m._redirects;
|
|
@@ -545,7 +587,7 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
545
587
|
m.emit('response', response, responseStream);
|
|
546
588
|
// Clean up
|
|
547
589
|
m._requestBodyBuffers = [];
|
|
548
|
-
return;
|
|
590
|
+
return; // 退出,不继续处理
|
|
549
591
|
}
|
|
550
592
|
// The response is a redirect, so abort the current request
|
|
551
593
|
destroyRequest(m._currentRequest);
|
|
@@ -553,34 +595,35 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
553
595
|
response.destroy();
|
|
554
596
|
// RFC7231§6.4: A client SHOULD detect and intervene
|
|
555
597
|
// in cyclical redirections (i.e., "infinite" redirection loops).
|
|
556
|
-
if (++m._redirectCount >
|
|
598
|
+
if (++m._redirectCount > opt.maxRedirects) throw new TooManyRedirectsError();
|
|
557
599
|
// Store the request headers if applicable
|
|
558
600
|
let requestHeaders;
|
|
559
|
-
const { beforeRedirect } =
|
|
601
|
+
const { beforeRedirect } = opt;
|
|
560
602
|
if (beforeRedirect) {
|
|
561
603
|
requestHeaders = {
|
|
562
604
|
// The Host header was set by nativeProtocol.request
|
|
605
|
+
// @ts-ignore
|
|
563
606
|
Host: response.req.getHeader('host'),
|
|
564
|
-
...
|
|
607
|
+
...opt.headers
|
|
565
608
|
};
|
|
566
609
|
}
|
|
567
610
|
// RFC7231§6.4: Automatic redirection needs to done with
|
|
568
611
|
// care for methods not known to be safe, […]
|
|
569
612
|
// RFC7231§6.4.2–3: For historical reasons, a user agent MAY change
|
|
570
613
|
// the request method from POST to GET for the subsequent request.
|
|
571
|
-
const { method } =
|
|
572
|
-
if ((statusCode === 301 || statusCode === 302) &&
|
|
614
|
+
const { method } = opt;
|
|
615
|
+
if ((statusCode === 301 || statusCode === 302) && opt.method === 'POST' || // RFC7231§6.4.4: The 303 (See Other) status code indicates that
|
|
573
616
|
// the server is redirecting the user agent to a different resource […]
|
|
574
617
|
// A user agent can perform a retrieval request targeting that URI
|
|
575
618
|
// (a GET or HEAD request if using HTTP) […]
|
|
576
|
-
statusCode === 303 && !/^(?:GET|HEAD)$/.test(
|
|
619
|
+
statusCode === 303 && !/^(?:GET|HEAD)$/.test(opt.method)) {
|
|
577
620
|
m.opt.method = 'GET';
|
|
578
621
|
// Drop a possible entity and headers related to it
|
|
579
622
|
m._requestBodyBuffers = [];
|
|
580
|
-
removeMatchingHeaders(/^content-/i,
|
|
623
|
+
removeMatchingHeaders(/^content-/i, opt.headers);
|
|
581
624
|
}
|
|
582
625
|
// Drop the Host header, as the redirect might lead to a different host
|
|
583
|
-
const currentHostHeader = removeMatchingHeaders(/^host$/i,
|
|
626
|
+
const currentHostHeader = removeMatchingHeaders(/^host$/i, opt.headers);
|
|
584
627
|
// If the redirect is relative, carry over the host of the last request
|
|
585
628
|
const currentUrlParts = utils.parseUrl(m._currentUrl);
|
|
586
629
|
const currentHost = currentHostHeader || currentUrlParts.host;
|
|
@@ -611,8 +654,8 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
611
654
|
method,
|
|
612
655
|
headers: requestHeaders
|
|
613
656
|
};
|
|
614
|
-
beforeRedirect(
|
|
615
|
-
m.sanitizeOptions(
|
|
657
|
+
beforeRedirect(opt, responseDetails, requestDetails);
|
|
658
|
+
m.sanitizeOptions(opt);
|
|
616
659
|
}
|
|
617
660
|
// Perform the redirected request
|
|
618
661
|
m.request() // 重新执行请求
|
|
@@ -620,11 +663,11 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
620
663
|
}
|
|
621
664
|
/**
|
|
622
665
|
* 处理响应stream
|
|
623
|
-
*
|
|
666
|
+
* 自动解压,透传流,需设置 decompress = false,避免解压数据
|
|
624
667
|
* @param {*} res
|
|
625
668
|
*/ processStream(res) {
|
|
626
669
|
const m = this;
|
|
627
|
-
const { opt
|
|
670
|
+
const { opt } = m;
|
|
628
671
|
const streams = [
|
|
629
672
|
res
|
|
630
673
|
];
|
|
@@ -635,13 +678,13 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
635
678
|
responseLength,
|
|
636
679
|
headers: res.headers
|
|
637
680
|
});
|
|
638
|
-
if (
|
|
639
|
-
|
|
640
|
-
streams.push(
|
|
681
|
+
if (opt.transformStream) {
|
|
682
|
+
opt.transformStream.responseLength = responseLength;
|
|
683
|
+
streams.push(opt.transformStream);
|
|
641
684
|
}
|
|
642
|
-
const empty = utils.noBody(
|
|
685
|
+
const empty = utils.noBody(opt.method, res.statusCode);
|
|
643
686
|
// decompress the response body transparently if required
|
|
644
|
-
if (
|
|
687
|
+
if (opt.decompress !== false && res.headers['content-encoding']) {
|
|
645
688
|
// if decompress disabled we should not decompress
|
|
646
689
|
// 压缩内容,加入 解压 stream,自动解压,axios v1.2 存在bug,不能自动解压
|
|
647
690
|
// if no content, but headers still say that it is encoded,
|
|
@@ -675,13 +718,14 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
675
718
|
default:
|
|
676
719
|
}
|
|
677
720
|
}
|
|
721
|
+
// 响应流,用于读
|
|
678
722
|
const responseStream = streams.length > 1 ? stream.pipeline(streams, utils.noop) : streams[0];
|
|
679
723
|
// 将内部 responseStream 可读流 映射到 redirectReq
|
|
680
724
|
m.responseStream = responseStream;
|
|
681
725
|
responseStream.redirectReq = m // 事情触发时引用
|
|
682
726
|
;
|
|
683
727
|
// stream 模式,事件透传到 请求类
|
|
684
|
-
if (
|
|
728
|
+
if (opt.stream) {
|
|
685
729
|
if (m._paused) responseStream.pause();
|
|
686
730
|
// 写入目的流
|
|
687
731
|
for (const dest of m.pipedests)m.pipeDest(dest);
|
|
@@ -708,13 +752,18 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
708
752
|
}
|
|
709
753
|
// Read Stream API
|
|
710
754
|
/**
|
|
755
|
+
* 建立读取流管道
|
|
711
756
|
* read stream to write stream
|
|
712
757
|
* pipe 只是建立连接管道,后续自动传输数据
|
|
713
|
-
* @
|
|
714
|
-
* @
|
|
715
|
-
* @
|
|
716
|
-
|
|
758
|
+
* @override - 重写父类方法
|
|
759
|
+
* @template T - 需要模板
|
|
760
|
+
* @param {T & stream.Writable} dest - The writable stream to which data is written.
|
|
761
|
+
* @param {Object} [opts] - Optional configuration object.
|
|
762
|
+
* @param {boolean} [opts.end=true] - Whether to end the writable stream when the readable stream ends.
|
|
763
|
+
* @returns {T} The destination stream.
|
|
764
|
+
*/ pipe(dest, opts = {}) {
|
|
717
765
|
const m = this;
|
|
766
|
+
// m.pipe()
|
|
718
767
|
// 请求已响应
|
|
719
768
|
if (m.responseStream) {
|
|
720
769
|
// 已有数据,不可pipe
|
|
@@ -778,7 +827,7 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
778
827
|
if (response?.headers && dest.headers && !dest.headersSent) {
|
|
779
828
|
const caseless = new Caseless(response.headers);
|
|
780
829
|
if (caseless.has('content-type')) {
|
|
781
|
-
const ctname = caseless.has('content-type');
|
|
830
|
+
const ctname = /** @type {string} */ caseless.has('content-type');
|
|
782
831
|
if (dest.setHeader) {
|
|
783
832
|
dest.setHeader(ctname, response.headers[ctname]);
|
|
784
833
|
} else {
|
|
@@ -786,7 +835,7 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
786
835
|
}
|
|
787
836
|
}
|
|
788
837
|
if (caseless.has('content-length')) {
|
|
789
|
-
const clname = caseless.has('content-length');
|
|
838
|
+
const clname = /** @type {string} */ caseless.has('content-length');
|
|
790
839
|
if (dest.setHeader) {
|
|
791
840
|
dest.setHeader(clname, response.headers[clname]);
|
|
792
841
|
} else {
|
|
@@ -795,32 +844,26 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
795
844
|
}
|
|
796
845
|
}
|
|
797
846
|
if (response?.headers && dest.setHeader && !dest.headersSent) {
|
|
798
|
-
for (const
|
|
799
|
-
dest.setHeader(h, response.headers[h]);
|
|
800
|
-
}
|
|
847
|
+
for (const k of Object.keys(response.headers))dest.setHeader(k, response.headers[k]);
|
|
801
848
|
dest.statusCode = response.statusCode;
|
|
802
849
|
}
|
|
803
|
-
if (m.pipefilter)
|
|
804
|
-
m.pipefilter(response, dest);
|
|
805
|
-
}
|
|
850
|
+
if (m.pipefilter) m.pipefilter(response, dest);
|
|
806
851
|
}
|
|
807
852
|
/**
|
|
808
853
|
* 暂停read流
|
|
809
|
-
|
|
810
|
-
*/ pause(...args) {
|
|
854
|
+
*/ pause() {
|
|
811
855
|
const m = this;
|
|
812
856
|
// 没有流
|
|
813
857
|
if (!m.responseStream) m._paused = true;
|
|
814
|
-
else m.responseStream.pause(
|
|
858
|
+
else m.responseStream.pause();
|
|
815
859
|
return m;
|
|
816
860
|
}
|
|
817
861
|
/**
|
|
818
|
-
* 继续read
|
|
819
|
-
|
|
820
|
-
*/ resume(...args) {
|
|
862
|
+
* 继续read响应流
|
|
863
|
+
*/ resume() {
|
|
821
864
|
const m = this;
|
|
822
865
|
if (!m.responseStream) m._paused = false;
|
|
823
|
-
else m.responseStream.resume(
|
|
866
|
+
else m.responseStream.resume();
|
|
824
867
|
return m;
|
|
825
868
|
}
|
|
826
869
|
isPaused() {
|
|
@@ -838,17 +881,27 @@ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', '
|
|
|
838
881
|
request.on('error', utils.noop);
|
|
839
882
|
request.destroy(error);
|
|
840
883
|
}
|
|
841
|
-
|
|
884
|
+
/**
|
|
885
|
+
*
|
|
886
|
+
* @param {RegExp} regex
|
|
887
|
+
* @param {Object.<string, string>} headers
|
|
888
|
+
* @returns
|
|
889
|
+
*/ function removeMatchingHeaders(regex, headers) {
|
|
842
890
|
let lastValue;
|
|
843
|
-
Object.keys(headers)
|
|
891
|
+
for (const k of Object.keys(headers)){
|
|
844
892
|
if (regex.test(k)) {
|
|
845
893
|
lastValue = headers[k];
|
|
846
894
|
delete headers[k];
|
|
847
895
|
}
|
|
848
|
-
}
|
|
896
|
+
}
|
|
849
897
|
return lastValue === null || typeof lastValue === 'undefined' ? undefined : String(lastValue).trim();
|
|
850
898
|
}
|
|
851
|
-
|
|
899
|
+
/**
|
|
900
|
+
*
|
|
901
|
+
* @param {string} subdomain
|
|
902
|
+
* @param {string} domain
|
|
903
|
+
* @returns
|
|
904
|
+
*/ function isSubdomain(subdomain, domain) {
|
|
852
905
|
assert(utils.isString(subdomain) && utils.isString(domain));
|
|
853
906
|
const dot = subdomain.length - domain.length - 1;
|
|
854
907
|
return dot > 0 && subdomain[dot] === '.' && subdomain.endsWith(domain);
|
package/lib/utils.js
CHANGED
|
@@ -25,20 +25,28 @@ const preservedUrlFields = [
|
|
|
25
25
|
'hash'
|
|
26
26
|
];
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
29
|
-
* @param {
|
|
30
|
-
* @param {
|
|
31
|
-
* @param {
|
|
32
|
-
* @returns
|
|
28
|
+
* Create a custom error type.
|
|
29
|
+
* @param {string} code - The error code.
|
|
30
|
+
* @param {string} message - The error message.
|
|
31
|
+
* @param {typeof Error} [baseClass] - The base error class to extend from. Defaults to `Error`.
|
|
32
|
+
* @returns {typeof Error & { new(properties?: object): CustomErrorInstance }} A custom error constructor.
|
|
33
|
+
* new(properties?: object) 为构造函数语法,返回 CustomErrorInstance 类型
|
|
34
|
+
* @typedef {object} CustomErrorInstance
|
|
35
|
+
* @property {string} code - The error code.
|
|
36
|
+
* @property {string} message - The error message.
|
|
37
|
+
* @property {Error | undefined} cause - The optional error cause.
|
|
33
38
|
*/ function createErrorType(code, message, baseClass) {
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Create constructor
|
|
41
|
+
* @param {*} properties
|
|
42
|
+
*/ function CustomError(properties) {
|
|
36
43
|
// istanbul ignore else
|
|
37
44
|
if (isFunction(Error.captureStackTrace)) {
|
|
38
45
|
Error.captureStackTrace(this, this.constructor);
|
|
39
46
|
}
|
|
40
47
|
Object.assign(this, properties || {});
|
|
41
48
|
this.code = code;
|
|
49
|
+
// @ts-ignore
|
|
42
50
|
this.message = this.cause ? `${message}: ${this.cause.message}` : message;
|
|
43
51
|
}
|
|
44
52
|
// Attach constructor and set default properties
|
|
@@ -53,6 +61,7 @@ const preservedUrlFields = [
|
|
|
53
61
|
enumerable: false
|
|
54
62
|
}
|
|
55
63
|
});
|
|
64
|
+
// @ts-ignore
|
|
56
65
|
return CustomError;
|
|
57
66
|
}
|
|
58
67
|
const InvalidUrlError = createErrorType('ERR_INVALID_URL', 'Invalid URL', TypeError);
|
|
@@ -122,7 +131,11 @@ const noop = ()=>{};
|
|
|
122
131
|
*/ function isURL(value) {
|
|
123
132
|
return URL && value instanceof URL;
|
|
124
133
|
}
|
|
125
|
-
|
|
134
|
+
/**
|
|
135
|
+
*
|
|
136
|
+
* @param {*} rs
|
|
137
|
+
* @returns
|
|
138
|
+
*/ function isReadStream(rs) {
|
|
126
139
|
return rs.readable && rs.path && rs.mode;
|
|
127
140
|
}
|
|
128
141
|
/**
|