@wiajs/request 3.0.33 → 3.0.36

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.mjs CHANGED
@@ -1,82 +1,155 @@
1
1
  /*!
2
- * wia request v3.0.33
3
- * (c) 2022-2024 Sibyl Yu and contributors
2
+ * wia request v3.0.36
3
+ * (c) 2022-2025 Sibyl Yu and contributors
4
4
  * Released under the MIT License.
5
5
  */
6
- import stream, { Duplex } from 'node:stream';
7
6
  import { log as log$2, name } from '@wiajs/log';
8
- import http from 'node:http';
9
- import https from 'node:https';
10
- import assert from 'node:assert';
11
- import url from 'node:url';
12
- import zlib from 'node:zlib';
7
+ import stream from 'stream';
8
+ import assert from 'assert';
9
+ import http from 'http';
10
+ import https from 'https';
13
11
  import mime from 'mime-types';
12
+ import stream$1, { Duplex } from 'node:stream';
13
+ import url from 'url';
14
+ import zlib from 'zlib';
14
15
 
15
- class ZlibTransform extends stream.Transform {
16
- /**
17
- *
18
- * @param {*} chunk
19
- * @param {*} encoding
20
- * @param {*} callback
21
- */
22
- __transform(chunk, encoding, callback) {
23
- this.push(chunk);
24
- callback();
25
- }
26
-
27
- /**
28
- *
29
- * @param {*} chunk
30
- * @param {*} encoding
31
- * @param {*} callback
32
- */
33
- _transform(chunk, encoding, callback) {
34
- if (chunk.length !== 0) {
35
- this._transform = this.__transform;
36
-
37
- // Add Default Compression headers if no zlib headers are present
38
- if (chunk[0] !== 120) {
39
- // Hex: 78
40
- const header = Buffer.alloc(2);
41
- header[0] = 120; // Hex: 78
42
- header[1] = 156; // Hex: 9C
43
- this.push(header, encoding);
44
- }
45
- }
46
-
47
- this.__transform(chunk, encoding, callback);
48
- }
49
- }
16
+ let ZlibTransform = class ZlibTransform extends stream.Transform {
17
+ /**
18
+ *
19
+ * @param {*} chunk
20
+ * @param {*} encoding
21
+ * @param {*} callback
22
+ */ __transform(chunk, encoding, callback) {
23
+ this.push(chunk);
24
+ callback();
25
+ }
26
+ /**
27
+ *
28
+ * @param {*} chunk
29
+ * @param {*} encoding
30
+ * @param {*} callback
31
+ */ _transform(chunk, encoding, callback) {
32
+ if (chunk.length !== 0) {
33
+ this._transform = this.__transform;
34
+ // Add Default Compression headers if no zlib headers are present
35
+ if (chunk[0] !== 120) {
36
+ // Hex: 78
37
+ const header = Buffer.alloc(2);
38
+ header[0] = 120 // Hex: 78
39
+ ;
40
+ header[1] = 156 // Hex: 9C
41
+ ;
42
+ this.push(header, encoding);
43
+ }
44
+ }
45
+ this.__transform(chunk, encoding, callback);
46
+ }
47
+ };
50
48
 
51
- /**
52
- * utils for request
53
- */
54
-
55
- const {URL} = url;
56
-
57
- // Whether to use the native URL object or the legacy url module
58
- let useNativeURL = false;
59
- try {
60
- assert(new URL(''));
61
- } catch (error) {
62
- useNativeURL = error.code === 'ERR_INVALID_URL';
63
- }
64
-
65
- // URL fields to preserve in copy operations
66
- const preservedUrlFields = [
67
- 'auth',
68
- 'host',
69
- 'hostname',
70
- 'href',
71
- 'path',
72
- 'pathname',
73
- 'port',
74
- 'protocol',
75
- 'query',
76
- 'search',
77
- 'hash',
78
- ];
79
-
49
+ let Caseless = class Caseless {
50
+ /**
51
+ *
52
+ * @param {*} name
53
+ * @param {*} value
54
+ * @param {*} clobber
55
+ * @returns
56
+ */ set(name, value, clobber) {
57
+ if (typeof name === 'object') {
58
+ for (const n of name){
59
+ this.set(n, name[n], value);
60
+ }
61
+ } else {
62
+ if (typeof clobber === 'undefined') clobber = true;
63
+ const has = this.has(name);
64
+ if (!clobber && has) this.dict[has] = this.dict[has] + ',' + value;
65
+ else this.dict[has || name] = value;
66
+ return has;
67
+ }
68
+ }
69
+ /**
70
+ *
71
+ * @param {string} name
72
+ * @returns
73
+ */ has(name) {
74
+ const keys = Object.keys(this.dict);
75
+ name = name.toLowerCase();
76
+ for(let i = 0; i < keys.length; i++){
77
+ if (keys[i].toLowerCase() === name) return keys[i];
78
+ }
79
+ return false;
80
+ }
81
+ /**
82
+ *
83
+ * @param {string} name
84
+ * @returns
85
+ */ get(name) {
86
+ name = name.toLowerCase();
87
+ let result;
88
+ let _key;
89
+ const headers = this.dict;
90
+ for (const key of Object.keys(headers)){
91
+ _key = key.toLowerCase();
92
+ if (name === _key) result = headers[key];
93
+ }
94
+ return result;
95
+ }
96
+ /**
97
+ *
98
+ * @param {string} name
99
+ * @returns
100
+ */ swap(name) {
101
+ const has = this.has(name);
102
+ if (has === name) return;
103
+ if (!has) throw new Error('There is no header than matches "' + name + '"');
104
+ this.dict[name] = this.dict[has];
105
+ delete this.dict[has];
106
+ }
107
+ /**
108
+ *
109
+ * @param {string} name
110
+ * @returns
111
+ */ del(name) {
112
+ name = String(name).toLowerCase();
113
+ let deleted = false;
114
+ let changed = 0;
115
+ const dict = this.dict;
116
+ for (const key of Object.keys(this.dict)){
117
+ if (name === String(key).toLowerCase()) {
118
+ deleted = delete dict[key];
119
+ changed += 1;
120
+ }
121
+ }
122
+ return changed === 0 ? true : deleted;
123
+ }
124
+ /**
125
+ * @param {*} dict
126
+ */ constructor(dict){
127
+ this.dict = dict || {};
128
+ }
129
+ };
130
+
131
+ const { URL } = url;
132
+ // Whether to use the native URL object or the legacy url module
133
+ let useNativeURL = false;
134
+ try {
135
+ assert(new URL(''));
136
+ } catch (error) {
137
+ useNativeURL = error.code === 'ERR_INVALID_URL';
138
+ }
139
+ // URL fields to preserve in copy operations
140
+ const preservedUrlFields = [
141
+ 'auth',
142
+ 'host',
143
+ 'hostname',
144
+ 'href',
145
+ 'path',
146
+ 'pathname',
147
+ 'port',
148
+ 'protocol',
149
+ 'query',
150
+ 'search',
151
+ 'hash'
152
+ ];
80
153
  /**
81
154
  * Create a custom error type.
82
155
  * @param {string} code - The error code.
@@ -88,366 +161,221 @@ const preservedUrlFields = [
88
161
  * @property {string} code - The error code.
89
162
  * @property {string} message - The error message.
90
163
  * @property {Error | undefined} cause - The optional error cause.
91
- */
92
- function createErrorType(code, message, baseClass) {
93
- /**
164
+ */ function createErrorType(code, message, baseClass) {
165
+ /**
94
166
  * Create constructor
95
167
  * @param {*} properties
96
- */
97
- function CustomError(properties) {
98
- // istanbul ignore else
99
- if (isFunction(Error.captureStackTrace)) {
100
- Error.captureStackTrace(this, this.constructor);
101
- }
102
- Object.assign(this, properties || {});
103
- this.code = code;
104
- // @ts-ignore
105
- this.message = this.cause ? `${message}: ${this.cause.message}` : message;
106
- }
107
-
108
- // Attach constructor and set default properties
109
- CustomError.prototype = new (baseClass || Error)();
110
- Object.defineProperties(CustomError.prototype, {
111
- constructor: {
112
- value: CustomError,
113
- enumerable: false,
114
- },
115
- name: {
116
- value: `Error [${code}]`,
117
- enumerable: false,
118
- },
119
- });
120
-
121
- // @ts-ignore
122
- return CustomError
123
- }
124
-
125
- const InvalidUrlError = createErrorType('ERR_INVALID_URL', 'Invalid URL', TypeError);
126
-
127
- // @ts-ignore
128
- const typeOfTest = type => thing => typeof thing === type;
129
-
168
+ */ function CustomError(properties) {
169
+ // istanbul ignore else
170
+ if (isFunction(Error.captureStackTrace)) {
171
+ Error.captureStackTrace(this, this.constructor);
172
+ }
173
+ Object.assign(this, properties || {});
174
+ this.code = code;
175
+ // @ts-ignore
176
+ this.message = this.cause ? `${message}: ${this.cause.message}` : message;
177
+ }
178
+ // Attach constructor and set default properties
179
+ CustomError.prototype = new (baseClass || Error)();
180
+ Object.defineProperties(CustomError.prototype, {
181
+ constructor: {
182
+ value: CustomError,
183
+ enumerable: false
184
+ },
185
+ name: {
186
+ value: `Error [${code}]`,
187
+ enumerable: false
188
+ }
189
+ });
190
+ // @ts-ignore
191
+ return CustomError;
192
+ }
193
+ const InvalidUrlError = createErrorType('ERR_INVALID_URL', 'Invalid URL', TypeError);
194
+ // @ts-ignore
195
+ const typeOfTest = (type)=>(thing)=>typeof thing === type;
130
196
  /**
131
197
  * Determine if a value is a String
132
198
  *
133
199
  * @param {*} val The value to test
134
200
  *
135
201
  * @returns {boolean} True if value is a String, otherwise false
136
- */
137
- const isString = typeOfTest('string');
138
-
202
+ */ const isString = typeOfTest('string');
139
203
  /**
140
204
  * Determine if a value is an Array
141
205
  *
142
206
  * @param {Object} val The value to test
143
207
  *
144
208
  * @returns {boolean} True if value is an Array, otherwise false
145
- */
146
- const {isArray} = Array;
147
-
209
+ */ const { isArray } = Array;
148
210
  /**
149
211
  * Determine if a value is undefined
150
212
  *
151
213
  * @param {*} val The value to test
152
214
  *
153
215
  * @returns {boolean} True if the value is undefined, otherwise false
154
- */
155
- const isUndefined = typeOfTest('undefined');
156
-
216
+ */ const isUndefined = typeOfTest('undefined');
157
217
  /**
158
218
  * Determine if a value is a Buffer
159
219
  *
160
220
  * @param {*} val The value to test
161
221
  *
162
222
  * @returns {boolean} True if value is a Buffer, otherwise false
163
- */
164
- function isBuffer(val) {
165
- return (
166
- val !== null &&
167
- !isUndefined(val) &&
168
- val.constructor !== null &&
169
- !isUndefined(val.constructor) &&
170
- isFunction(val.constructor.isBuffer) &&
171
- val.constructor.isBuffer(val)
172
- )
173
- }
174
-
223
+ */ function isBuffer(val) {
224
+ return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor) && isFunction(val.constructor.isBuffer) && val.constructor.isBuffer(val);
225
+ }
175
226
  /**
176
227
  * Determine if a value is a Function
177
228
  *
178
229
  * @param {*} val The value to test
179
230
  * @returns {boolean} True if value is a Function, otherwise false
180
- */
181
- const isFunction = typeOfTest('function');
182
-
231
+ */ const isFunction = typeOfTest('function');
183
232
  /**
184
233
  * Determine if a value is a Number
185
234
  *
186
235
  * @param {*} val The value to test
187
236
  *
188
237
  * @returns {boolean} True if value is a Number, otherwise false
189
- */
190
- const isNumber = typeOfTest('number');
191
-
238
+ */ const isNumber = typeOfTest('number');
192
239
  /**
193
240
  * Determine if a value is an Object
194
241
  *
195
242
  * @param {*} thing The value to test
196
243
  *
197
244
  * @returns {boolean} True if value is an Object, otherwise false
198
- */
199
- const isObject = thing => thing !== null && typeof thing === 'object';
200
-
245
+ */ const isObject = (thing)=>thing !== null && typeof thing === 'object';
201
246
  /**
202
247
  * Determine if a value is a Boolean
203
248
  *
204
249
  * @param {*} thing The value to test
205
250
  * @returns {boolean} True if value is a Boolean, otherwise false
206
- */
207
- const isBoolean = thing => thing === true || thing === false;
208
-
209
- const noop = () => {};
210
-
251
+ */ const isBoolean = (thing)=>thing === true || thing === false;
252
+ const noop = ()=>{};
211
253
  /**
212
254
  *
213
255
  * @param {*} value
214
256
  * @returns
215
- */
216
- function isURL(value) {
217
- return URL && value instanceof URL
218
- }
219
-
257
+ */ function isURL(value) {
258
+ return URL && value instanceof URL;
259
+ }
220
260
  /**
221
261
  *
222
262
  * @param {*} rs
223
263
  * @returns
224
- */
225
- function isReadStream(rs) {
226
- return rs.readable && rs.path && rs.mode
227
- }
228
-
264
+ */ function isReadStream(rs) {
265
+ return rs.readable && rs.path && rs.mode;
266
+ }
229
267
  /**
230
268
  *
231
269
  * @param {*} urlObject
232
270
  * @param {*} target
233
271
  * @returns
234
- */
235
- function spreadUrlObject(urlObject, target) {
236
- const spread = target || {};
237
- for (const key of preservedUrlFields) {
238
- spread[key] = urlObject[key];
239
- }
240
-
241
- // Fix IPv6 hostname
242
- if (spread.hostname.startsWith('[')) {
243
- spread.hostname = spread.hostname.slice(1, -1);
244
- }
245
- // Ensure port is a number
246
- if (spread.port !== '') {
247
- spread.port = Number(spread.port);
248
- }
249
- // Concatenate path
250
- spread.path = spread.search ? spread.pathname + spread.search : spread.pathname;
251
-
252
- return spread
253
- }
254
-
272
+ */ function spreadUrlObject(urlObject, target) {
273
+ const spread = target || {};
274
+ for (const key of preservedUrlFields){
275
+ spread[key] = urlObject[key];
276
+ }
277
+ // Fix IPv6 hostname
278
+ if (spread.hostname.startsWith('[')) {
279
+ spread.hostname = spread.hostname.slice(1, -1);
280
+ }
281
+ // Ensure port is a number
282
+ if (spread.port !== '') {
283
+ spread.port = Number(spread.port);
284
+ }
285
+ // Concatenate path
286
+ spread.path = spread.search ? spread.pathname + spread.search : spread.pathname;
287
+ return spread;
288
+ }
255
289
  /**
256
290
  *
257
291
  * @param {*} input
258
292
  * @returns
259
- */
260
- function parseUrl(input) {
261
- let parsed;
262
- // istanbul ignore else
263
- if (useNativeURL) {
264
- parsed = new URL(input);
265
- } else {
266
- // Ensure the URL is valid and absolute
267
- parsed = validateUrl(url.parse(input));
268
- if (!isString(parsed.protocol)) {
269
- throw new InvalidUrlError({input})
270
- }
271
- }
272
- return parsed
273
- }
274
-
293
+ */ function parseUrl(input) {
294
+ let parsed;
295
+ // istanbul ignore else
296
+ if (useNativeURL) {
297
+ parsed = new URL(input);
298
+ } else {
299
+ // Ensure the URL is valid and absolute
300
+ parsed = validateUrl(url.parse(input));
301
+ if (!isString(parsed.protocol)) {
302
+ throw new InvalidUrlError({
303
+ input
304
+ });
305
+ }
306
+ }
307
+ return parsed;
308
+ }
275
309
  /**
276
310
  *
277
311
  * @param {*} input
278
312
  * @returns
279
- */
280
- function validateUrl(input) {
281
- if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
282
- throw new InvalidUrlError({input: input.href || input})
283
- }
284
- if (/^\[/.test(input.host) && !/^\[[:0-9a-f]+\](:\d+)?$/i.test(input.host)) {
285
- throw new InvalidUrlError({input: input.href || input})
286
- }
287
- return input
288
- }
289
-
313
+ */ function validateUrl(input) {
314
+ if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
315
+ throw new InvalidUrlError({
316
+ input: input.href || input
317
+ });
318
+ }
319
+ if (/^\[/.test(input.host) && !/^\[[:0-9a-f]+\](:\d+)?$/i.test(input.host)) {
320
+ throw new InvalidUrlError({
321
+ input: input.href || input
322
+ });
323
+ }
324
+ return input;
325
+ }
290
326
  /**
291
327
  *
292
328
  * @param {*} relative
293
329
  * @param {*} base
294
330
  * @returns
295
- */
296
- function resolveUrl(relative, base) {
297
- // istanbul ignore next
298
- return useNativeURL ? new URL(relative, base) : parseUrl(url.resolve(base, relative))
299
- }
300
-
331
+ */ function resolveUrl(relative, base) {
332
+ // istanbul ignore next
333
+ return useNativeURL ? new URL(relative, base) : parseUrl(url.resolve(base, relative));
334
+ }
301
335
  /**
302
336
  *
303
337
  * @param {string} method
304
338
  * @param {number} code
305
339
  * @returns
306
- */
307
- function noBody(method, code) {
308
- return (
309
- method === 'HEAD' ||
310
- // Informational
311
- (code >= 100 && code < 200) ||
312
- // No Content
313
- code === 204 ||
314
- // Not Modified
315
- code === 304
316
- )
317
- }
318
-
340
+ */ function noBody(method, code) {
341
+ return method === 'HEAD' || // Informational
342
+ code >= 100 && code < 200 || // No Content
343
+ code === 204 || // Not Modified
344
+ code === 304;
345
+ }
319
346
  /**
320
347
  * Determine if a value is a Stream
321
348
  *
322
349
  * @param {*} val The value to test
323
350
  *
324
351
  * @returns {boolean} True if value is a Stream, otherwise false
325
- */
326
- const isStream = val => isObject(val) && isFunction(val.pipe);
327
-
328
- const utils = {
329
- createErrorType,
330
- InvalidUrlError,
331
- isString,
332
- isArray,
333
- isBuffer,
334
- isUndefined,
335
- isNumber,
336
- isBoolean,
337
- isFunction,
338
- isObject,
339
- isURL,
340
- isReadStream,
341
- isStream,
342
- noop,
343
- parseUrl,
344
- spreadUrlObject,
345
- validateUrl,
346
- resolveUrl,
347
- noBody,
352
+ */ const isStream = (val)=>isObject(val) && isFunction(val.pipe);
353
+ const utils = {
354
+ createErrorType,
355
+ InvalidUrlError,
356
+ isString,
357
+ isArray,
358
+ isBuffer,
359
+ isUndefined,
360
+ isNumber,
361
+ isBoolean,
362
+ isFunction,
363
+ isObject,
364
+ isURL,
365
+ isReadStream,
366
+ isStream,
367
+ noop,
368
+ parseUrl,
369
+ spreadUrlObject,
370
+ validateUrl,
371
+ resolveUrl,
372
+ noBody
348
373
  };
349
374
 
350
- class Caseless {
351
- /**
352
- * @param {*} dict
353
- */
354
- constructor(dict) {
355
- this.dict = dict || {};
356
- }
357
-
358
- /**
359
- *
360
- * @param {*} name
361
- * @param {*} value
362
- * @param {*} clobber
363
- * @returns
364
- */
365
- set(name, value, clobber) {
366
- if (typeof name === 'object') {
367
- for (const n of name) {
368
- this.set(n, name[n], value);
369
- }
370
- } else {
371
- if (typeof clobber === 'undefined') clobber = true;
372
- const has = this.has(name);
373
-
374
- if (!clobber && has) this.dict[has] = this.dict[has] + ',' + value;
375
- else this.dict[has || name] = value;
376
- return has
377
- }
378
- }
379
-
380
- /**
381
- *
382
- * @param {string} name
383
- * @returns
384
- */
385
- has(name) {
386
- const keys = Object.keys(this.dict);
387
- name = name.toLowerCase();
388
- for (let i = 0; i < keys.length; i++) {
389
- if (keys[i].toLowerCase() === name) return keys[i]
390
- }
391
- return false
392
- }
393
-
394
- /**
395
- *
396
- * @param {string} name
397
- * @returns
398
- */
399
- get(name) {
400
- name = name.toLowerCase();
401
- let result;
402
- let _key;
403
- const headers = this.dict;
404
- for (const key of Object.keys(headers)) {
405
- _key = key.toLowerCase();
406
- if (name === _key) result = headers[key];
407
- }
408
- return result
409
- }
410
-
411
- /**
412
- *
413
- * @param {string} name
414
- * @returns
415
- */
416
- swap(name) {
417
- const has = this.has(name);
418
- if (has === name) return
419
- if (!has) throw new Error('There is no header than matches "' + name + '"')
420
- this.dict[name] = this.dict[has];
421
- delete this.dict[has];
422
- }
423
-
424
- /**
425
- *
426
- * @param {string} name
427
- * @returns
428
- */
429
- del(name) {
430
- name = String(name).toLowerCase();
431
- let deleted = false;
432
- let changed = 0;
433
- const dict = this.dict;
434
- for (const key of Object.keys(this.dict)) {
435
- if (name === String(key).toLowerCase()) {
436
- deleted = delete dict[key];
437
- changed += 1;
438
- }
439
- }
440
- return changed === 0 ? true : deleted
441
- }
442
- }
443
-
444
- /**
445
- * fork from follow-redirects
446
- * https://github.com/follow-redirects/follow-redirects
447
- */
448
-
449
- const log$1 = log$2({env: `wia:req:${name(import.meta.url)}`}); // __filename
450
-
375
+ const log$1 = log$2({
376
+ env: `wia:req:${name(import.meta.url)}`
377
+ }) // __filename
378
+ ;
451
379
  /**
452
380
  * @typedef {object} Opts
453
381
  * @prop {Object.<string,string>} headers
@@ -467,466 +395,267 @@ const log$1 = log$2({env: `wia:req:${name(import.meta.url)}`}); // __filename
467
395
  * @prop {number} [maxBodyLength = -1]
468
396
  * @prop {*} [trackRedirects]
469
397
  * @prop {*} [data]
470
- */
471
-
472
- /** @typedef {object} ResponseExt
398
+ */ /** @typedef {object} ResponseExt
473
399
  * @prop {*[]} [redirects]
474
400
  * @prop {string} [responseUrl]
475
401
  * @prop {number} [responseStartTime]
476
- */
477
-
478
- /** @typedef { http.IncomingMessage & ResponseExt} Response */
479
-
480
- const httpModules = {'http:': http, 'https:': https};
481
-
482
- const zlibOptions = {
483
- flush: zlib.constants.Z_SYNC_FLUSH,
484
- finishFlush: zlib.constants.Z_SYNC_FLUSH,
485
- };
486
-
487
- const brotliOptions = {
488
- flush: zlib.constants.BROTLI_OPERATION_FLUSH,
489
- finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH,
490
- };
491
-
492
- const isBrotliSupported = utils.isFunction(zlib.createBrotliDecompress);
493
-
494
- // clientRequest 属性转发
495
- const writeProps = [
496
- 'protocol',
497
- 'method',
498
- 'path',
499
- 'host',
500
- 'reusedSocket',
501
- 'socket',
502
- 'closed',
503
- 'destroyed',
504
- 'writable',
505
- 'writableAborted',
506
- 'writableEnded',
507
- 'writableCorked',
508
- 'errored',
509
- 'writableFinished',
510
- 'writableHighWaterMark',
511
- 'writableLength',
512
- 'writableNeedDrain',
513
- 'writableObjectMode',
514
- ];
515
-
516
- // clientReq 方法转发
517
- const writeMethods = ['cork', 'flushHeaders', 'setNoDelay', 'setSocketKeepAlive'];
518
-
519
- // Create handlers that pass events from native requests
520
- // clientRequest 事件转发写事件
521
- const writeEvents = [
522
- // 'abort', // 弃用
523
- // 'aborted', // 弃用
524
- 'close',
525
- 'connect',
526
- 'continue',
527
- 'drain',
528
- // 'error', // 单独处理,未注册 'error' 事件处理程序,错误将冒泡到全局导致程序崩溃
529
- 'finish',
530
- 'information',
531
- 'pipe',
532
- // 'response', 由 processResponse 触发
533
- 'socket', // 建立连接时触发
534
- 'timeout',
535
- 'unpipe',
536
- 'upgrade',
537
- ];
538
-
539
- const writeEventEmit = Object.create(null);
540
-
541
- for (const ev of writeEvents)
542
- writeEventEmit[ev] = /** @param {...any} args */ function (...args) {
543
- const m = this; // 事件回调,this === clientRequest 实例
544
- // log('req event', {ev})
545
- m.redirectReq.emit(ev, ...args); // 内部请求req 事情转发到 Request
546
- };
547
-
548
- // stream.Readable,在响应流上转发读流取事件
549
- // data 单独处理
550
- const readEvents = ['close', 'end', 'error', 'pause', 'readable', 'resume'];
551
- const readEventEmit = Object.create(null);
552
- for (const ev of readEvents)
553
- readEventEmit[ev] = /** @param {...any} args */ function (...args) {
554
- const m = this; // 事件回调,this === clientRequest 实例
555
- // log('res event', {ev})
556
- m.redirectReq.emit(ev, ...args); // 向上触发事件
557
- };
558
-
559
- // Error types with codes
560
- const RedirectionError = utils.createErrorType(
561
- 'ERR_FR_REDIRECTION_FAILURE',
562
- 'Redirected request failed'
563
- );
564
-
565
- const TooManyRedirectsError = utils.createErrorType(
566
- 'ERR_FR_TOO_MANY_REDIRECTS',
567
- 'Maximum number of redirects exceeded',
568
- RedirectionError
569
- );
570
-
571
- const MaxBodyLengthExceededError = utils.createErrorType(
572
- 'ERR_FR_MAX_BODY_LENGTH_EXCEEDED',
573
- 'Request body larger than maxBodyLength limit'
574
- );
575
-
576
- const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', 'write after end');
577
-
578
- // request err
579
- const HostNotfoundError = utils.createErrorType('ERR_HOSTNOTFOUND', 'DNS 解析失败,主机名可能无效');
580
- const ConnRefusedError = utils.createErrorType(
581
- 'ERR_CONNREFUSED',
582
- '连接被拒绝,目标服务器可能不可用'
583
- );
584
- const ConnTimedoutError = utils.createErrorType(
585
- 'ERR_CONNTIMEDOUT',
586
- '请求超时,请检查网络连接或服务器负载'
587
- );
588
- const ConnResetError = utils.createErrorType(
589
- 'ERR_CONNRESET',
590
- '连接被重置,可能是网络问题或服务器关闭了连接'
591
- );
592
-
593
- /**
594
- * An HTTP(S) request that can be redirected
595
- * wrap http.ClientRequest
596
- */
597
- class Request extends Duplex {
598
- /** @type {NodeJS.Timeout} */
599
- _timeout = null
600
- /** @type {*} */
601
- socket = null
602
- /** @type {http.ClientRequest} */
603
- _currentRequest = null
604
- /** @type {Response} */
605
- response = null
606
- /** @type {stream.Readable} */
607
- responseStream = null
608
- timing = false
609
- responseStarted = false
610
- responseStartTime = 0
611
- _destdata = false
612
- _paused = false
613
- _respended = false
614
- /** @type {stream.Readable} */
615
- pipesrc = null // 被 pipe 时的 src stream
616
- /** @type {stream.Writable[]} */
617
- pipedests = [] // pipe dest
618
- /** @type {*} */
619
- startTimer = null
620
- /** @type {Opts} */
621
- opt
622
- /** @type {*} */
623
- pipefilter
624
- /** @type {string} */
625
- _currentUrl
626
-
627
- /**
628
- * responseCallback 原消息处理回调
629
- * @param {Opts} opts
630
- * @param {*} resCallback
631
- */
632
- constructor(opts, resCallback) {
633
- super();
634
- const m = this;
635
-
636
- // log({opts}, 'new Request')
637
-
638
- // Initialize the request
639
- m.sanitizeOptions(opts);
640
- m.opt = opts;
641
- m.headers = opts.headers;
642
-
643
- // log({opts}, 'constructor')
644
-
645
- m._ended = false;
646
- m._ending = false;
647
- m._redirectCount = 0;
648
- /** @type {any[]} */
649
- m._redirects = [];
650
- m._requestBodyLength = 0;
651
- /** @type {any[]} */
652
- m._requestBodyBuffers = [];
653
-
654
- // save the callback if passed
655
- m.resCallback = resCallback;
656
-
402
+ */ /** @typedef { http.IncomingMessage & ResponseExt} Response */ const httpModules = {
403
+ 'http:': http,
404
+ 'https:': https
405
+ };
406
+ const zlibOptions = {
407
+ flush: zlib.constants.Z_SYNC_FLUSH,
408
+ finishFlush: zlib.constants.Z_SYNC_FLUSH
409
+ };
410
+ const brotliOptions = {
411
+ flush: zlib.constants.BROTLI_OPERATION_FLUSH,
412
+ finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH
413
+ };
414
+ const isBrotliSupported = utils.isFunction(zlib.createBrotliDecompress);
415
+ // clientRequest 属性转发
416
+ const writeProps = [
417
+ 'protocol',
418
+ 'method',
419
+ 'path',
420
+ 'host',
421
+ 'reusedSocket',
422
+ 'socket',
423
+ 'closed',
424
+ 'destroyed',
425
+ 'writable',
426
+ 'writableAborted',
427
+ 'writableEnded',
428
+ 'writableCorked',
429
+ 'errored',
430
+ 'writableFinished',
431
+ 'writableHighWaterMark',
432
+ 'writableLength',
433
+ 'writableNeedDrain',
434
+ 'writableObjectMode'
435
+ ];
436
+ // clientReq 方法转发
437
+ const writeMethods = [
438
+ 'cork',
439
+ 'flushHeaders',
440
+ 'setNoDelay',
441
+ 'setSocketKeepAlive'
442
+ ];
443
+ // Create handlers that pass events from native requests
444
+ // 在 clientRequest 事件转发写事件
445
+ const writeEvents = [
446
+ // 'abort', // 弃用
447
+ // 'aborted', // 弃用
448
+ 'close',
449
+ 'connect',
450
+ 'continue',
451
+ 'drain',
452
+ // 'error', // 单独处理,未注册 'error' 事件处理程序,错误将冒泡到全局导致程序崩溃
453
+ 'finish',
454
+ 'information',
455
+ 'pipe',
456
+ // 'response', 由 processResponse 触发
457
+ 'socket',
458
+ 'timeout',
459
+ 'unpipe',
460
+ 'upgrade'
461
+ ];
462
+ const writeEventEmit = Object.create(null);
463
+ for (const ev of writeEvents)writeEventEmit[ev] = /** @param {...any} args */ function(...args) {
464
+ const m = this // 事件回调,this === clientRequest 实例
465
+ ;
466
+ // log('req event', {ev})
467
+ m.redirectReq.emit(ev, ...args) // 内部请求req 事情转发到 Request
468
+ ;
469
+ };
470
+ // stream.Readable,在响应流上转发读流取事件
471
+ // data 单独处理
472
+ const readEvents = [
473
+ 'close',
474
+ 'end',
475
+ 'error',
476
+ 'pause',
477
+ 'readable',
478
+ 'resume'
479
+ ];
480
+ const readEventEmit = Object.create(null);
481
+ for (const ev of readEvents)readEventEmit[ev] = /** @param {...any} args */ function(...args) {
482
+ const m = this // 事件回调,this === clientRequest 实例
483
+ ;
484
+ // log('res event', {ev})
485
+ m.redirectReq.emit(ev, ...args) // 向上触发事件
486
+ ;
487
+ };
488
+ // Error types with codes
489
+ const RedirectionError = utils.createErrorType('ERR_FR_REDIRECTION_FAILURE', 'Redirected request failed');
490
+ const TooManyRedirectsError = utils.createErrorType('ERR_FR_TOO_MANY_REDIRECTS', 'Maximum number of redirects exceeded', RedirectionError);
491
+ const MaxBodyLengthExceededError = utils.createErrorType('ERR_FR_MAX_BODY_LENGTH_EXCEEDED', 'Request body larger than maxBodyLength limit');
492
+ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', 'write after end');
493
+ // request err
494
+ const HostNotfoundError = utils.createErrorType('ERR_HOSTNOTFOUND', 'DNS 解析失败,主机名可能无效');
495
+ const ConnRefusedError = utils.createErrorType('ERR_CONNREFUSED', '连接被拒绝,目标服务器可能不可用');
496
+ const ConnTimedoutError = utils.createErrorType('ERR_CONNTIMEDOUT', '请求超时,请检查网络连接或服务器负载');
497
+ const ConnResetError = utils.createErrorType('ERR_CONNRESET', '连接被重置,可能是网络问题或服务器关闭了连接');
498
+ let Request = class Request extends Duplex {
657
499
  /**
658
- * React to responses of native requests
659
- * 接管 response 事件,非重定向,触发 response 事件
660
- * @param {Response} res
661
- */
662
- m._onResponse = res => {
663
- try {
664
- m.processResponse(res);
665
- } catch (cause) {
666
- m.emit(
667
- 'error',
668
- cause instanceof RedirectionError ? cause : new RedirectionError({cause: cause})
669
- );
670
- }
671
- };
672
-
673
- // Proxy all other public ClientRequest methods 'getHeader'
674
- for (const method of writeMethods) {
675
- // @ts-ignore
676
- m[method] = (a, b) => {
677
- // log(method, {a, b})
678
- // @ts-ignore
679
- m._currentRequest?.[method](a, b);
680
- };
681
- }
682
-
683
- // Proxy all public ClientRequest properties
684
- // 'aborted', 'connection' 弃用
685
- for (const property of writeProps) {
686
- Object.defineProperty(m, property, {
687
- get() {
688
- // @ts-ignore
689
- const val = m._currentRequest?.[property];
690
- // log('get property', {property})
691
- return val
692
- },
693
- });
694
- }
695
-
696
- // 流模式
697
- if (opts.stream) {
698
- // 被 pipe 作为目标时触发,拷贝 src headers
699
- m.on(
700
- 'pipe',
701
- /** @param {stream.Readable & {headers?: Object.<string, string>}} src */ src => {
702
- // m.ntick &&
703
- if (m._currentRequest) {
704
- m.emit(
705
- 'error',
706
- new Error('You cannot pipe to this stream after the outbound request has started.')
707
- );
708
- }
709
-
710
- m.pipesrc = src;
711
-
712
- if (utils.isReadStream(src)) {
713
- // @ts-ignore
714
- if (!m.hasHeader('content-type')) m.setHeader('content-type', mime.lookup(src.path));
715
- } else {
716
- // 拷贝请求头
717
- if (src.headers) {
718
- for (const k of Object.keys(src.headers)) {
719
- if (!m.hasHeader(k)) {
720
- m.setHeader(k, src.headers[k]);
721
- }
722
- }
723
- }
724
-
725
- // @ts-ignore
726
- if (src.opt.method && !m.opt.method) m.opt.method = src.opt.method;
727
- }
728
- }
729
- );
730
- }
731
-
732
- // Perform the first request
733
- // m.request(); // 创建时不连接,写入数据时连接,否则 pipe 时无法写入header
734
- }
735
-
736
- /**
737
500
  * Executes the next native request (initial or redirect)
738
501
  * @returns http(s) 实例
739
- */
740
- request() {
741
- let R = null;
742
- const m = this;
743
- const {opt} = m;
744
-
745
- try {
746
- // reset read stream
747
- m.response = null;
748
- m.responseStarted = false;
749
- m.responseStream = null;
750
- m.timing = false;
751
- m.responseStartTime = 0;
752
- m._destdata = false;
753
- m._paused = false;
754
- m._respended = false;
755
-
756
- // m.httpModule = httpModules[protocol];
757
-
758
- // Load the native protocol
759
- let {protocol} = opt;
760
- const {agents} = opt;
761
-
762
- // 代理以目的网址协议为准
763
- // If specified, use the agent corresponding to the protocol
764
- // (HTTP and HTTPS use different types of agents)
765
- // agents 优于 agent
766
- if (agents) {
767
- const scheme = protocol.slice(0, -1);
768
- opt.agent = agents[scheme];
769
-
770
- // http 非隧道代理模式,模块以代理主机为准,其他以目的网址为准
771
- // 代理内部会根据代理协议选择 http(s) 发起请求创建连接
772
- if (protocol === 'http:' && agents.http) {
773
- protocol =
774
- agents.http.proxy && !agents.http.tunnel ? agents.http.proxy.protocol : protocol;
775
- }
776
- }
777
-
778
- const httpModule = httpModules[protocol];
779
- if (!httpModule) throw TypeError(`Unsupported protocol: ${protocol}`)
780
-
781
- // log({opt, protocol}, 'request')
782
- // Create the native request and set up its event handlers
783
- // @ts-ignore
784
- const req = httpModule.request(opt, m._onResponse);
785
- m._currentRequest = req;
786
- // @ts-ignore
787
- req.redirectReq = m;
788
-
789
- // 启动 startTimer
790
- if (m.startTimer) m._currentRequest.once('socket', m.startTimer);
791
-
792
- // set tcp keep alive to prevent drop connection by peer
793
- req.on(
794
- 'socket',
795
- /** @param {*} socket */ socket => {
796
- // default interval of sending ack packet is 1 minute
797
- socket.setKeepAlive(true, 1000 * 60);
798
- }
799
- );
800
-
801
- // 请求error单独处理
802
- // 'error' 事件处理,避免错误将冒泡到全局导致程序崩溃
803
- req.on('error', err => {
804
- destroyRequest(req); // 释放资源
805
- // @ts-ignore
806
- log$1.error({errcode: err?.code}, 'request');
807
- // @ts-ignore
808
- switch (err?.code) {
809
- case 'ENOTFOUND':
810
- m.emit('error', new HostNotfoundError());
811
- break
812
- case 'ECONNREFUSED':
813
- m.emit('error', new ConnRefusedError());
814
- break
815
- case 'ETIMEDOUT':
816
- m.emit('error', new ConnTimedoutError());
817
- break
818
- case 'ECONNRESET':
819
- m.emit('error', new ConnResetError());
820
- break
821
- default:
822
- m.emit('error', utils.createErrorType('ERR_CONNOTHER', `网络错误: ${err.message}`));
823
- }
824
- });
825
-
826
- // 接收req事件,转发 request 上发射,网络关闭事件,触发 error
827
- for (const ev of writeEvents) req.on(ev, writeEventEmit[ev]);
828
-
829
- // RFC7230§5.3.1: When making a request directly to an origin server, […]
830
- // a client MUST send only the absolute path […] as the request-target.
831
- // When making a request to a proxy, […]
832
- // a client MUST send the target URI in absolute-form […].
833
- m._currentUrl = /^\//.test(opt.path) ? url.format(opt) : opt.path;
834
-
835
- // End a redirected request
836
- // (The first request must be ended explicitly with RedirectableRequest#end)
837
- if (m._isRedirect) {
838
- // Write the request entity and end
839
- let i = 0;
840
- const buffers = m._requestBodyBuffers;
841
-
842
- /**
502
+ */ request() {
503
+ let R = null;
504
+ const m = this;
505
+ const { opt } = m;
506
+ try {
507
+ // reset read stream
508
+ m.response = null;
509
+ m.responseStarted = false;
510
+ m.responseStream = null;
511
+ m.timing = false;
512
+ m.responseStartTime = 0;
513
+ m._destdata = false;
514
+ m._paused = false;
515
+ m._respended = false;
516
+ // m.httpModule = httpModules[protocol];
517
+ // Load the native protocol
518
+ let { protocol } = opt;
519
+ const { agents } = opt;
520
+ // 代理以目的网址协议为准
521
+ // If specified, use the agent corresponding to the protocol
522
+ // (HTTP and HTTPS use different types of agents)
523
+ // agents 优于 agent
524
+ if (agents) {
525
+ const scheme = protocol.slice(0, -1);
526
+ opt.agent = agents[scheme];
527
+ // http 非隧道代理模式,模块以代理主机为准,其他以目的网址为准
528
+ // 代理内部会根据代理协议选择 http(s) 发起请求创建连接
529
+ if (protocol === 'http:' && agents.http) {
530
+ protocol = agents.http.proxy && !agents.http.tunnel ? agents.http.proxy.protocol : protocol;
531
+ }
532
+ // log({scheme, agents, protocol}, 'request')
533
+ }
534
+ const httpModule = httpModules[protocol];
535
+ if (!httpModule) throw TypeError(`Unsupported protocol: ${protocol}`);
536
+ // log({opt, protocol}, 'request')
537
+ // Create the native request and set up its event handlers
538
+ // @ts-ignore
539
+ log$1({
540
+ httpModule,
541
+ opt
542
+ }, 'request');
543
+ const req = httpModule.request(opt, m._onResponse);
544
+ m._currentRequest = req;
545
+ // @ts-ignore
546
+ req.redirectReq = m;
547
+ // 启动 startTimer
548
+ if (m.startTimer) m._currentRequest.once('socket', m.startTimer);
549
+ // set tcp keep alive to prevent drop connection by peer
550
+ req.on('socket', /** @param {*} socket */ (socket)=>{
551
+ // default interval of sending ack packet is 1 minute
552
+ socket.setKeepAlive(true, 1000 * 60);
553
+ });
554
+ // 请求error单独处理
555
+ // 'error' 事件处理,避免错误将冒泡到全局导致程序崩溃
556
+ req.on('error', (err)=>{
557
+ destroyRequest(req) // 释放资源
558
+ ;
559
+ // @ts-ignore
560
+ log$1.error({
561
+ errcode: err == null ? void 0 : err.code
562
+ }, 'request');
563
+ // @ts-ignore
564
+ switch(err == null ? void 0 : err.code){
565
+ case 'ENOTFOUND':
566
+ m.emit('error', new HostNotfoundError());
567
+ break;
568
+ case 'ECONNREFUSED':
569
+ m.emit('error', new ConnRefusedError());
570
+ break;
571
+ case 'ETIMEDOUT':
572
+ m.emit('error', new ConnTimedoutError());
573
+ break;
574
+ case 'ECONNRESET':
575
+ m.emit('error', new ConnResetError());
576
+ break;
577
+ default:
578
+ m.emit('error', utils.createErrorType('ERR_CONNOTHER', `网络错误: ${err.message}`));
579
+ }
580
+ });
581
+ // 接收req事件,转发 到 request 上发射,网络关闭事件,触发 error
582
+ for (const ev of writeEvents)req.on(ev, writeEventEmit[ev]);
583
+ // RFC7230§5.3.1: When making a request directly to an origin server, […]
584
+ // a client MUST send only the absolute path […] as the request-target.
585
+ // When making a request to a proxy, […]
586
+ // a client MUST send the target URI in absolute-form […].
587
+ m._currentUrl = /^\//.test(opt.path) ? url.format(opt) : opt.path;
588
+ // End a redirected request
589
+ // (The first request must be ended explicitly with RedirectableRequest#end)
590
+ if (m._isRedirect) {
591
+ // Write the request entity and end
592
+ let i = 0;
593
+ const buffers = m._requestBodyBuffers;
594
+ /**
843
595
  *
844
596
  * @param {*} error
845
- */
846
- function writeNext(error) {
847
- // Only write if this request has not been redirected yet
848
- /* istanbul ignore else */
849
- if (req === m._currentRequest) {
850
- // Report any write errors
851
- /* istanbul ignore if */
852
- if (error) m.emit('error', error);
853
- // Write the next buffer if there are still left
854
- else if (i < buffers.length) {
855
- const buf = buffers[i++];
856
- /* istanbul ignore else */
857
- if (!req.finished) req.write(buf.data, buf.encoding, writeNext);
858
- }
859
- // End the request if `end` has been called on us
860
- else if (m._ended) req.end();
861
- }
862
- }
863
- writeNext();
864
- }
865
-
866
- R = req;
867
- } catch (e) {
868
- log$1.err(e, 'request');
869
- throw e
870
- }
871
-
872
- return R
873
- }
874
-
875
- /**
597
+ */ function writeNext(error) {
598
+ // Only write if this request has not been redirected yet
599
+ /* istanbul ignore else */ if (req === m._currentRequest) {
600
+ // Report any write errors
601
+ /* istanbul ignore if */ if (error) m.emit('error', error);
602
+ else if (i < buffers.length) {
603
+ const buf = buffers[i++];
604
+ /* istanbul ignore else */ if (!req.finished) req.write(buf.data, buf.encoding, writeNext);
605
+ } else if (m._ended) req.end();
606
+ }
607
+ }
608
+ writeNext();
609
+ }
610
+ R = req;
611
+ } catch (e) {
612
+ log$1.err(e, 'request');
613
+ throw e;
614
+ }
615
+ return R;
616
+ }
617
+ /**
876
618
  * 写入错误,释放请求,触发 abort 终止事件
877
- */
878
- abort() {
879
- destroyRequest(this._currentRequest);
880
- this.emit('abort');
881
- }
882
-
883
- /**
619
+ */ abort() {
620
+ destroyRequest(this._currentRequest);
621
+ this.emit('abort');
622
+ }
623
+ /**
884
624
  * 析构
885
625
  * @param {*} error
886
626
  * @returns
887
- */
888
- destroy(error) {
889
- const m = this;
890
- if (!m._ended) m.end();
891
- if (m.response) m.response.destroy();
892
- if (m.responseStream) m.responseStream.destroy();
893
-
894
- // m.clearTimeout();
895
- destroyRequest(m._currentRequest, error);
896
- super.destroy(error);
897
- return this
898
- }
899
-
900
- /**
627
+ */ destroy(error) {
628
+ const m = this;
629
+ if (!m._ended) m.end();
630
+ if (m.response) m.response.destroy();
631
+ if (m.responseStream) m.responseStream.destroy();
632
+ // m.clearTimeout();
633
+ destroyRequest(m._currentRequest, error);
634
+ super.destroy(error);
635
+ return this;
636
+ }
637
+ /**
901
638
  * 发送数据
902
- */
903
- send() {
904
- const m = this;
905
- const {data} = m.opt;
906
- // 发送数据
907
- if (utils.isStream(data)) {
908
-
909
- data.on('end', () => {
910
- });
911
-
912
- data.once(
913
- 'error',
914
- /** @param {*} err */ err => {
915
- // req.destroy(err)
916
- }
917
- );
918
-
919
- data.on('close', () => {
920
- // if (!ended && !errored) {
921
- // throw new WritebBeenAbortedError()
922
- // }
923
- });
924
-
925
- data.pipe(m); // 写入数据流
926
- } else m.end(data);
927
- }
928
-
929
- /**
639
+ */ send() {
640
+ const m = this;
641
+ const { data } = m.opt;
642
+ // 发送数据
643
+ if (utils.isStream(data)) {
644
+ data.on('end', ()=>{
645
+ });
646
+ data.once('error', /** @param {*} err */ (err)=>{
647
+ // req.destroy(err)
648
+ });
649
+ data.on('close', ()=>{
650
+ // if (!ended && !errored) {
651
+ // throw new WritebBeenAbortedError()
652
+ // }
653
+ });
654
+ data.pipe(m) // 写入数据流
655
+ ;
656
+ } else m.end(data);
657
+ }
658
+ /**
930
659
  * Writes buffered data to the current native request
931
660
  * 如 request 不存在,则创建连接,pipe 时可写入 header
932
661
  * @override - 重写父类方法
@@ -934,514 +663,427 @@ class Request extends Duplex {
934
663
  * @param {BufferEncoding | ((error: Error | null) => void)} [encoding] - Encoding for string data, or the callback if no encoding is provided.
935
664
  * @param {(error: Error | null) => void} [cb] - Callback to signal the end of the write operation.
936
665
  * @returns {boolean} True if the write was successful, false otherwise.
937
- */
938
- write(chunk, encoding, cb) {
939
- const m = this;
940
- // log({data: chunk, encoding, cb}, 'write')
941
-
942
- // Writing is not allowed if end has been called
943
- if (m._ending) {
944
- // throw new WriteAfterEndError()
945
- m.emit('error', new WriteAfterEndError());
946
- return
947
- }
948
-
949
- // ! 数据写入时连接,pipe 时可设置 header
950
- if (!m._currentRequest) m.request();
951
-
952
- // Validate input and shift parameters if necessary
953
- if (!utils.isString(chunk) && !utils.isBuffer(chunk))
954
- throw new TypeError('data should be a string, Buffer or Uint8Array')
955
-
956
- if (utils.isFunction(encoding)) {
957
- // @ts-ignore
958
- cb = encoding;
959
- encoding = null;
960
- }
961
-
962
- // Ignore empty buffers, since writing them doesn't invoke the callback
963
- // https://github.com/nodejs/node/issues/22066
964
- if (chunk.length === 0) {
965
- if (cb) cb(null);
966
- return
967
- }
968
-
969
- // Only write when we don't exceed the maximum body length
970
- if (m._requestBodyLength + chunk.length <= m.opt.maxBodyLength) {
971
- m._requestBodyLength += chunk.length;
972
- m._requestBodyBuffers.push({data: chunk, encoding});
973
- // @ts-ignore
974
- m._currentRequest.write(chunk, encoding, cb);
975
- }
976
- // Error when we exceed the maximum body length
977
- else {
978
- m.emit('error', new MaxBodyLengthExceededError());
979
- m.abort();
980
- }
981
- }
982
-
983
- /**
666
+ */ write(chunk, encoding, cb) {
667
+ const m = this;
668
+ // log({data: chunk, encoding, cb}, 'write')
669
+ // Writing is not allowed if end has been called
670
+ if (m._ending) {
671
+ // throw new WriteAfterEndError()
672
+ m.emit('error', new WriteAfterEndError());
673
+ return;
674
+ }
675
+ // ! 数据写入时连接,pipe 时可设置 header
676
+ if (!m._currentRequest) m.request();
677
+ // Validate input and shift parameters if necessary
678
+ if (!utils.isString(chunk) && !utils.isBuffer(chunk)) throw new TypeError('data should be a string, Buffer or Uint8Array');
679
+ if (utils.isFunction(encoding)) {
680
+ // @ts-ignore
681
+ cb = encoding;
682
+ encoding = null;
683
+ }
684
+ // Ignore empty buffers, since writing them doesn't invoke the callback
685
+ // https://github.com/nodejs/node/issues/22066
686
+ if (chunk.length === 0) {
687
+ if (cb) cb(null);
688
+ return;
689
+ }
690
+ // Only write when we don't exceed the maximum body length
691
+ if (m._requestBodyLength + chunk.length <= m.opt.maxBodyLength) {
692
+ m._requestBodyLength += chunk.length;
693
+ m._requestBodyBuffers.push({
694
+ data: chunk,
695
+ encoding
696
+ });
697
+ // @ts-ignore
698
+ m._currentRequest.write(chunk, encoding, cb);
699
+ } else {
700
+ m.emit('error', new MaxBodyLengthExceededError());
701
+ m.abort();
702
+ }
703
+ }
704
+ /**
984
705
  * Ends the current native request
985
706
  * @override - 重写父类方法
986
707
  * @param {*} [chunk] - Optional data to write before ending the stream.
987
708
  * @param {BufferEncoding | (() => void)} [encoding] - Encoding for string data, or the callback if no encoding is provided.
988
709
  * @param {() => void} [cb] - Optional callback to signal completion.
989
710
  * @returns {this} The current stream instance, to allow chaining.
990
- */
991
- end(chunk, encoding, cb) {
992
- const m = this;
993
-
994
- // Shift parameters if necessary
995
- if (utils.isFunction(chunk)) {
996
- cb = chunk;
997
- chunk = null;
998
- encoding = null;
999
- } else if (utils.isFunction(encoding)) {
1000
- // @ts-ignore
1001
- cb = encoding;
1002
- encoding = null;
1003
- }
1004
-
1005
- // ! 创建实例时不连接,数据写入时发起连接,连接后无法设置 header,因此 pipe 时可设置 header
1006
- if (!m._currentRequest) m.request();
1007
-
1008
- // Write data if needed and end
1009
- if (!chunk) {
1010
- m._ended = true;
1011
- m._ending = true;
1012
- m._currentRequest.end(null, null, cb);
1013
- } else {
1014
- const currentRequest = m._currentRequest;
1015
- m.write(chunk, encoding, () => {
1016
- m._ended = true;
1017
- currentRequest.end(null, null, cb);
1018
- });
1019
-
1020
- m._ending = true;
1021
- }
1022
-
1023
- return m
1024
- }
1025
-
1026
- /**
711
+ */ end(chunk, encoding, cb) {
712
+ const m = this;
713
+ // Shift parameters if necessary
714
+ if (utils.isFunction(chunk)) {
715
+ cb = chunk;
716
+ chunk = null;
717
+ encoding = null;
718
+ } else if (utils.isFunction(encoding)) {
719
+ // @ts-ignore
720
+ cb = encoding;
721
+ encoding = null;
722
+ }
723
+ // ! 创建实例时不连接,数据写入时发起连接,连接后无法设置 header,因此 pipe 时可设置 header
724
+ if (!m._currentRequest) m.request();
725
+ // Write data if needed and end
726
+ if (!chunk) {
727
+ m._ended = true;
728
+ m._ending = true;
729
+ m._currentRequest.end(null, null, cb);
730
+ } else {
731
+ const currentRequest = m._currentRequest;
732
+ m.write(chunk, encoding, ()=>{
733
+ m._ended = true;
734
+ currentRequest.end(null, null, cb);
735
+ });
736
+ m._ending = true;
737
+ }
738
+ return m;
739
+ }
740
+ /**
1027
741
  *
1028
742
  * @param {string} name
1029
743
  * @returns
1030
- */
1031
- hasHeader(name) {
1032
- return Object.keys(this.opt.headers).includes(name)
1033
- }
1034
-
1035
- /**
744
+ */ hasHeader(name) {
745
+ return Object.keys(this.opt.headers).includes(name);
746
+ }
747
+ /**
1036
748
  *
1037
749
  * @param {string} name
1038
750
  * @returns {string}
1039
- */
1040
- getHeader(name) {
1041
- return this.opt.headers[name]
1042
- }
1043
-
1044
- /**
751
+ */ getHeader(name) {
752
+ return this.opt.headers[name];
753
+ }
754
+ /**
1045
755
  * Sets a header value on the current native request
1046
756
  * @param {string} name
1047
757
  * @param {string} value
1048
- */
1049
- setHeader(name, value) {
1050
- this.opt.headers[name] = value;
1051
- this._currentRequest?.setHeader(name, value);
1052
- }
1053
-
1054
- /**
758
+ */ setHeader(name, value) {
759
+ var _this__currentRequest;
760
+ this.opt.headers[name] = value;
761
+ (_this__currentRequest = this._currentRequest) == null ? void 0 : _this__currentRequest.setHeader(name, value);
762
+ }
763
+ /**
1055
764
  * Clears a header value on the current native request
1056
765
  * @param {string} name
1057
- */
1058
- removeHeader(name) {
1059
- delete this.opt.headers[name];
1060
- this._currentRequest?.removeHeader(name);
1061
- }
1062
-
1063
- /**
766
+ */ removeHeader(name) {
767
+ var _this__currentRequest;
768
+ delete this.opt.headers[name];
769
+ (_this__currentRequest = this._currentRequest) == null ? void 0 : _this__currentRequest.removeHeader(name);
770
+ }
771
+ /**
1064
772
  * 标头是否已发送
1065
773
  * @returns
1066
- */
1067
- get headersSent() {
1068
- return this._currentRequest?.headersSent
1069
- }
1070
-
1071
- /**
774
+ */ get headersSent() {
775
+ var _this__currentRequest;
776
+ return (_this__currentRequest = this._currentRequest) == null ? void 0 : _this__currentRequest.headersSent;
777
+ }
778
+ /**
1072
779
  * Global timeout for all underlying requests
1073
780
  * @param {*} msecs
1074
781
  * @param {*} callback
1075
782
  * @returns
1076
- */
1077
- setTimeout(msecs, callback) {
1078
- const m = this;
1079
-
1080
- /**
783
+ */ setTimeout(msecs, callback) {
784
+ const m = this;
785
+ /**
1081
786
  * Destroys the socket on timeout
1082
787
  * @param {*} socket
1083
- */
1084
- function destroyOnTimeout(socket) {
1085
- socket.setTimeout(msecs);
1086
- socket.removeListener('timeout', socket.destroy);
1087
- socket.addListener('timeout', socket.destroy);
1088
- }
1089
-
1090
- /**
788
+ */ function destroyOnTimeout(socket) {
789
+ socket.setTimeout(msecs);
790
+ socket.removeListener('timeout', socket.destroy);
791
+ socket.addListener('timeout', socket.destroy);
792
+ }
793
+ /**
1091
794
  * Sets up a timer to trigger a timeout event
1092
795
  * @param {*} socket
1093
- */
1094
- function startTimer(socket) {
1095
- if (m.startTimer) m.startTimer = null;
1096
-
1097
- if (m._timeout) clearTimeout(m._timeout);
1098
-
1099
- m._timeout = setTimeout(() => {
1100
- m.emit('timeout');
1101
- clearTimer();
1102
- }, msecs);
1103
-
1104
- destroyOnTimeout(socket);
1105
- }
1106
-
1107
- // Stops a timeout from triggering
1108
- function clearTimer() {
1109
- // Clear the timeout
1110
- if (m._timeout) {
1111
- clearTimeout(m._timeout);
1112
- m._timeout = null;
1113
- }
1114
-
1115
- // Clean up all attached listeners
1116
- m.removeListener('abort', clearTimer);
1117
- m.removeListener('error', clearTimer);
1118
- m.removeListener('response', clearTimer);
1119
- m.removeListener('close', clearTimer);
1120
-
1121
- if (callback) {
1122
- m.removeListener('timeout', callback);
1123
- }
1124
- if (!m.socket) {
1125
- m._currentRequest.removeListener('socket', startTimer);
1126
- }
1127
- }
1128
-
1129
- // Attach callback if passed
1130
- if (callback) m.on('timeout', callback);
1131
-
1132
- // Start the timer if or when the socket is opened
1133
- if (m.socket) startTimer(m.socket);
1134
- else m.startTimer = startTimer; // 未连接,先登记,连接后启动
1135
-
1136
- // Clean up on events
1137
- m.on('socket', destroyOnTimeout);
1138
- m.on('abort', clearTimer);
1139
- m.on('error', clearTimer);
1140
- m.on('response', clearTimer);
1141
- m.on('close', clearTimer);
1142
-
1143
- return m
1144
- }
1145
-
1146
- /**
796
+ */ function startTimer(socket) {
797
+ if (m.startTimer) m.startTimer = null;
798
+ if (m._timeout) clearTimeout(m._timeout);
799
+ m._timeout = setTimeout(()=>{
800
+ m.emit('timeout');
801
+ clearTimer();
802
+ }, msecs);
803
+ destroyOnTimeout(socket);
804
+ }
805
+ // Stops a timeout from triggering
806
+ function clearTimer() {
807
+ // Clear the timeout
808
+ if (m._timeout) {
809
+ clearTimeout(m._timeout);
810
+ m._timeout = null;
811
+ }
812
+ // Clean up all attached listeners
813
+ m.removeListener('abort', clearTimer);
814
+ m.removeListener('error', clearTimer);
815
+ m.removeListener('response', clearTimer);
816
+ m.removeListener('close', clearTimer);
817
+ if (callback) {
818
+ m.removeListener('timeout', callback);
819
+ }
820
+ if (!m.socket) {
821
+ m._currentRequest.removeListener('socket', startTimer);
822
+ }
823
+ }
824
+ // Attach callback if passed
825
+ if (callback) m.on('timeout', callback);
826
+ // Start the timer if or when the socket is opened
827
+ if (m.socket) startTimer(m.socket);
828
+ else m.startTimer = startTimer // 未连接,先登记,连接后启动
829
+ ;
830
+ // Clean up on events
831
+ m.on('socket', destroyOnTimeout);
832
+ m.on('abort', clearTimer);
833
+ m.on('error', clearTimer);
834
+ m.on('response', clearTimer);
835
+ m.on('close', clearTimer);
836
+ return m;
837
+ }
838
+ /**
1147
839
  *
1148
840
  * @param {*} options
1149
- */
1150
- sanitizeOptions(options) {
1151
- // Ensure headers are always present
1152
- if (!options.headers) options.headers = {};
1153
-
1154
- // Since http.request treats host as an alias of hostname,
1155
- // but the url module interprets host as hostname plus port,
1156
- // eliminate the host property to avoid confusion.
1157
- if (options.host) {
1158
- // Use hostname if set, because it has precedence
1159
- if (!options.hostname) {
1160
- options.hostname = options.host;
1161
- }
1162
- options.host = undefined;
1163
- }
1164
-
1165
- // Complete the URL object when necessary
1166
- if (!options.pathname && options.path) {
1167
- const searchPos = options.path.indexOf('?');
1168
- if (searchPos < 0) {
1169
- options.pathname = options.path;
1170
- } else {
1171
- options.pathname = options.path.substring(0, searchPos);
1172
- options.search = options.path.substring(searchPos);
1173
- }
1174
- }
1175
- }
1176
-
1177
- /**
841
+ */ sanitizeOptions(options) {
842
+ // Ensure headers are always present
843
+ if (!options.headers) options.headers = {};
844
+ // Since http.request treats host as an alias of hostname,
845
+ // but the url module interprets host as hostname plus port,
846
+ // eliminate the host property to avoid confusion.
847
+ if (options.host) {
848
+ // Use hostname if set, because it has precedence
849
+ if (!options.hostname) {
850
+ options.hostname = options.host;
851
+ }
852
+ options.host = undefined;
853
+ }
854
+ // Complete the URL object when necessary
855
+ if (!options.pathname && options.path) {
856
+ const searchPos = options.path.indexOf('?');
857
+ if (searchPos < 0) {
858
+ options.pathname = options.path;
859
+ } else {
860
+ options.pathname = options.path.substring(0, searchPos);
861
+ options.search = options.path.substring(searchPos);
862
+ }
863
+ }
864
+ }
865
+ /**
1178
866
  * Processes a response from the current native request
1179
867
  * @param {Response} response
1180
868
  * @returns
1181
- */
1182
- processResponse(response) {
1183
- const m = this;
1184
- const {opt} = m;
1185
-
1186
- // Store the redirected response
1187
- const {statusCode} = response;
1188
- if (opt.trackRedirects) {
1189
- m._redirects.push({
1190
- url: m._currentUrl,
1191
- headers: response.headers,
1192
- statusCode,
1193
- });
1194
- }
1195
-
1196
- // RFC7231§6.4: The 3xx (Redirection) class of status code indicates
1197
- // that further action needs to be taken by the user agent in order to
1198
- // fulfill the request. If a Location header field is provided,
1199
- // the user agent MAY automatically redirect its request to the URI
1200
- // referenced by the Location field value,
1201
- // even if the specific status code is not understood.
1202
-
1203
- // If the response is not a redirect; return it as-is
1204
- const {location} = response.headers;
1205
-
1206
- // log({statusCode, headers: response.headers}, 'processResponse')
1207
-
1208
- if (!location || opt.followRedirects === false || statusCode < 300 || statusCode >= 400) {
1209
- // 非重定向,返回给原始回调处理
1210
- response.responseUrl = m._currentUrl;
1211
- response.redirects = m._redirects;
1212
-
1213
- if (opt.stream) m.response = response;
1214
-
1215
- // Be a good stream and emit end when the response is finished.
1216
- // Hack to emit end on close because of a core bug that never fires end
1217
- response.on('close', () => {
1218
- if (!m._respended) {
1219
- response.emit('end');
1220
- }
1221
- });
1222
-
1223
- response.once('end', () => {
1224
- m._respended = true;
1225
- });
1226
-
1227
- const responseStream = m.processStream(response);
1228
- // NOTE: responseStartTime is deprecated in favor of .timings
1229
- response.responseStartTime = m.responseStartTime;
1230
-
1231
- // 触发原回调函数
1232
- m.resCallback?.(response, responseStream);
1233
-
1234
- // 类似 ClientRequest,触发 response 事件
1235
- m.emit('response', response, responseStream);
1236
-
1237
- // Clean up
1238
- m._requestBodyBuffers = [];
1239
- return // 退出,不继续处理
1240
- }
1241
-
1242
- // The response is a redirect, so abort the current request
1243
- destroyRequest(m._currentRequest);
1244
- // Discard the remainder of the response to avoid waiting for data
1245
- response.destroy();
1246
-
1247
- // RFC7231§6.4: A client SHOULD detect and intervene
1248
- // in cyclical redirections (i.e., "infinite" redirection loops).
1249
- if (++m._redirectCount > opt.maxRedirects) throw new TooManyRedirectsError()
1250
-
1251
- // Store the request headers if applicable
1252
- let requestHeaders;
1253
- const {beforeRedirect} = opt;
1254
- if (beforeRedirect) {
1255
- requestHeaders = {
1256
- // The Host header was set by nativeProtocol.request
1257
- // @ts-ignore
1258
- Host: response.req.getHeader('host'),
1259
- ...opt.headers,
1260
- };
1261
- }
1262
-
1263
- // RFC7231§6.4: Automatic redirection needs to done with
1264
- // care for methods not known to be safe, […]
1265
- // RFC7231§6.4.2–3: For historical reasons, a user agent MAY change
1266
- // the request method from POST to GET for the subsequent request.
1267
- const {method} = opt;
1268
- if (
1269
- ((statusCode === 301 || statusCode === 302) && opt.method === 'POST') ||
1270
- // RFC7231§6.4.4: The 303 (See Other) status code indicates that
1271
- // the server is redirecting the user agent to a different resource […]
1272
- // A user agent can perform a retrieval request targeting that URI
1273
- // (a GET or HEAD request if using HTTP) […]
1274
- (statusCode === 303 && !/^(?:GET|HEAD)$/.test(opt.method))
1275
- ) {
1276
- m.opt.method = 'GET';
1277
- // Drop a possible entity and headers related to it
1278
- m._requestBodyBuffers = [];
1279
- removeMatchingHeaders(/^content-/i, opt.headers);
1280
- }
1281
-
1282
- // Drop the Host header, as the redirect might lead to a different host
1283
- const currentHostHeader = removeMatchingHeaders(/^host$/i, opt.headers);
1284
-
1285
- // If the redirect is relative, carry over the host of the last request
1286
- const currentUrlParts = utils.parseUrl(m._currentUrl);
1287
- const currentHost = currentHostHeader || currentUrlParts.host;
1288
- const currentUrl = /^\w+:/.test(location)
1289
- ? m._currentUrl
1290
- : url.format(Object.assign(currentUrlParts, {host: currentHost}));
1291
-
1292
- // Create the redirected request
1293
- const redirectUrl = utils.resolveUrl(location, currentUrl);
1294
-
1295
- log$1({redirectUrl}, 'redirecting to');
1296
-
1297
- m._isRedirect = true;
1298
- // 覆盖原 url 解析部分,包括 protocol、hostname、port等
1299
- utils.spreadUrlObject(redirectUrl, m.opt);
1300
-
1301
- // Drop confidential headers when redirecting to a less secure protocol
1302
- // or to a different domain that is not a superdomain
1303
- if (
1304
- (redirectUrl.protocol !== currentUrlParts.protocol && redirectUrl.protocol !== 'https:') ||
1305
- (redirectUrl.host !== currentHost && !isSubdomain(redirectUrl.host, currentHost))
1306
- ) {
1307
- removeMatchingHeaders(/^(?:(?:proxy-)?authorization|cookie)$/i, this.opt.headers);
1308
- }
1309
-
1310
- // Evaluate the beforeRedirect callback
1311
- if (utils.isFunction(beforeRedirect)) {
1312
- const responseDetails = {
1313
- headers: response.headers,
1314
- statusCode,
1315
- };
1316
- const requestDetails = {
1317
- url: currentUrl,
1318
- method,
1319
- headers: requestHeaders,
1320
- };
1321
-
1322
- beforeRedirect(opt, responseDetails, requestDetails);
1323
- m.sanitizeOptions(opt);
1324
- }
1325
-
1326
- // Perform the redirected request
1327
- m.request(); // 重新执行请求
1328
- }
1329
-
1330
- /**
869
+ */ processResponse(response) {
870
+ const m = this;
871
+ const { opt } = m;
872
+ // Store the redirected response
873
+ const { statusCode } = response;
874
+ if (opt.trackRedirects) {
875
+ m._redirects.push({
876
+ url: m._currentUrl,
877
+ headers: response.headers,
878
+ statusCode
879
+ });
880
+ }
881
+ // RFC7231§6.4: The 3xx (Redirection) class of status code indicates
882
+ // that further action needs to be taken by the user agent in order to
883
+ // fulfill the request. If a Location header field is provided,
884
+ // the user agent MAY automatically redirect its request to the URI
885
+ // referenced by the Location field value,
886
+ // even if the specific status code is not understood.
887
+ // If the response is not a redirect; return it as-is
888
+ const { location } = response.headers;
889
+ // log({statusCode, headers: response.headers}, 'processResponse')
890
+ if (!location || opt.followRedirects === false || statusCode < 300 || statusCode >= 400) {
891
+ // 非重定向,返回给原始回调处理
892
+ response.responseUrl = m._currentUrl;
893
+ response.redirects = m._redirects;
894
+ if (opt.stream) m.response = response;
895
+ // Be a good stream and emit end when the response is finished.
896
+ // Hack to emit end on close because of a core bug that never fires end
897
+ response.on('close', ()=>{
898
+ if (!m._respended) {
899
+ response.emit('end');
900
+ }
901
+ });
902
+ response.once('end', ()=>{
903
+ m._respended = true;
904
+ });
905
+ const responseStream = m.processStream(response);
906
+ // NOTE: responseStartTime is deprecated in favor of .timings
907
+ response.responseStartTime = m.responseStartTime;
908
+ // 触发原回调函数
909
+ m.resCallback == null ? void 0 : m.resCallback.call(m, response, responseStream);
910
+ // 类似 ClientRequest,触发 response 事件
911
+ m.emit('response', response, responseStream);
912
+ // Clean up
913
+ m._requestBodyBuffers = [];
914
+ return; // 退出,不继续处理
915
+ }
916
+ // The response is a redirect, so abort the current request
917
+ destroyRequest(m._currentRequest);
918
+ // Discard the remainder of the response to avoid waiting for data
919
+ response.destroy();
920
+ // RFC7231§6.4: A client SHOULD detect and intervene
921
+ // in cyclical redirections (i.e., "infinite" redirection loops).
922
+ if (++m._redirectCount > opt.maxRedirects) throw new TooManyRedirectsError();
923
+ // Store the request headers if applicable
924
+ let requestHeaders;
925
+ const { beforeRedirect } = opt;
926
+ if (beforeRedirect) {
927
+ requestHeaders = {
928
+ // The Host header was set by nativeProtocol.request
929
+ // @ts-ignore
930
+ Host: response.req.getHeader('host'),
931
+ ...opt.headers
932
+ };
933
+ }
934
+ // RFC7231§6.4: Automatic redirection needs to done with
935
+ // care for methods not known to be safe, […]
936
+ // RFC7231§6.4.2–3: For historical reasons, a user agent MAY change
937
+ // the request method from POST to GET for the subsequent request.
938
+ const { method } = opt;
939
+ if ((statusCode === 301 || statusCode === 302) && opt.method === 'POST' || // RFC7231§6.4.4: The 303 (See Other) status code indicates that
940
+ // the server is redirecting the user agent to a different resource […]
941
+ // A user agent can perform a retrieval request targeting that URI
942
+ // (a GET or HEAD request if using HTTP) […]
943
+ statusCode === 303 && !/^(?:GET|HEAD)$/.test(opt.method)) {
944
+ m.opt.method = 'GET';
945
+ // Drop a possible entity and headers related to it
946
+ m._requestBodyBuffers = [];
947
+ removeMatchingHeaders(/^content-/i, opt.headers);
948
+ }
949
+ // Drop the Host header, as the redirect might lead to a different host
950
+ const currentHostHeader = removeMatchingHeaders(/^host$/i, opt.headers);
951
+ // If the redirect is relative, carry over the host of the last request
952
+ const currentUrlParts = utils.parseUrl(m._currentUrl);
953
+ const currentHost = currentHostHeader || currentUrlParts.host;
954
+ const currentUrl = /^\w+:/.test(location) ? m._currentUrl : url.format(Object.assign(currentUrlParts, {
955
+ host: currentHost
956
+ }));
957
+ // Create the redirected request
958
+ const redirectUrl = utils.resolveUrl(location, currentUrl);
959
+ log$1({
960
+ redirectUrl
961
+ }, 'redirecting to');
962
+ m._isRedirect = true;
963
+ // 覆盖原 url 解析部分,包括 protocol、hostname、port等
964
+ utils.spreadUrlObject(redirectUrl, m.opt);
965
+ // Drop confidential headers when redirecting to a less secure protocol
966
+ // or to a different domain that is not a superdomain
967
+ if (redirectUrl.protocol !== currentUrlParts.protocol && redirectUrl.protocol !== 'https:' || redirectUrl.host !== currentHost && !isSubdomain(redirectUrl.host, currentHost)) {
968
+ removeMatchingHeaders(/^(?:(?:proxy-)?authorization|cookie)$/i, this.opt.headers);
969
+ }
970
+ // Evaluate the beforeRedirect callback
971
+ if (utils.isFunction(beforeRedirect)) {
972
+ const responseDetails = {
973
+ headers: response.headers,
974
+ statusCode
975
+ };
976
+ const requestDetails = {
977
+ url: currentUrl,
978
+ method,
979
+ headers: requestHeaders
980
+ };
981
+ beforeRedirect(opt, responseDetails, requestDetails);
982
+ m.sanitizeOptions(opt);
983
+ }
984
+ // Perform the redirected request
985
+ m.request() // 重新执行请求
986
+ ;
987
+ }
988
+ /**
1331
989
  * 处理响应stream
1332
990
  * 自动解压,透传流,需设置 decompress = false,避免解压数据
1333
991
  * @param {Response} res
1334
992
  * @returns {Response | stream.Readable}
1335
- */
1336
- processStream(res) {
1337
- const m = this;
1338
- const {opt} = m;
1339
-
1340
- const streams = [res];
1341
- let responseStream = res;
1342
- // 'transfer-encoding': 'chunked'时,无content-length,axios v1.2 不能自动解压
1343
- const responseLength = +res.headers['content-length'];
1344
-
1345
- // log('processStream', {
1346
- // statusCode: res.statusCode,
1347
- // responseLength,
1348
- // headers: res.headers,
1349
- // })
1350
-
1351
- if (opt.transformStream) {
1352
- opt.transformStream.responseLength = responseLength;
1353
- streams.push(opt.transformStream);
1354
- }
1355
-
1356
- const empty = utils.noBody(opt.method, res.statusCode);
1357
- // decompress the response body transparently if required
1358
- if (opt.decompress !== false && res.headers['content-encoding']) {
1359
- // if decompress disabled we should not decompress
1360
- // 压缩内容,加入 解压 stream,自动解压,axios v1.2 存在bug,不能自动解压
1361
- // if no content, but headers still say that it is encoded,
1362
- // remove the header not confuse downstream operations
1363
- // if ((!responseLength || res.statusCode === 204) && res.headers['content-encoding']) {
1364
- if (empty && res.headers['content-encoding']) res.headers['content-encoding'] = undefined;
1365
-
1366
- // 'content-encoding': 'gzip',
1367
- switch ((res.headers['content-encoding'] || '').toLowerCase()) {
1368
- /*eslint default-case:0*/
1369
- case 'gzip':
1370
- case 'x-gzip':
1371
- case 'compress':
1372
- case 'x-compress':
1373
- // add the unzipper to the body stream processing pipeline
1374
- // @ts-ignore
1375
- streams.push(zlib.createUnzip(zlibOptions));
1376
-
1377
- // remove the content-encoding in order to not confuse downstream operations
1378
- res.headers['content-encoding'] = undefined;
1379
- break
1380
-
1381
- case 'deflate':
1382
- // @ts-ignore
1383
- streams.push(new ZlibTransform());
1384
-
1385
- // add the unzipper to the body stream processing pipeline
1386
- // @ts-ignore
1387
- streams.push(zlib.createUnzip(zlibOptions));
1388
-
1389
- // remove the content-encoding in order to not confuse downstream operations
1390
- res.headers['content-encoding'] = undefined;
1391
- break
1392
-
1393
- case 'br':
1394
- if (isBrotliSupported) {
1395
- // @ts-ignore
1396
- streams.push(zlib.createBrotliDecompress(brotliOptions));
1397
- res.headers['content-encoding'] = undefined;
1398
- }
1399
- break
1400
- }
1401
- }
1402
-
1403
- // 响应流,用于读
1404
- // @ts-ignore
1405
- responseStream = streams.length > 1 ? stream.pipeline(streams, utils.noop) : streams[0];
1406
- // 将内部 responseStream 可读流 映射到 redirectReq
1407
-
1408
- // @ts-ignore
1409
- m.responseStream = responseStream;
1410
- // @ts-ignore
1411
- responseStream.redirectReq = m; // 事情触发时引用
1412
-
1413
- // stream 模式,事件透传到 请求类
1414
- if (opt.stream) {
1415
- if (m._paused) responseStream.pause();
1416
- // 写入目的流
1417
- for (const dest of m.pipedests) m.pipeDest(dest);
1418
-
1419
- // 接收responseStream事件,转发 redirectReq 发射
1420
- for (const ev of readEvents) responseStream.on(ev, readEventEmit[ev]);
1421
-
1422
- // @ts-ignore
1423
- responseStream.on('data', chunk => {
1424
- if (m.timing && !m.responseStarted) {
1425
- m.responseStartTime = new Date().getTime();
1426
- }
1427
- m._destdata = true;
1428
- m.emit('data', chunk); // 向上触发
1429
- });
1430
- }
1431
-
1432
- // 可读流结束,触发 finished,方便上层清理
1433
- // A cleanup function which removes all registered listeners.
1434
- const offListeners = stream.finished(responseStream, () => {
1435
- offListeners(); // cleanup
1436
- this.emit('finished');
1437
- });
1438
-
1439
- return responseStream
1440
- }
1441
-
1442
- // Read Stream API
1443
-
1444
- /**
993
+ */ processStream(res) {
994
+ const m = this;
995
+ const { opt } = m;
996
+ const streams = [
997
+ res
998
+ ];
999
+ let responseStream = res;
1000
+ // 'transfer-encoding': 'chunked'时,无content-length,axios v1.2 不能自动解压
1001
+ const responseLength = +res.headers['content-length'];
1002
+ // log('processStream', {
1003
+ // statusCode: res.statusCode,
1004
+ // responseLength,
1005
+ // headers: res.headers,
1006
+ // })
1007
+ if (opt.transformStream) {
1008
+ opt.transformStream.responseLength = responseLength;
1009
+ streams.push(opt.transformStream);
1010
+ }
1011
+ const empty = utils.noBody(opt.method, res.statusCode);
1012
+ // decompress the response body transparently if required
1013
+ if (opt.decompress !== false && res.headers['content-encoding']) {
1014
+ // if decompress disabled we should not decompress
1015
+ // 压缩内容,加入 解压 stream,自动解压,axios v1.2 存在bug,不能自动解压
1016
+ // if no content, but headers still say that it is encoded,
1017
+ // remove the header not confuse downstream operations
1018
+ // if ((!responseLength || res.statusCode === 204) && res.headers['content-encoding']) {
1019
+ if (empty && res.headers['content-encoding']) res.headers['content-encoding'] = undefined;
1020
+ // 'content-encoding': 'gzip',
1021
+ switch((res.headers['content-encoding'] || '').toLowerCase()){
1022
+ /*eslint default-case:0*/ case 'gzip':
1023
+ case 'x-gzip':
1024
+ case 'compress':
1025
+ case 'x-compress':
1026
+ // add the unzipper to the body stream processing pipeline
1027
+ // @ts-ignore
1028
+ streams.push(zlib.createUnzip(zlibOptions));
1029
+ // remove the content-encoding in order to not confuse downstream operations
1030
+ res.headers['content-encoding'] = undefined;
1031
+ break;
1032
+ case 'deflate':
1033
+ // @ts-ignore
1034
+ streams.push(new ZlibTransform());
1035
+ // add the unzipper to the body stream processing pipeline
1036
+ // @ts-ignore
1037
+ streams.push(zlib.createUnzip(zlibOptions));
1038
+ // remove the content-encoding in order to not confuse downstream operations
1039
+ res.headers['content-encoding'] = undefined;
1040
+ break;
1041
+ case 'br':
1042
+ if (isBrotliSupported) {
1043
+ // @ts-ignore
1044
+ streams.push(zlib.createBrotliDecompress(brotliOptions));
1045
+ res.headers['content-encoding'] = undefined;
1046
+ }
1047
+ break;
1048
+ }
1049
+ }
1050
+ // 响应流,用于读
1051
+ // @ts-ignore
1052
+ responseStream = streams.length > 1 ? stream$1.pipeline(streams, utils.noop) : streams[0];
1053
+ // 将内部 responseStream 可读流 映射到 redirectReq
1054
+ // @ts-ignore
1055
+ m.responseStream = responseStream;
1056
+ // @ts-ignore
1057
+ responseStream.redirectReq = m // 事情触发时引用
1058
+ ;
1059
+ // stream 模式,事件透传到 请求类
1060
+ if (opt.stream) {
1061
+ if (m._paused) responseStream.pause();
1062
+ // 写入目的流
1063
+ for (const dest of m.pipedests)m.pipeDest(dest);
1064
+ // 接收responseStream事件,转发 redirectReq 发射
1065
+ for (const ev of readEvents)responseStream.on(ev, readEventEmit[ev]);
1066
+ // @ts-ignore
1067
+ responseStream.on('data', (chunk)=>{
1068
+ if (m.timing && !m.responseStarted) {
1069
+ m.responseStartTime = new Date().getTime();
1070
+ }
1071
+ m._destdata = true;
1072
+ m.emit('data', chunk) // 向上触发
1073
+ ;
1074
+ });
1075
+ }
1076
+ // 可读流结束,触发 finished,方便上层清理
1077
+ // A cleanup function which removes all registered listeners.
1078
+ const offListeners = stream$1.finished(responseStream, ()=>{
1079
+ offListeners() // cleanup
1080
+ ;
1081
+ this.emit('finished');
1082
+ });
1083
+ return responseStream;
1084
+ }
1085
+ // Read Stream API
1086
+ /**
1445
1087
  * 建立读取流管道
1446
1088
  * read stream to write stream
1447
1089
  * pipe 只是建立连接管道,后续自动传输数据
@@ -1451,192 +1093,248 @@ class Request extends Duplex {
1451
1093
  * @param {Object} [opt] - Optional configuration object.
1452
1094
  * @param {boolean} [opt.end=true] - Whether to end the writable stream when the readable stream ends.
1453
1095
  * @returns {T} The destination stream.
1454
- */
1455
- pipe(dest, opts = {}) {
1456
- const m = this;
1457
- // m.pipe()
1458
- // 请求已响应
1459
- if (m.responseStream) {
1460
- // 已有数据,不可pipe
1461
- if (m._destdata)
1462
- m.emit('error', new Error('You cannot pipe after data has been emitted from the response.'));
1463
- else if (m._respended)
1464
- m.emit('error', new Error('You cannot pipe after the response has been ended.'));
1465
- else {
1466
- // stream.Stream.prototype.pipe.call(self, dest, opts);
1467
- super.pipe(dest, opts); // 建立连接管道,自动传输数据
1468
- m.pipeDest(dest);
1469
- return dest // 返回写入 stream
1470
- }
1471
- } else {
1472
- // 已请求还未响应
1473
- m.pipedests.push(dest);
1474
- // stream.Stream.prototype.pipe.call(self, dest, opts);
1475
- super.pipe(dest, opts); // 建立连接管道
1476
- return dest // 返回写入 stream
1477
- }
1478
- }
1479
-
1480
- /**
1096
+ */ pipe(dest, opts = {}) {
1097
+ const m = this;
1098
+ // m.pipe()
1099
+ // 请求已响应
1100
+ if (m.responseStream) {
1101
+ // 已有数据,不可pipe
1102
+ if (m._destdata) m.emit('error', new Error('You cannot pipe after data has been emitted from the response.'));
1103
+ else if (m._respended) m.emit('error', new Error('You cannot pipe after the response has been ended.'));
1104
+ else {
1105
+ // stream.Stream.prototype.pipe.call(self, dest, opts);
1106
+ super.pipe(dest, opts) // 建立连接管道,自动传输数据
1107
+ ;
1108
+ m.pipeDest(dest);
1109
+ return dest // 返回写入 stream
1110
+ ;
1111
+ }
1112
+ } else {
1113
+ // 已请求还未响应
1114
+ m.pipedests.push(dest);
1115
+ // stream.Stream.prototype.pipe.call(self, dest, opts);
1116
+ super.pipe(dest, opts) // 建立连接管道
1117
+ ;
1118
+ return dest // 返回写入 stream
1119
+ ;
1120
+ }
1121
+ }
1122
+ /**
1481
1123
  * 分离先前使用pipe()方法附加的Writable流。
1482
1124
  * @param {stream.Writable} dest
1483
1125
  * @returns
1484
- */
1485
- unpipe(dest) {
1486
- const m = this;
1487
-
1488
- // 请求已响应
1489
- if (m.responseStream) {
1490
- // 已有数据,不可 unpipe
1491
- if (m._destdata)
1492
- m.emit(
1493
- 'error',
1494
- new Error('You cannot unpipe after data has been emitted from the response.')
1495
- );
1496
- else if (m._respended)
1497
- m.emit('error', new Error('You cannot unpipe after the response has been ended.'));
1498
- else {
1499
- // stream.Stream.prototype.pipe.call(self, dest, opts);
1500
- super.unpipe(dest); // 建立连接管道,自动传输数据
1501
- m.pipedests = m.pipedests.filter(v => v !== dest);
1502
- return m
1503
- }
1504
- } else {
1505
- // 已请求还未响应
1506
- m.pipedests = m.pipedests.filter(v => v !== dest);
1507
- super.unpipe(dest); // 从连接管道中分离
1508
- return m
1509
- }
1510
- }
1511
-
1512
- /**
1126
+ */ unpipe(dest) {
1127
+ const m = this;
1128
+ // 请求已响应
1129
+ if (m.responseStream) {
1130
+ // 已有数据,不可 unpipe
1131
+ if (m._destdata) m.emit('error', new Error('You cannot unpipe after data has been emitted from the response.'));
1132
+ else if (m._respended) m.emit('error', new Error('You cannot unpipe after the response has been ended.'));
1133
+ else {
1134
+ // stream.Stream.prototype.pipe.call(self, dest, opts);
1135
+ super.unpipe(dest) // 建立连接管道,自动传输数据
1136
+ ;
1137
+ m.pipedests = m.pipedests.filter((v)=>v !== dest);
1138
+ return m;
1139
+ }
1140
+ } else {
1141
+ // 已请求还未响应
1142
+ m.pipedests = m.pipedests.filter((v)=>v !== dest);
1143
+ super.unpipe(dest) // 从连接管道中分离
1144
+ ;
1145
+ return m;
1146
+ }
1147
+ }
1148
+ /**
1513
1149
  * 收请求响应,传输数据到可写流之前,设置可写流 header
1514
1150
  * content-type 和 content-length,实现数据 透传,比如图片
1515
1151
  * 流模式透传,需设置 decompress = false,避免解压数据
1516
1152
  * (await req.stream('http://google.com/img.png')).pipe(await req.stream('http://mysite.com/img.png'))
1517
1153
  * pipe to dest
1518
1154
  * @param {*} dest
1519
- */
1520
- pipeDest(dest) {
1521
- const m = this;
1522
- const {response} = m;
1523
-
1524
- // Called after the response is received
1525
- if (response?.headers && dest.headers && !dest.headersSent) {
1526
- const caseless = new Caseless(response.headers);
1527
- if (caseless.has('content-type')) {
1528
- const ctname = /** @type {string} */ (caseless.has('content-type'));
1529
- if (dest.setHeader) {
1530
- dest.setHeader(ctname, response.headers[ctname]);
1531
- } else {
1532
- dest.headers[ctname] = response.headers[ctname];
1533
- }
1534
- }
1535
-
1536
- if (caseless.has('content-length')) {
1537
- const clname = /** @type {string} */ (caseless.has('content-length'));
1538
- if (dest.setHeader) {
1539
- dest.setHeader(clname, response.headers[clname]);
1540
- } else {
1541
- dest.headers[clname] = response.headers[clname];
1542
- }
1543
- }
1544
- }
1545
-
1546
- if (response?.headers && dest.setHeader && !dest.headersSent) {
1547
- for (const k of Object.keys(response.headers)) dest.setHeader(k, response.headers[k]);
1548
-
1549
- dest.statusCode = response.statusCode;
1550
- }
1551
-
1552
- if (m.pipefilter) m.pipefilter(response, dest);
1553
- }
1554
-
1555
- /**
1155
+ */ pipeDest(dest) {
1156
+ const m = this;
1157
+ const { response } = m;
1158
+ // Called after the response is received
1159
+ if ((response == null ? void 0 : response.headers) && dest.headers && !dest.headersSent) {
1160
+ const caseless = new Caseless(response.headers);
1161
+ if (caseless.has('content-type')) {
1162
+ const ctname = /** @type {string} */ caseless.has('content-type');
1163
+ if (dest.setHeader) {
1164
+ dest.setHeader(ctname, response.headers[ctname]);
1165
+ } else {
1166
+ dest.headers[ctname] = response.headers[ctname];
1167
+ }
1168
+ }
1169
+ if (caseless.has('content-length')) {
1170
+ const clname = /** @type {string} */ caseless.has('content-length');
1171
+ if (dest.setHeader) {
1172
+ dest.setHeader(clname, response.headers[clname]);
1173
+ } else {
1174
+ dest.headers[clname] = response.headers[clname];
1175
+ }
1176
+ }
1177
+ }
1178
+ if ((response == null ? void 0 : response.headers) && dest.setHeader && !dest.headersSent) {
1179
+ for (const k of Object.keys(response.headers))dest.setHeader(k, response.headers[k]);
1180
+ dest.statusCode = response.statusCode;
1181
+ }
1182
+ if (m.pipefilter) m.pipefilter(response, dest);
1183
+ }
1184
+ /**
1556
1185
  * 暂停read流
1557
- */
1558
- pause() {
1559
- const m = this;
1560
- // 没有流
1561
- if (!m.responseStream) m._paused = true;
1562
- else m.responseStream.pause();
1563
- return m
1564
- }
1565
-
1566
- /**
1186
+ */ pause() {
1187
+ const m = this;
1188
+ // 没有流
1189
+ if (!m.responseStream) m._paused = true;
1190
+ else m.responseStream.pause();
1191
+ return m;
1192
+ }
1193
+ /**
1567
1194
  * 继续read响应流
1568
- */
1569
- resume() {
1570
- const m = this;
1571
- if (!m.responseStream) m._paused = false;
1572
- else m.responseStream.resume();
1573
- return m
1574
- }
1575
-
1576
- isPaused() {
1577
- return this._paused
1578
- }
1579
- }
1580
-
1195
+ */ resume() {
1196
+ const m = this;
1197
+ if (!m.responseStream) m._paused = false;
1198
+ else m.responseStream.resume();
1199
+ return m;
1200
+ }
1201
+ isPaused() {
1202
+ return this._paused;
1203
+ }
1204
+ /**
1205
+ * responseCallback 原消息处理回调
1206
+ * @param {Opts} opts
1207
+ * @param {*} resCallback
1208
+ */ constructor(opts, resCallback){
1209
+ 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
1210
+ , /** @type {stream.Writable[]} */ this.pipedests = [] // pipe dest
1211
+ , /** @type {*} */ this.startTimer = null;
1212
+ const m = this;
1213
+ // log({opts}, 'new Request')
1214
+ // Initialize the request
1215
+ m.sanitizeOptions(opts);
1216
+ m.opt = opts;
1217
+ m.headers = opts.headers;
1218
+ // log({opts}, 'constructor')
1219
+ m._ended = false;
1220
+ m._ending = false;
1221
+ m._redirectCount = 0;
1222
+ /** @type {any[]} */ m._redirects = [];
1223
+ m._requestBodyLength = 0;
1224
+ /** @type {any[]} */ m._requestBodyBuffers = [];
1225
+ // save the callback if passed
1226
+ m.resCallback = resCallback;
1227
+ /**
1228
+ * React to responses of native requests
1229
+ * 接管 response 事件,非重定向,触发 response 事件
1230
+ * @param {Response} res
1231
+ */ m._onResponse = (res)=>{
1232
+ try {
1233
+ m.processResponse(res);
1234
+ } catch (cause) {
1235
+ m.emit('error', cause instanceof RedirectionError ? cause : new RedirectionError({
1236
+ cause: cause
1237
+ }));
1238
+ }
1239
+ };
1240
+ // Proxy all other public ClientRequest methods 'getHeader'
1241
+ for (const method of writeMethods){
1242
+ // @ts-ignore
1243
+ m[method] = (a, b)=>{
1244
+ var // log(method, {a, b})
1245
+ // @ts-ignore
1246
+ _m__currentRequest;
1247
+ (_m__currentRequest = m._currentRequest) == null ? void 0 : _m__currentRequest[method](a, b);
1248
+ };
1249
+ }
1250
+ // Proxy all public ClientRequest properties
1251
+ // 'aborted', 'connection' 弃用
1252
+ for (const property of writeProps){
1253
+ Object.defineProperty(m, property, {
1254
+ get () {
1255
+ var _m__currentRequest;
1256
+ // @ts-ignore
1257
+ const val = (_m__currentRequest = m._currentRequest) == null ? void 0 : _m__currentRequest[property];
1258
+ // log('get property', {property})
1259
+ return val;
1260
+ }
1261
+ });
1262
+ }
1263
+ // 流模式
1264
+ if (opts.stream) {
1265
+ // 被 pipe 作为目标时触发,拷贝 src headers
1266
+ m.on('pipe', /** @param {stream.Readable & {headers?: Object.<string, string>}} src */ (src)=>{
1267
+ // m.ntick &&
1268
+ if (m._currentRequest) {
1269
+ m.emit('error', new Error('You cannot pipe to this stream after the outbound request has started.'));
1270
+ }
1271
+ m.pipesrc = src;
1272
+ if (utils.isReadStream(src)) {
1273
+ // @ts-ignore
1274
+ if (!m.hasHeader('content-type')) m.setHeader('content-type', mime.lookup(src.path));
1275
+ } else {
1276
+ // 拷贝请求头
1277
+ if (src.headers) {
1278
+ for (const k of Object.keys(src.headers)){
1279
+ if (!m.hasHeader(k)) {
1280
+ m.setHeader(k, src.headers[k]);
1281
+ }
1282
+ }
1283
+ }
1284
+ // @ts-ignore
1285
+ if (src.opt.method && !m.opt.method) m.opt.method = src.opt.method;
1286
+ }
1287
+ });
1288
+ }
1289
+ // Perform the first request
1290
+ // m.request(); // 创建时不连接,写入数据时连接,否则 pipe 时无法写入header
1291
+ }
1292
+ };
1581
1293
  /**
1582
1294
  * 释放请求,触发error事件
1583
1295
  * 'error' event, and emit a 'close' event.
1584
1296
  * Calling this will cause remaining data in the response to be dropped and the socket to be destroyed.
1585
1297
  * @param {*} request
1586
1298
  * @param {*} error
1587
- */
1588
- function destroyRequest(request, error) {
1589
- for (const ev of writeEvents) {
1590
- request.removeListener(ev, writeEventEmit[ev]);
1591
- }
1592
- request.on('error', utils.noop);
1593
- request.destroy(error); // 触发 error 事件
1594
- }
1595
-
1299
+ */ function destroyRequest(request, error) {
1300
+ for (const ev of writeEvents){
1301
+ request.removeListener(ev, writeEventEmit[ev]);
1302
+ }
1303
+ request.on('error', utils.noop);
1304
+ request.destroy(error) // 触发 error 事件
1305
+ ;
1306
+ }
1596
1307
  /**
1597
1308
  *
1598
1309
  * @param {RegExp} regex
1599
1310
  * @param {Object.<string, string>} headers
1600
1311
  * @returns
1601
- */
1602
- function removeMatchingHeaders(regex, headers) {
1603
- let lastValue;
1604
- for (const k of Object.keys(headers)) {
1605
- if (regex.test(k)) {
1606
- lastValue = headers[k];
1607
- delete headers[k];
1608
- }
1609
- }
1610
-
1611
- return lastValue === null || typeof lastValue === 'undefined'
1612
- ? undefined
1613
- : String(lastValue).trim()
1614
- }
1615
-
1312
+ */ function removeMatchingHeaders(regex, headers) {
1313
+ let lastValue;
1314
+ for (const k of Object.keys(headers)){
1315
+ if (regex.test(k)) {
1316
+ lastValue = headers[k];
1317
+ delete headers[k];
1318
+ }
1319
+ }
1320
+ return lastValue === null || typeof lastValue === 'undefined' ? undefined : String(lastValue).trim();
1321
+ }
1616
1322
  /**
1617
1323
  *
1618
1324
  * @param {string} subdomain
1619
1325
  * @param {string} domain
1620
1326
  * @returns
1621
- */
1622
- function isSubdomain(subdomain, domain) {
1623
- assert(utils.isString(subdomain) && utils.isString(domain));
1624
- const dot = subdomain.length - domain.length - 1;
1625
- return dot > 0 && subdomain[dot] === '.' && subdomain.endsWith(domain)
1327
+ */ function isSubdomain(subdomain, domain) {
1328
+ assert(utils.isString(subdomain) && utils.isString(domain));
1329
+ const dot = subdomain.length - domain.length - 1;
1330
+ return dot > 0 && subdomain[dot] === '.' && subdomain.endsWith(domain);
1626
1331
  }
1627
1332
 
1628
- /**
1629
- * from 'https://github.com/follow-redirects/follow-redirects'
1630
- * used by axios
1631
- * 修改以支持http、https 代理服务器
1632
- * 代理模式下,http or https 请求,取决于 proxy 代理服务器,而不是目的服务器。
1633
- */
1634
-
1635
- const log = log$2({env: `wia:req:${name(import.meta.url)}`}); // __filename
1636
-
1637
- /** @typedef { import('./request').Response} Response */
1638
-
1639
- /**
1333
+ const log = log$2({
1334
+ env: `wia:req:${name(import.meta.url)}`
1335
+ }) // __filename
1336
+ ;
1337
+ /** @typedef { import('./request').Response} Response */ /**
1640
1338
  * @typedef {object} Opts
1641
1339
  * @prop {Object.<string,string>} [headers]
1642
1340
  * @prop {string} [url]
@@ -1658,104 +1356,89 @@ const log = log$2({env: `wia:req:${name(import.meta.url)}`}); // __filename
1658
1356
  * @prop {number} [maxRedirects=21] - 最大重定向次数
1659
1357
  * @prop {number} [maxBodyLength = 0] - body限制,缺省不限
1660
1358
  * @prop {*} [trackRedirects]
1661
- */
1662
-
1663
- /** @typedef {(res: Response, stream?: stream.Readable) => void} Cb*/
1664
-
1665
- utils.createErrorType(
1666
- 'ERR_STREAM_WRITE_BEEN_ABORTED',
1667
- 'Request stream has been aborted'
1668
- )
1669
-
1670
- // Preventive platform detection
1671
- // istanbul ignore
1672
- ;(function detectUnsupportedEnvironment() {
1673
- const looksLikeNode = typeof process !== 'undefined';
1674
- const looksLikeBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
1675
- const looksLikeV8 = utils.isFunction(Error.captureStackTrace);
1676
- if (!looksLikeNode && (looksLikeBrowser || !looksLikeV8)) {
1677
- log.warn('The follow-redirects package should be excluded from browser builds.');
1678
- }
1679
- })();
1680
-
1359
+ */ /** @typedef {(res: Response, stream?: stream.Readable) => void} Cb*/ utils.createErrorType('ERR_STREAM_WRITE_BEEN_ABORTED', 'Request stream has been aborted');
1360
+ (function detectUnsupportedEnvironment() {
1361
+ const looksLikeNode = typeof process !== 'undefined';
1362
+ const looksLikeBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
1363
+ const looksLikeV8 = utils.isFunction(Error.captureStackTrace);
1364
+ if (!looksLikeNode && (looksLikeBrowser || !looksLikeV8)) {
1365
+ log.warn('The follow-redirects package should be excluded from browser builds.');
1366
+ }
1367
+ })();
1681
1368
  /**
1682
1369
  * 封装http(s),实现重定向
1683
1370
  * 重定向可能切换http、https
1684
1371
  * 支持隧道及非隧道、http(s)代理
1685
- */
1686
-
1687
- /**
1372
+ */ /**
1688
1373
  * 初始化参数
1689
1374
  * @param {string | Opts} uri/opts
1690
1375
  * @param {Opts | Cb} [opts] /cb
1691
1376
  * @param {Cb} [cb]
1692
1377
  * @returns {{opt: Opts, cb: Cb}}
1693
- */
1694
- function init(uri, opts, cb) {
1695
- let R;
1696
- try {
1697
- // Parse parameters, ensuring that input is an object
1698
- if (utils.isURL(uri)) uri = utils.spreadUrlObject(uri);
1699
- else if (utils.isString(uri)) uri = utils.spreadUrlObject(utils.parseUrl(uri));
1700
- else {
1701
- // @ts-ignore
1702
- cb = opts;
1703
- // @ts-ignore
1704
- opts = uri;
1705
- // @ts-ignore
1706
- const {url} = opts;
1707
- // url,解析
1708
- if (url) {
1709
- // @ts-ignore
1710
- // biome-ignore lint/performance/noDelete: <explanation>
1711
- delete opts.url;
1712
- if (utils.isURL(url)) uri = utils.spreadUrlObject(url);
1713
- else if (utils.isString(url)) uri = utils.spreadUrlObject(utils.parseUrl(url));
1714
- } else {
1715
- // @ts-ignore
1716
- opts = uri; // 不判断 utils.validateUrl(uri)
1717
- uri = {};
1718
- }
1719
- }
1720
-
1721
- if (utils.isFunction(opts)) {
1722
- // @ts-ignore
1723
- cb = opts;
1724
- opts = {};
1725
- }
1726
-
1727
- // copy options
1728
- /** @type {Opts} */
1729
- const opt = {
1730
- // @ts-ignore
1731
- ...uri,
1732
- ...opts,
1733
- };
1734
-
1735
- if (!utils.isString(opt.host) && !utils.isString(opt.hostname)) opt.hostname = '::1';
1736
- opt.method = (opt.method ?? 'get').toUpperCase();
1737
-
1738
- // follow-redirects does not skip comparison, so it should always succeed for axios -1 unlimited
1739
- opt.maxBodyLength = opt.maxBodyLength ?? Number.POSITIVE_INFINITY;
1740
- opt.maxRedirects = opt.maxRedirects ?? 21;
1741
- if (opt.maxRedirects === 0) opt.followRedirects = false;
1742
- opt.headers = opt.headers ?? {
1743
- Accept: 'application/json, text/plain, */*',
1744
- 'User-Agent':
1745
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.35',
1746
- 'Accept-Encoding': 'gzip, compress, deflate, br',
1747
- };
1748
-
1749
- R = {opt, cb};
1750
- // log({R}, 'init')
1751
- } catch (e) {
1752
- log.err(e, 'init');
1753
- }
1754
-
1755
- // @ts-ignore
1756
- return R
1757
- }
1758
-
1378
+ */ function init(uri, opts, cb) {
1379
+ let R;
1380
+ try {
1381
+ // Parse parameters, ensuring that input is an object
1382
+ if (utils.isURL(uri)) uri = utils.spreadUrlObject(uri);
1383
+ else if (utils.isString(uri)) uri = utils.spreadUrlObject(utils.parseUrl(uri));
1384
+ else {
1385
+ // @ts-ignore
1386
+ cb = opts;
1387
+ // @ts-ignore
1388
+ opts = uri;
1389
+ // @ts-ignore
1390
+ const { url } = opts;
1391
+ // url,解析
1392
+ if (url) {
1393
+ // @ts-ignore
1394
+ // biome-ignore lint/performance/noDelete: <explanation>
1395
+ delete opts.url;
1396
+ if (utils.isURL(url)) uri = utils.spreadUrlObject(url);
1397
+ else if (utils.isString(url)) uri = utils.spreadUrlObject(utils.parseUrl(url));
1398
+ } else {
1399
+ // @ts-ignore
1400
+ opts = uri // 不判断 utils.validateUrl(uri)
1401
+ ;
1402
+ uri = {};
1403
+ }
1404
+ }
1405
+ if (utils.isFunction(opts)) {
1406
+ // @ts-ignore
1407
+ cb = opts;
1408
+ opts = {};
1409
+ }
1410
+ // copy options
1411
+ /** @type {Opts} */ const opt = {
1412
+ // @ts-ignore
1413
+ ...uri,
1414
+ ...opts
1415
+ };
1416
+ if (!utils.isString(opt.host) && !utils.isString(opt.hostname)) opt.hostname = '::1';
1417
+ var _opt_method;
1418
+ opt.method = ((_opt_method = opt.method) != null ? _opt_method : 'get').toUpperCase();
1419
+ var _opt_maxBodyLength;
1420
+ // follow-redirects does not skip comparison, so it should always succeed for axios -1 unlimited
1421
+ opt.maxBodyLength = (_opt_maxBodyLength = opt.maxBodyLength) != null ? _opt_maxBodyLength : Number.POSITIVE_INFINITY;
1422
+ var _opt_maxRedirects;
1423
+ opt.maxRedirects = (_opt_maxRedirects = opt.maxRedirects) != null ? _opt_maxRedirects : 21;
1424
+ if (opt.maxRedirects === 0) opt.followRedirects = false;
1425
+ var _opt_headers;
1426
+ opt.headers = (_opt_headers = opt.headers) != null ? _opt_headers : {
1427
+ Accept: 'application/json, text/plain, */*',
1428
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.35',
1429
+ 'Accept-Encoding': 'gzip, compress, deflate, br'
1430
+ };
1431
+ R = {
1432
+ opt,
1433
+ cb
1434
+ };
1435
+ // log({R}, 'init')
1436
+ } catch (e) {
1437
+ log.err(e, 'init');
1438
+ }
1439
+ // @ts-ignore
1440
+ return R;
1441
+ }
1759
1442
  /**
1760
1443
  * Executes a request, following redirects
1761
1444
  * 替换原 http(s).request,参数类似
@@ -1767,64 +1450,51 @@ function init(uri, opts, cb) {
1767
1450
  * @param {Opts | Cb} [opts] /callback
1768
1451
  * @param {Cb} [callback] /null
1769
1452
  * @returns {Request}
1770
- */
1771
- function request(uri, opts, callback) {
1772
- let R = null;
1773
-
1774
- try {
1775
- // @ts-ignore
1776
- const {opt, cb} = init(uri, opts, callback);
1777
- // log.error({uri, options, opts}, 'request')
1778
-
1779
- const {data, stream} = opt;
1780
- // data 在本函数完成处理,不传递到 request
1781
- opt.data = undefined;
1782
-
1783
- // @ts-ignore
1784
- const req = new Request(opt, cb);
1785
-
1786
- // 非流模式,自动发送请求,流模式通过流写入发送
1787
- if (!stream) {
1788
- // 发送数据
1789
- if (utils.isStream(data)) {
1790
- // Send the request
1791
- let ended = false;
1792
- let errored = false;
1793
-
1794
- data.on('end', () => {
1795
- ended = true;
1796
- });
1797
-
1798
- data.once(
1799
- 'error',
1800
- /** @param {*} err */ err => {
1801
- errored = true;
1802
- // req.destroy(err)
1803
- }
1804
- );
1805
-
1806
- data.on('close', () => {
1807
- if (!ended && !errored) {
1808
- // throw new WritebBeenAbortedError()
1809
- }
1810
- });
1811
-
1812
- // log.error({data}, 'request data.pipe')
1813
- data.pipe(req); // 写入数据流
1814
- } else {
1815
- // log.error({data}, 'request req.end')
1816
- req.end(data); // 写入数据
1817
- }
1818
- }
1819
-
1820
- R = req;
1821
- } catch (e) {
1822
- log.err(e, 'request');
1823
- }
1824
-
1825
- return R
1826
- }
1827
-
1453
+ */ function request(uri, opts, callback) {
1454
+ let R = null;
1455
+ try {
1456
+ // @ts-ignore
1457
+ const { opt, cb } = init(uri, opts, callback);
1458
+ // log({uri, opt, opts}, 'request')
1459
+ const { data, stream } = opt;
1460
+ // data 在本函数完成处理,不传递到 request
1461
+ opt.data = undefined;
1462
+ // @ts-ignore
1463
+ const req = new Request(opt, cb);
1464
+ // 非流模式,自动发送请求,流模式通过流写入发送
1465
+ if (!stream) {
1466
+ // 发送数据
1467
+ if (utils.isStream(data)) {
1468
+ // Send the request
1469
+ let ended = false;
1470
+ let errored = false;
1471
+ data.on('end', ()=>{
1472
+ ended = true;
1473
+ });
1474
+ data.once('error', /** @param {*} err */ (err)=>{
1475
+ errored = true;
1476
+ // req.destroy(err)
1477
+ });
1478
+ data.on('close', ()=>{
1479
+ if (!ended && !errored) {
1480
+ // throw new WritebBeenAbortedError()
1481
+ }
1482
+ });
1483
+ // log.error({data}, 'request data.pipe')
1484
+ data.pipe(req) // 写入数据流
1485
+ ;
1486
+ } else {
1487
+ // log.error({data}, 'request req.end')
1488
+ req.end(data) // 写入数据
1489
+ ;
1490
+ }
1491
+ }
1492
+ R = req;
1493
+ } catch (e) {
1494
+ log.err(e, 'request');
1495
+ }
1496
+ return R;
1497
+ }
1828
1498
  /**
1829
1499
  * 执行简单的数据(支持stream)请求
1830
1500
  * 非流模式,直接写入数据流,流模式,由管道触发,或手动调用 end() data.pipe 写入数据
@@ -1832,32 +1502,29 @@ function request(uri, opts, callback) {
1832
1502
  * organize params for patch, post, put, head, del
1833
1503
  * @param {string} verb
1834
1504
  * @returns {(url: string | Opts, opts?: Opts | Cb, cb?: Cb) => void}}
1835
- */
1836
- function fn(verb) {
1837
- const method = verb.toUpperCase();
1838
- /**
1505
+ */ function fn(verb) {
1506
+ const method = verb.toUpperCase();
1507
+ /**
1839
1508
  *
1840
1509
  * @param {string | Opts} uri /options
1841
1510
  * @param {Opts | Cb} [opts] /callback
1842
1511
  * @param {Cb} [cb] /null
1843
1512
  * @returns
1844
- */
1845
- function fn(uri, opts, cb) {
1846
- // @ts-ignore
1847
- opts.method = method;
1848
- return request(uri, opts, cb)
1849
- }
1850
- return fn
1851
- }
1852
-
1853
- // define like this to please codeintel/intellisense IDEs
1854
- request.get = fn('get');
1855
- request.head = fn('head');
1856
- request.options = fn('options');
1857
- request.post = fn('post');
1858
- request.put = fn('put');
1859
- request.patch = fn('patch');
1860
- request.del = fn('delete');
1513
+ */ function fn(uri, opts, cb) {
1514
+ // @ts-ignore
1515
+ opts.method = method;
1516
+ return request(uri, opts, cb);
1517
+ }
1518
+ return fn;
1519
+ }
1520
+ // define like this to please codeintel/intellisense IDEs
1521
+ request.get = fn('get');
1522
+ request.head = fn('head');
1523
+ request.options = fn('options');
1524
+ request.post = fn('post');
1525
+ request.put = fn('put');
1526
+ request.patch = fn('patch');
1527
+ request.del = fn('delete');
1861
1528
  request.delete = fn('delete');
1862
1529
 
1863
1530
  export { request as default };