@wiajs/request 3.0.0 → 3.0.1

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.
Files changed (117) hide show
  1. package/.github/ISSUE_TEMPLATE.md +56 -0
  2. package/.github/PULL_REQUEST_TEMPLATE.md +13 -0
  3. package/.github/stale.yml +19 -0
  4. package/.swcrc +57 -0
  5. package/.travis.yml +21 -0
  6. package/CHANGELOG.md +717 -0
  7. package/CONTRIBUTING.md +81 -0
  8. package/biome.json +44 -0
  9. package/codecov.yml +2 -0
  10. package/disabled.appveyor.yml +36 -0
  11. package/dist/request.cjs +1476 -0
  12. package/dist/request.mjs +1474 -0
  13. package/examples/README.md +135 -0
  14. package/gulpfile.js +71 -0
  15. package/package.json +1 -2
  16. package/release.sh +45 -0
  17. package/tests/browser/karma.conf.js +57 -0
  18. package/tests/browser/ssl/ca.crt +14 -0
  19. package/tests/browser/ssl/server.crt +14 -0
  20. package/tests/browser/ssl/server.key +15 -0
  21. package/tests/browser/start.js +37 -0
  22. package/tests/browser/test.js +34 -0
  23. package/tests/fixtures/har.json +158 -0
  24. package/tests/googledoodle.jpg +0 -0
  25. package/tests/server.js +142 -0
  26. package/tests/squid.conf +76 -0
  27. package/tests/ssl/ca/README.md +8 -0
  28. package/tests/ssl/ca/ca.cnf +20 -0
  29. package/tests/ssl/ca/ca.crl +0 -0
  30. package/tests/ssl/ca/ca.crt +17 -0
  31. package/tests/ssl/ca/ca.csr +13 -0
  32. package/tests/ssl/ca/ca.key +18 -0
  33. package/tests/ssl/ca/ca.srl +1 -0
  34. package/tests/ssl/ca/client-enc.key +30 -0
  35. package/tests/ssl/ca/client.cnf +20 -0
  36. package/tests/ssl/ca/client.crt +20 -0
  37. package/tests/ssl/ca/client.csr +18 -0
  38. package/tests/ssl/ca/client.key +27 -0
  39. package/tests/ssl/ca/gen-all-certs.sh +6 -0
  40. package/tests/ssl/ca/gen-client.sh +25 -0
  41. package/tests/ssl/ca/gen-localhost.sh +22 -0
  42. package/tests/ssl/ca/gen-server.sh +18 -0
  43. package/tests/ssl/ca/localhost.cnf +20 -0
  44. package/tests/ssl/ca/localhost.crt +20 -0
  45. package/tests/ssl/ca/localhost.csr +18 -0
  46. package/tests/ssl/ca/localhost.js +33 -0
  47. package/tests/ssl/ca/localhost.key +27 -0
  48. package/tests/ssl/ca/server.cnf +19 -0
  49. package/tests/ssl/ca/server.crt +25 -0
  50. package/tests/ssl/ca/server.csr +29 -0
  51. package/tests/ssl/ca/server.js +34 -0
  52. package/tests/ssl/ca/server.key +51 -0
  53. package/tests/ssl/npm-ca.crt +16 -0
  54. package/tests/ssl/test.crt +15 -0
  55. package/tests/ssl/test.key +15 -0
  56. package/tests/test-agent.js +102 -0
  57. package/tests/test-agentOptions.js +51 -0
  58. package/tests/test-api.js +33 -0
  59. package/tests/test-aws.js +123 -0
  60. package/tests/test-baseUrl.js +133 -0
  61. package/tests/test-basic-auth.js +221 -0
  62. package/tests/test-bearer-auth.js +187 -0
  63. package/tests/test-body.js +154 -0
  64. package/tests/test-cookies.js +130 -0
  65. package/tests/test-defaults.js +340 -0
  66. package/tests/test-digest-auth.js +232 -0
  67. package/tests/test-emptyBody.js +56 -0
  68. package/tests/test-errors.js +108 -0
  69. package/tests/test-event-forwarding.js +39 -0
  70. package/tests/test-follow-all-303.js +45 -0
  71. package/tests/test-follow-all.js +57 -0
  72. package/tests/test-form-data-error.js +85 -0
  73. package/tests/test-form-data.js +133 -0
  74. package/tests/test-form-urlencoded.js +73 -0
  75. package/tests/test-form.js +101 -0
  76. package/tests/test-gzip.js +296 -0
  77. package/tests/test-har.js +175 -0
  78. package/tests/test-hawk.js +187 -0
  79. package/tests/test-headers.js +305 -0
  80. package/tests/test-http-signature.js +110 -0
  81. package/tests/test-httpModule.js +112 -0
  82. package/tests/test-https.js +116 -0
  83. package/tests/test-isUrl.js +120 -0
  84. package/tests/test-json-request.js +117 -0
  85. package/tests/test-localAddress.js +49 -0
  86. package/tests/test-multipart-encoding.js +147 -0
  87. package/tests/test-multipart.js +129 -0
  88. package/tests/test-node-debug.js +95 -0
  89. package/tests/test-oauth.js +721 -0
  90. package/tests/test-onelineproxy.js +61 -0
  91. package/tests/test-option-reuse.js +54 -0
  92. package/tests/test-options-convenience-method.js +52 -0
  93. package/tests/test-params.js +101 -0
  94. package/tests/test-piped-redirect.js +55 -0
  95. package/tests/test-pipes.js +383 -0
  96. package/tests/test-pool.js +148 -0
  97. package/tests/test-promise.js +53 -0
  98. package/tests/test-proxy-connect.js +80 -0
  99. package/tests/test-proxy.js +304 -0
  100. package/tests/test-qs.js +135 -0
  101. package/tests/test-redirect-auth.js +131 -0
  102. package/tests/test-redirect-complex.js +93 -0
  103. package/tests/test-redirect.js +449 -0
  104. package/tests/test-rfc3986.js +106 -0
  105. package/tests/test-stream.js +36 -0
  106. package/tests/test-timeout.js +260 -0
  107. package/tests/test-timing.js +147 -0
  108. package/tests/test-toJSON.js +45 -0
  109. package/tests/test-tunnel.js +466 -0
  110. package/tests/test-unix.js +74 -0
  111. package/tests/unicycle.jpg +0 -0
  112. package/request.js +0 -1553
  113. package/src/ZlibTransform.js +0 -27
  114. package/src/caseless.js +0 -118
  115. package/src/index.js +0 -122
  116. package/src/request.js +0 -967
  117. package/src/utils.js +0 -274
@@ -0,0 +1,1474 @@
1
+ /*!
2
+ * wia request v3.0.0
3
+ * (c) 2022-2024 Sibyl Yu and contributors
4
+ * Released under the MIT License.
5
+ */
6
+ import { log as log$2, name } from '@wiajs/log';
7
+ import http from 'node:http';
8
+ import https from 'node:https';
9
+ import assert from 'node:assert';
10
+ import url from 'node:url';
11
+ import stream, { Duplex } from 'node:stream';
12
+ import zlib from 'node:zlib';
13
+ import mime from 'mime-types';
14
+
15
+ class ZlibTransform extends stream.Transform {
16
+ __transform(chunk, encoding, callback) {
17
+ this.push(chunk);
18
+ callback();
19
+ }
20
+
21
+ _transform(chunk, encoding, callback) {
22
+ if (chunk.length !== 0) {
23
+ this._transform = this.__transform;
24
+
25
+ // Add Default Compression headers if no zlib headers are present
26
+ if (chunk[0] !== 120) {
27
+ // Hex: 78
28
+ const header = Buffer.alloc(2);
29
+ header[0] = 120; // Hex: 78
30
+ header[1] = 156; // Hex: 9C
31
+ this.push(header, encoding);
32
+ }
33
+ }
34
+
35
+ this.__transform(chunk, encoding, callback);
36
+ }
37
+ }
38
+
39
+ /**
40
+ * utils for request
41
+ */
42
+
43
+ const {URL} = url;
44
+
45
+ // Whether to use the native URL object or the legacy url module
46
+ let useNativeURL = false;
47
+ try {
48
+ assert(new URL(''));
49
+ } catch (error) {
50
+ useNativeURL = error.code === 'ERR_INVALID_URL';
51
+ }
52
+
53
+ // URL fields to preserve in copy operations
54
+ const preservedUrlFields = [
55
+ 'auth',
56
+ 'host',
57
+ 'hostname',
58
+ 'href',
59
+ 'path',
60
+ 'pathname',
61
+ 'port',
62
+ 'protocol',
63
+ 'query',
64
+ 'search',
65
+ 'hash',
66
+ ];
67
+
68
+ /**
69
+ *
70
+ * @param {*} code
71
+ * @param {*} message
72
+ * @param {*} baseClass
73
+ * @returns
74
+ */
75
+ function createErrorType(code, message, baseClass) {
76
+ // Create constructor
77
+ function CustomError(properties) {
78
+ // istanbul ignore else
79
+ if (isFunction(Error.captureStackTrace)) {
80
+ Error.captureStackTrace(this, this.constructor);
81
+ }
82
+ Object.assign(this, properties || {});
83
+ this.code = code;
84
+ this.message = this.cause ? `${message}: ${this.cause.message}` : message;
85
+ }
86
+
87
+ // Attach constructor and set default properties
88
+ CustomError.prototype = new (baseClass || Error)();
89
+ Object.defineProperties(CustomError.prototype, {
90
+ constructor: {
91
+ value: CustomError,
92
+ enumerable: false,
93
+ },
94
+ name: {
95
+ value: `Error [${code}]`,
96
+ enumerable: false,
97
+ },
98
+ });
99
+ return CustomError
100
+ }
101
+
102
+ const InvalidUrlError = createErrorType('ERR_INVALID_URL', 'Invalid URL', TypeError);
103
+
104
+ // @ts-ignore
105
+ const typeOfTest = type => thing => typeof thing === type;
106
+
107
+ /**
108
+ * Determine if a value is a String
109
+ *
110
+ * @param {*} val The value to test
111
+ *
112
+ * @returns {boolean} True if value is a String, otherwise false
113
+ */
114
+ const isString = typeOfTest('string');
115
+
116
+ /**
117
+ * Determine if a value is an Array
118
+ *
119
+ * @param {Object} val The value to test
120
+ *
121
+ * @returns {boolean} True if value is an Array, otherwise false
122
+ */
123
+ const {isArray} = Array;
124
+
125
+ /**
126
+ * Determine if a value is undefined
127
+ *
128
+ * @param {*} val The value to test
129
+ *
130
+ * @returns {boolean} True if the value is undefined, otherwise false
131
+ */
132
+ const isUndefined = typeOfTest('undefined');
133
+
134
+ /**
135
+ * Determine if a value is a Buffer
136
+ *
137
+ * @param {*} val The value to test
138
+ *
139
+ * @returns {boolean} True if value is a Buffer, otherwise false
140
+ */
141
+ function isBuffer(val) {
142
+ return (
143
+ val !== null &&
144
+ !isUndefined(val) &&
145
+ val.constructor !== null &&
146
+ !isUndefined(val.constructor) &&
147
+ isFunction(val.constructor.isBuffer) &&
148
+ val.constructor.isBuffer(val)
149
+ )
150
+ }
151
+
152
+ /**
153
+ * Determine if a value is a Function
154
+ *
155
+ * @param {*} val The value to test
156
+ * @returns {boolean} True if value is a Function, otherwise false
157
+ */
158
+ const isFunction = typeOfTest('function');
159
+
160
+ /**
161
+ * Determine if a value is a Number
162
+ *
163
+ * @param {*} val The value to test
164
+ *
165
+ * @returns {boolean} True if value is a Number, otherwise false
166
+ */
167
+ const isNumber = typeOfTest('number');
168
+
169
+ /**
170
+ * Determine if a value is an Object
171
+ *
172
+ * @param {*} thing The value to test
173
+ *
174
+ * @returns {boolean} True if value is an Object, otherwise false
175
+ */
176
+ const isObject = thing => thing !== null && typeof thing === 'object';
177
+
178
+ /**
179
+ * Determine if a value is a Boolean
180
+ *
181
+ * @param {*} thing The value to test
182
+ * @returns {boolean} True if value is a Boolean, otherwise false
183
+ */
184
+ const isBoolean = thing => thing === true || thing === false;
185
+
186
+ const noop = () => {};
187
+
188
+ /**
189
+ *
190
+ * @param {*} value
191
+ * @returns
192
+ */
193
+ function isURL(value) {
194
+ return URL && value instanceof URL
195
+ }
196
+
197
+ function isReadStream(rs) {
198
+ return rs.readable && rs.path && rs.mode
199
+ }
200
+
201
+ /**
202
+ *
203
+ * @param {*} urlObject
204
+ * @param {*} target
205
+ * @returns
206
+ */
207
+ function spreadUrlObject(urlObject, target) {
208
+ const spread = target || {};
209
+ for (const key of preservedUrlFields) {
210
+ spread[key] = urlObject[key];
211
+ }
212
+
213
+ // Fix IPv6 hostname
214
+ if (spread.hostname.startsWith('[')) {
215
+ spread.hostname = spread.hostname.slice(1, -1);
216
+ }
217
+ // Ensure port is a number
218
+ if (spread.port !== '') {
219
+ spread.port = Number(spread.port);
220
+ }
221
+ // Concatenate path
222
+ spread.path = spread.search ? spread.pathname + spread.search : spread.pathname;
223
+
224
+ return spread
225
+ }
226
+
227
+ /**
228
+ *
229
+ * @param {*} input
230
+ * @returns
231
+ */
232
+ function parseUrl(input) {
233
+ let parsed;
234
+ // istanbul ignore else
235
+ if (useNativeURL) {
236
+ parsed = new URL(input);
237
+ } else {
238
+ // Ensure the URL is valid and absolute
239
+ parsed = validateUrl(url.parse(input));
240
+ if (!isString(parsed.protocol)) {
241
+ throw new InvalidUrlError({input})
242
+ }
243
+ }
244
+ return parsed
245
+ }
246
+
247
+ /**
248
+ *
249
+ * @param {*} input
250
+ * @returns
251
+ */
252
+ function validateUrl(input) {
253
+ if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
254
+ throw new InvalidUrlError({input: input.href || input})
255
+ }
256
+ if (/^\[/.test(input.host) && !/^\[[:0-9a-f]+\](:\d+)?$/i.test(input.host)) {
257
+ throw new InvalidUrlError({input: input.href || input})
258
+ }
259
+ return input
260
+ }
261
+
262
+ /**
263
+ *
264
+ * @param {*} relative
265
+ * @param {*} base
266
+ * @returns
267
+ */
268
+ function resolveUrl(relative, base) {
269
+ // istanbul ignore next
270
+ return useNativeURL ? new URL(relative, base) : parseUrl(url.resolve(base, relative))
271
+ }
272
+
273
+ /**
274
+ *
275
+ * @param {string} method
276
+ * @param {number} code
277
+ * @returns
278
+ */
279
+ function noBody(method, code) {
280
+ return (
281
+ method === 'HEAD' ||
282
+ // Informational
283
+ (code >= 100 && code < 200) ||
284
+ // No Content
285
+ code === 204 ||
286
+ // Not Modified
287
+ code === 304
288
+ )
289
+ }
290
+
291
+ const utils = {
292
+ createErrorType,
293
+ InvalidUrlError,
294
+ isString,
295
+ isArray,
296
+ isBuffer,
297
+ isUndefined,
298
+ isNumber,
299
+ isBoolean,
300
+ isFunction,
301
+ isObject,
302
+ isURL,
303
+ isReadStream,
304
+ noop,
305
+ parseUrl,
306
+ spreadUrlObject,
307
+ validateUrl,
308
+ resolveUrl,
309
+ noBody,
310
+ };
311
+
312
+ class Caseless {
313
+ /**
314
+ * @param {*} dict
315
+ */
316
+ constructor(dict) {
317
+ this.dict = dict || {};
318
+ }
319
+
320
+ /**
321
+ *
322
+ * @param {*} name
323
+ * @param {*} value
324
+ * @param {*} clobber
325
+ * @returns
326
+ */
327
+ set(name, value, clobber) {
328
+ if (typeof name === 'object') {
329
+ for (const n of name) {
330
+ this.set(n, name[n], value);
331
+ }
332
+ } else {
333
+ if (typeof clobber === 'undefined') clobber = true;
334
+ const has = this.has(name);
335
+
336
+ if (!clobber && has) this.dict[has] = this.dict[has] + ',' + value;
337
+ else this.dict[has || name] = value;
338
+ return has
339
+ }
340
+ }
341
+
342
+ /**
343
+ *
344
+ * @param {string} name
345
+ * @returns
346
+ */
347
+ has(name) {
348
+ const keys = Object.keys(this.dict);
349
+ name = name.toLowerCase();
350
+ for (let i = 0; i < keys.length; i++) {
351
+ if (keys[i].toLowerCase() === name) return keys[i]
352
+ }
353
+ return false
354
+ }
355
+
356
+ /**
357
+ *
358
+ * @param {string} name
359
+ * @returns
360
+ */
361
+ get(name) {
362
+ name = name.toLowerCase();
363
+ let result;
364
+ let _key;
365
+ const headers = this.dict;
366
+ for (const key of Object.keys(headers)) {
367
+ _key = key.toLowerCase();
368
+ if (name === _key) result = headers[key];
369
+ }
370
+ return result
371
+ }
372
+
373
+ /**
374
+ *
375
+ * @param {string} name
376
+ * @returns
377
+ */
378
+ swap(name) {
379
+ const has = this.has(name);
380
+ if (has === name) return
381
+ if (!has) throw new Error('There is no header than matches "' + name + '"')
382
+ this.dict[name] = this.dict[has];
383
+ delete this.dict[has];
384
+ }
385
+
386
+ del(name) {
387
+ name = String(name).toLowerCase();
388
+ let deleted = false;
389
+ let changed = 0;
390
+ const dict = this.dict;
391
+ for (const key of Object.keys(this.dict)) {
392
+ if (name === String(key).toLowerCase()) {
393
+ deleted = delete dict[key];
394
+ changed += 1;
395
+ }
396
+ }
397
+ return changed === 0 ? true : deleted
398
+ }
399
+ }
400
+
401
+ /**
402
+ * fork from follow-redirects
403
+ * https://github.com/follow-redirects/follow-redirects
404
+ */
405
+
406
+ const log$1 = log$2({env: `wia:req:${name(__filename)}`});
407
+
408
+ const httpModules = {'http:': http, 'https:': https};
409
+
410
+ const zlibOptions = {
411
+ flush: zlib.constants.Z_SYNC_FLUSH,
412
+ finishFlush: zlib.constants.Z_SYNC_FLUSH,
413
+ };
414
+
415
+ const brotliOptions = {
416
+ flush: zlib.constants.BROTLI_OPERATION_FLUSH,
417
+ finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH,
418
+ };
419
+
420
+ const isBrotliSupported = utils.isFunction(zlib.createBrotliDecompress);
421
+
422
+ // Create handlers that pass events from native requests
423
+ const writeEvents = [
424
+ 'abort', // 弃用
425
+ 'aborted', // 弃用
426
+ 'close',
427
+ 'connect',
428
+ 'continue',
429
+ 'drain',
430
+ 'error',
431
+ 'finish',
432
+ 'information',
433
+ 'pipe',
434
+ // 'response', 由 processResponse 触发
435
+ 'socket',
436
+ 'timeout',
437
+ 'unpipe',
438
+ 'upgrade',
439
+ ];
440
+
441
+ const writeEventEmit = Object.create(null);
442
+ for (const ev of writeEvents)
443
+ writeEventEmit[ev] = function (...args) {
444
+ const m = this; // 事件回调,this === clientRequest 实例
445
+ log$1.debug('req event', {ev});
446
+ m.redirectReq.emit(ev, ...args); // req 事情映射到 redirectReq 上触发
447
+ };
448
+
449
+ // stream.Readable
450
+ // data 单独处理
451
+ const readEvents = ['close', 'end', 'error', 'pause', 'readable', 'resume'];
452
+ const readEventEmit = Object.create(null);
453
+ for (const ev of readEvents)
454
+ readEventEmit[ev] = function (...args) {
455
+ const m = this; // 事件回调,this === clientRequest 实例
456
+ log$1.debug('res event', {ev});
457
+ m.redirectReq.emit(ev, ...args); // 向上触发事件
458
+ };
459
+
460
+ // Error types with codes
461
+ const RedirectionError = utils.createErrorType('ERR_FR_REDIRECTION_FAILURE', 'Redirected request failed');
462
+
463
+ const TooManyRedirectsError = utils.createErrorType(
464
+ 'ERR_FR_TOO_MANY_REDIRECTS',
465
+ 'Maximum number of redirects exceeded',
466
+ RedirectionError
467
+ );
468
+
469
+ const MaxBodyLengthExceededError = utils.createErrorType(
470
+ 'ERR_FR_MAX_BODY_LENGTH_EXCEEDED',
471
+ 'Request body larger than maxBodyLength limit'
472
+ );
473
+
474
+ const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', 'write after end');
475
+
476
+ /**
477
+ * An HTTP(S) request that can be redirected
478
+ * wrap http.ClientRequest
479
+ */
480
+ class Request extends Duplex {
481
+ _timeout = 0
482
+ /** @type {*} */
483
+ socket = null
484
+ /** @type {http.ClientRequest} */
485
+ _currentRequest = null
486
+ /** @type {stream.Readable} */
487
+ response = null
488
+ /** @type {stream.Readable} */
489
+ responseStream = null
490
+ timing = false
491
+ responseStarted = false
492
+ responseStartTime = 0
493
+ _destdata = false
494
+ _paused = false
495
+ _respended = false
496
+ /** @type {stream.Readable} */
497
+ pipesrc = null // 被 pipe 时的 src stream
498
+ /** @type {stream.Writable[]} */
499
+ pipedests = [] // pipe dest
500
+
501
+ /**
502
+ * responseCallback 原消息处理回调
503
+ * @param {*} options
504
+ * @param {*} resCallback
505
+ */
506
+ constructor(options, resCallback) {
507
+ super();
508
+ const m = this;
509
+
510
+ // log({options}, 'constructor');
511
+
512
+ // Initialize the request
513
+ m._sanitizeOptions(options);
514
+ m._options = options;
515
+ m.headers = options.headers;
516
+ m._ended = false;
517
+ m._ending = false;
518
+ m._redirectCount = 0;
519
+ /** @type {any[]} */
520
+ m._redirects = [];
521
+ m._requestBodyLength = 0;
522
+ /** @type {any[]} */
523
+ m._requestBodyBuffers = [];
524
+
525
+ // save the callback if passed
526
+ m.resCallback = resCallback;
527
+ // React to responses of native requests
528
+ // 接管 response 事件,非重定向,触发 response 事件
529
+ m._onResponse = response => {
530
+ try {
531
+ m.processResponse(response);
532
+ } catch (cause) {
533
+ m.emit('error', cause instanceof RedirectionError ? cause : new RedirectionError({cause: cause}));
534
+ }
535
+ };
536
+
537
+ // Proxy all other public ClientRequest methods
538
+ for (const method of ['flushHeaders', 'setNoDelay', 'setSocketKeepAlive']) {
539
+ m[method] = (a, b) => {
540
+ log$1.debug(method, {a, b});
541
+ m._currentRequest[method](a, b);
542
+ };
543
+ }
544
+
545
+ // Proxy all public ClientRequest properties
546
+ for (const property of ['aborted', 'connection', 'socket']) {
547
+ Object.defineProperty(m, property, {
548
+ get() {
549
+ const val = m._currentRequest[property];
550
+ log$1.debug('get property', {property});
551
+ return val
552
+ },
553
+ });
554
+ }
555
+
556
+ // 流模式
557
+ if (options.stream)
558
+ // 被 pipe 作为目标时触发,拷贝 src headers
559
+ m.on(
560
+ 'pipe',
561
+ /** @type {stream.Readable} */ src => {
562
+ // m.ntick &&
563
+ if (m._currentRequest) {
564
+ m.emit('error', new Error('You cannot pipe to this stream after the outbound request has started.'));
565
+ }
566
+
567
+ m.pipesrc = src;
568
+
569
+ if (utils.isReadStream(src)) {
570
+ if (!m.hasHeader('content-type')) {
571
+ m.setHeader('content-type', mime.lookup(src.path));
572
+ }
573
+ } else {
574
+ if (src.headers) {
575
+ for (const h of src.headers) {
576
+ if (!m.hasHeader(h)) {
577
+ m.setHeader(h, src.headers[h]);
578
+ }
579
+ }
580
+ }
581
+
582
+ // if (src.method && !self.explicitMethod) {
583
+ // m.method = src.method
584
+ // }
585
+ }
586
+ }
587
+ );
588
+
589
+ // Perform the first request
590
+ // m.request(); // 写入数据时执行,否则 pipe时无法写入header
591
+ }
592
+
593
+ /**
594
+ * Executes the next native request (initial or redirect)
595
+ * @returns http(s) 实例
596
+ */
597
+ request() {
598
+ let R = null;
599
+
600
+ try {
601
+ const m = this;
602
+ // read stream
603
+ m.response = null;
604
+ m.responseStarted = false;
605
+ m.responseStream = null;
606
+ m.timing = false;
607
+ m.responseStartTime = 0;
608
+ m._destdata = false;
609
+ m._paused = false;
610
+ m._respended = false;
611
+
612
+ // m.httpModule = httpModules[protocol];
613
+
614
+ // Load the native protocol
615
+ let {protocol} = m._options;
616
+ const {agents} = m._options;
617
+
618
+ // 代理以目的网址协议为准
619
+ // If specified, use the agent corresponding to the protocol
620
+ // (HTTP and HTTPS use different types of agents)
621
+ if (agents) {
622
+ const scheme = protocol.slice(0, -1);
623
+ m._options.agent = agents[scheme];
624
+ }
625
+
626
+ // http 非隧道代理模式,模块以代理主机为准,其他以目的网址为准
627
+ // 代理内部会根据代理协议选择 http(s) 发起请求创建连接
628
+ if (protocol === 'http:' && agents.http) {
629
+ protocol = agents.http.proxy && !agents.http.tunnel ? agents.http.proxy.protocol : protocol;
630
+ }
631
+
632
+ const httpModule = httpModules[protocol];
633
+ if (!httpModule) throw TypeError(`Unsupported protocol: ${protocol}`)
634
+
635
+ log$1.debug('request', {options: m._options, protocol});
636
+
637
+ debugger
638
+
639
+ // Create the native request and set up its event handlers
640
+ const req = httpModule.request(m._options, m._onResponse);
641
+ m._currentRequest = req;
642
+
643
+ req.redirectReq = m;
644
+ // 接收req事件,转发 到 redirectReq 发射
645
+ for (const ev of writeEvents) req.on(ev, writeEventEmit[ev]);
646
+
647
+ // RFC7230§5.3.1: When making a request directly to an origin server, […]
648
+ // a client MUST send only the absolute path […] as the request-target.
649
+ // When making a request to a proxy, […]
650
+ // a client MUST send the target URI in absolute-form […].
651
+ m._currentUrl = /^\//.test(m._options.path) ? url.format(m._options) : m._options.path;
652
+
653
+ // End a redirected request
654
+ // (The first request must be ended explicitly with RedirectableRequest#end)
655
+ if (m._isRedirect) {
656
+ // Write the request entity and end
657
+ let i = 0;
658
+ const buffers = m._requestBodyBuffers
659
+ ;(function writeNext(error) {
660
+ // Only write if this request has not been redirected yet
661
+ /* istanbul ignore else */
662
+ if (req === m._currentRequest) {
663
+ // Report any write errors
664
+ /* istanbul ignore if */
665
+ if (error) m.emit('error', error);
666
+ // Write the next buffer if there are still left
667
+ else if (i < buffers.length) {
668
+ const buf = buffers[i++];
669
+ /* istanbul ignore else */
670
+ if (!req.finished) req.write(buf.data, buf.encoding, writeNext);
671
+ }
672
+ // End the request if `end` has been called on us
673
+ else if (m._ended) req.end();
674
+ }
675
+ })();
676
+ }
677
+
678
+ R = req;
679
+ } catch (e) {
680
+ log$1.err(e, 'request');
681
+ throw e
682
+ }
683
+
684
+ return R
685
+ }
686
+
687
+ abort() {
688
+ destroyRequest(this._currentRequest);
689
+ this._currentRequest.abort();
690
+ this.emit('abort');
691
+ }
692
+
693
+ /**
694
+ * 析构
695
+ * @param {*} error
696
+ * @returns
697
+ */
698
+ destroy(error) {
699
+ const m = this;
700
+ if (!m._ended) m.end();
701
+ else if (m.response) m.response.destroy();
702
+ else if (m.responseStream) m.responseStream.destroy();
703
+
704
+ // m.clearTimeout();
705
+ destroyRequest(m._currentRequest, error);
706
+ super.destroy(error);
707
+ return this
708
+ }
709
+
710
+ /**
711
+ * Writes buffered data to the current native request
712
+ * 如 request 不存在,则创建连接,pipe 时可写入 header
713
+ * @param {*} chunk
714
+ * @param {BufferEncoding=} encoding
715
+ * @param {(error: Error) => void} [cb]
716
+ * @returns {boolean}
717
+ */
718
+ write(chunk, encoding, cb) {
719
+ const m = this;
720
+
721
+ log$1.debug('write', {data: chunk, encoding, callback: cb});
722
+
723
+ // Writing is not allowed if end has been called
724
+ if (m._ending) throw new WriteAfterEndError()
725
+
726
+ // ! 数据写入时连接,pipe 时可设置 header
727
+ if (!m._currentRequest) m.request();
728
+
729
+ // Validate input and shift parameters if necessary
730
+ if (!utils.isString(chunk) && !utils.isBuffer(chunk))
731
+ throw new TypeError('data should be a string, Buffer or Uint8Array')
732
+
733
+ if (utils.isFunction(encoding)) {
734
+ cb = encoding;
735
+ encoding = null;
736
+ }
737
+
738
+ // Ignore empty buffers, since writing them doesn't invoke the callback
739
+ // https://github.com/nodejs/node/issues/22066
740
+ if (chunk.length === 0) {
741
+ if (cb) cb();
742
+ return
743
+ }
744
+
745
+ // Only write when we don't exceed the maximum body length
746
+ if (m._requestBodyLength + chunk.length <= m._options.maxBodyLength) {
747
+ m._requestBodyLength += chunk.length;
748
+ m._requestBodyBuffers.push({data: chunk, encoding});
749
+ m._currentRequest.write(chunk, encoding, cb);
750
+ }
751
+ // Error when we exceed the maximum body length
752
+ else {
753
+ m.emit('error', new MaxBodyLengthExceededError());
754
+ m.abort();
755
+ }
756
+ }
757
+
758
+ /**
759
+ * Ends the current native request
760
+ * @param {*} data
761
+ * @param {*} encoding
762
+ * @param {*} callback
763
+ */
764
+ end(data, encoding, callback) {
765
+ const m = this;
766
+
767
+ // Shift parameters if necessary
768
+ if (utils.isFunction(data)) {
769
+ callback = data;
770
+ data = null;
771
+ encoding = null;
772
+ } else if (utils.isFunction(encoding)) {
773
+ callback = encoding;
774
+ encoding = null;
775
+ }
776
+
777
+ // ! 数据写入时连接,pipe 时可设置 header
778
+ if (!m._currentRequest) m.request();
779
+
780
+ // Write data if needed and end
781
+ if (!data) {
782
+ m._ended = true;
783
+ m._ending = true;
784
+ m._currentRequest.end(null, null, callback);
785
+ } else {
786
+ const currentRequest = m._currentRequest;
787
+ m.write(data, encoding, () => {
788
+ m._ended = true;
789
+ currentRequest.end(null, null, callback);
790
+ });
791
+
792
+ m._ending = true;
793
+ }
794
+ }
795
+
796
+ /**
797
+ *
798
+ * @param {string} name
799
+ * @returns
800
+ */
801
+ hasHeader(name) {
802
+ return this._options.headers.includes(name)
803
+ }
804
+
805
+ /**
806
+ *
807
+ * @param {string} name
808
+ * @returns {string}
809
+ */
810
+ getHeader(name) {
811
+ return this._options.headers[name]
812
+ }
813
+
814
+ /**
815
+ * Sets a header value on the current native request
816
+ * @param {string} name
817
+ */
818
+ setHeader(name, value) {
819
+ this._options.headers[name] = value;
820
+ this._currentRequest?.setHeader(name, value);
821
+ }
822
+
823
+ /**
824
+ * Clears a header value on the current native request
825
+ * @param {string} name
826
+ */
827
+ removeHeader(name) {
828
+ delete this._options.headers[name];
829
+ this._currentRequest?.removeHeader(name);
830
+ }
831
+
832
+ /**
833
+ * 标头是否已发送
834
+ * @returns
835
+ */
836
+ get headersSent() {
837
+ return this._currentRequest?.headersSent
838
+ }
839
+
840
+ // Global timeout for all underlying requests
841
+ /**
842
+ *
843
+ * @param {*} msecs
844
+ * @param {*} callback
845
+ * @returns
846
+ */
847
+ setTimeout(msecs, callback) {
848
+ const m = this;
849
+
850
+ // Destroys the socket on timeout
851
+ /**
852
+ *
853
+ * @param {*} socket
854
+ */
855
+ function destroyOnTimeout(socket) {
856
+ socket.setTimeout(msecs);
857
+ socket.removeListener('timeout', socket.destroy);
858
+ socket.addListener('timeout', socket.destroy);
859
+ }
860
+
861
+ // Sets up a timer to trigger a timeout event
862
+ /**
863
+ *
864
+ * @param {*} socket
865
+ */
866
+ function startTimer(socket) {
867
+ if (m._timeout) {
868
+ clearTimeout(m._timeout);
869
+ }
870
+ m._timeout = setTimeout(() => {
871
+ m.emit('timeout');
872
+ clearTimer();
873
+ }, msecs);
874
+ destroyOnTimeout(socket);
875
+ }
876
+
877
+ // Stops a timeout from triggering
878
+ function clearTimer() {
879
+ // Clear the timeout
880
+ if (m._timeout) {
881
+ clearTimeout(m._timeout);
882
+ m._timeout = null;
883
+ }
884
+
885
+ // Clean up all attached listeners
886
+ m.removeListener('abort', clearTimer);
887
+ m.removeListener('error', clearTimer);
888
+ m.removeListener('response', clearTimer);
889
+ m.removeListener('close', clearTimer);
890
+
891
+ if (callback) {
892
+ m.removeListener('timeout', callback);
893
+ }
894
+ if (!m.socket) {
895
+ m._currentRequest.removeListener('socket', startTimer);
896
+ }
897
+ }
898
+
899
+ // Attach callback if passed
900
+ if (callback) m.on('timeout', callback);
901
+
902
+ // Start the timer if or when the socket is opened
903
+ if (m.socket) startTimer(m.socket);
904
+ else m._currentRequest.once('socket', startTimer);
905
+
906
+ // Clean up on events
907
+ m.on('socket', destroyOnTimeout);
908
+ m.on('abort', clearTimer);
909
+ m.on('error', clearTimer);
910
+ m.on('response', clearTimer);
911
+ m.on('close', clearTimer);
912
+ return m
913
+ }
914
+
915
+ _sanitizeOptions(options) {
916
+ // Ensure headers are always present
917
+ if (!options.headers) options.headers = {};
918
+
919
+ // Since http.request treats host as an alias of hostname,
920
+ // but the url module interprets host as hostname plus port,
921
+ // eliminate the host property to avoid confusion.
922
+ if (options.host) {
923
+ // Use hostname if set, because it has precedence
924
+ if (!options.hostname) {
925
+ options.hostname = options.host;
926
+ }
927
+ options.host = undefined;
928
+ }
929
+
930
+ // Complete the URL object when necessary
931
+ if (!options.pathname && options.path) {
932
+ const searchPos = options.path.indexOf('?');
933
+ if (searchPos < 0) {
934
+ options.pathname = options.path;
935
+ } else {
936
+ options.pathname = options.path.substring(0, searchPos);
937
+ options.search = options.path.substring(searchPos);
938
+ }
939
+ }
940
+ }
941
+
942
+ /**
943
+ * Processes a response from the current native request
944
+ * @param {*} response
945
+ * @returns
946
+ */
947
+ processResponse(response) {
948
+ const m = this;
949
+
950
+ // Store the redirected response
951
+ const {statusCode} = response;
952
+ if (m._options.trackRedirects) {
953
+ m._redirects.push({
954
+ url: m._currentUrl,
955
+ headers: response.headers,
956
+ statusCode,
957
+ });
958
+ }
959
+
960
+ // RFC7231§6.4: The 3xx (Redirection) class of status code indicates
961
+ // that further action needs to be taken by the user agent in order to
962
+ // fulfill the request. If a Location header field is provided,
963
+ // the user agent MAY automatically redirect its request to the URI
964
+ // referenced by the Location field value,
965
+ // even if the specific status code is not understood.
966
+
967
+ // If the response is not a redirect; return it as-is
968
+ const {location} = response.headers;
969
+
970
+ log$1('processResponse', {statusCode, headers: response.headers});
971
+
972
+ if (!location || m._options.followRedirects === false || statusCode < 300 || statusCode >= 400) {
973
+ // 非重定向,返回给原始回调处理
974
+ response.responseUrl = m._currentUrl;
975
+ response.redirects = m._redirects;
976
+ m.response = response;
977
+ // Be a good stream and emit end when the response is finished.
978
+ // Hack to emit end on close because of a core bug that never fires end
979
+ response.on('close', () => {
980
+ if (!m._respended) {
981
+ m.response.emit('end');
982
+ }
983
+ });
984
+
985
+ response.once('end', () => {
986
+ m._respended = true;
987
+ });
988
+
989
+ const responseStream = m.processStream(response);
990
+ // NOTE: responseStartTime is deprecated in favor of .timings
991
+ response.responseStartTime = m.responseStartTime;
992
+
993
+ // 触发原回调函数
994
+ m.resCallback?.(response, responseStream);
995
+
996
+ // 类似 ClientRequest,触发 response 事件
997
+ m.emit('response', response, responseStream);
998
+
999
+ // Clean up
1000
+ m._requestBodyBuffers = [];
1001
+ return
1002
+ }
1003
+
1004
+ // The response is a redirect, so abort the current request
1005
+ destroyRequest(m._currentRequest);
1006
+ // Discard the remainder of the response to avoid waiting for data
1007
+ response.destroy();
1008
+
1009
+ // RFC7231§6.4: A client SHOULD detect and intervene
1010
+ // in cyclical redirections (i.e., "infinite" redirection loops).
1011
+ if (++m._redirectCount > m._options.maxRedirects) throw new TooManyRedirectsError()
1012
+
1013
+ // Store the request headers if applicable
1014
+ let requestHeaders;
1015
+ const {beforeRedirect} = m._options;
1016
+ if (beforeRedirect) {
1017
+ requestHeaders = {
1018
+ // The Host header was set by nativeProtocol.request
1019
+ Host: response.req.getHeader('host'),
1020
+ ...m._options.headers,
1021
+ };
1022
+ }
1023
+
1024
+ // RFC7231§6.4: Automatic redirection needs to done with
1025
+ // care for methods not known to be safe, […]
1026
+ // RFC7231§6.4.2–3: For historical reasons, a user agent MAY change
1027
+ // the request method from POST to GET for the subsequent request.
1028
+ const {method} = m._options;
1029
+ if (
1030
+ ((statusCode === 301 || statusCode === 302) && m._options.method === 'POST') ||
1031
+ // RFC7231§6.4.4: The 303 (See Other) status code indicates that
1032
+ // the server is redirecting the user agent to a different resource […]
1033
+ // A user agent can perform a retrieval request targeting that URI
1034
+ // (a GET or HEAD request if using HTTP) […]
1035
+ (statusCode === 303 && !/^(?:GET|HEAD)$/.test(m._options.method))
1036
+ ) {
1037
+ m._options.method = 'GET';
1038
+ // Drop a possible entity and headers related to it
1039
+ m._requestBodyBuffers = [];
1040
+ removeMatchingHeaders(/^content-/i, m._options.headers);
1041
+ }
1042
+
1043
+ // Drop the Host header, as the redirect might lead to a different host
1044
+ const currentHostHeader = removeMatchingHeaders(/^host$/i, m._options.headers);
1045
+
1046
+ // If the redirect is relative, carry over the host of the last request
1047
+ const currentUrlParts = utils.parseUrl(m._currentUrl);
1048
+ const currentHost = currentHostHeader || currentUrlParts.host;
1049
+ const currentUrl = /^\w+:/.test(location)
1050
+ ? m._currentUrl
1051
+ : url.format(Object.assign(currentUrlParts, {host: currentHost}));
1052
+
1053
+ // Create the redirected request
1054
+ const redirectUrl = utils.resolveUrl(location, currentUrl);
1055
+ log$1({redirectUrl}, 'redirecting to');
1056
+ m._isRedirect = true;
1057
+ // 覆盖原 url 解析部分,包括 protocol、hostname、port等
1058
+ utils.spreadUrlObject(redirectUrl, m._options);
1059
+
1060
+ // Drop confidential headers when redirecting to a less secure protocol
1061
+ // or to a different domain that is not a superdomain
1062
+ if (
1063
+ (redirectUrl.protocol !== currentUrlParts.protocol && redirectUrl.protocol !== 'https:') ||
1064
+ (redirectUrl.host !== currentHost && !isSubdomain(redirectUrl.host, currentHost))
1065
+ ) {
1066
+ removeMatchingHeaders(/^(?:(?:proxy-)?authorization|cookie)$/i, this._options.headers);
1067
+ }
1068
+
1069
+ // Evaluate the beforeRedirect callback
1070
+ if (utils.isFunction(beforeRedirect)) {
1071
+ const responseDetails = {
1072
+ headers: response.headers,
1073
+ statusCode,
1074
+ };
1075
+ const requestDetails = {
1076
+ url: currentUrl,
1077
+ method,
1078
+ headers: requestHeaders,
1079
+ };
1080
+
1081
+ beforeRedirect(m._options, responseDetails, requestDetails);
1082
+ m._sanitizeOptions(m._options);
1083
+ }
1084
+
1085
+ // Perform the redirected request
1086
+ m.request(); // 重新执行请求
1087
+ }
1088
+
1089
+ /**
1090
+ * 处理响应stream
1091
+ * 如:解压,透传流,需设置 decompress = false,避免解压数据
1092
+ * @param {*} res
1093
+ */
1094
+ processStream(res) {
1095
+ const m = this;
1096
+ const {_options: opts} = m;
1097
+
1098
+ const streams = [res];
1099
+
1100
+ // 'transfer-encoding': 'chunked'时,无content-length,axios v1.2 不能自动解压
1101
+ const responseLength = +res.headers['content-length'];
1102
+
1103
+ log$1('processStream', {
1104
+ statusCode: res.statusCode,
1105
+ responseLength,
1106
+ headers: res.headers,
1107
+ });
1108
+
1109
+ if (opts.transformStream) {
1110
+ opts.transformStream.responseLength = responseLength;
1111
+ streams.push(opts.transformStream);
1112
+ }
1113
+
1114
+ const empty = utils.noBody(opts.method, res.statusCode);
1115
+ // decompress the response body transparently if required
1116
+ if (opts.decompress !== false && res.headers['content-encoding']) {
1117
+ // if decompress disabled we should not decompress
1118
+ // 压缩内容,加入 解压 stream,自动解压,axios v1.2 存在bug,不能自动解压
1119
+ // if no content, but headers still say that it is encoded,
1120
+ // remove the header not confuse downstream operations
1121
+ // if ((!responseLength || res.statusCode === 204) && res.headers['content-encoding']) {
1122
+ if (empty && res.headers['content-encoding']) res.headers['content-encoding'] = undefined;
1123
+
1124
+ // 'content-encoding': 'gzip',
1125
+ switch ((res.headers['content-encoding'] || '').toLowerCase()) {
1126
+ /*eslint default-case:0*/
1127
+ case 'gzip':
1128
+ case 'x-gzip':
1129
+ case 'compress':
1130
+ case 'x-compress':
1131
+ // add the unzipper to the body stream processing pipeline
1132
+ streams.push(zlib.createUnzip(zlibOptions));
1133
+
1134
+ // remove the content-encoding in order to not confuse downstream operations
1135
+ res.headers['content-encoding'] = undefined;
1136
+ break
1137
+
1138
+ case 'deflate':
1139
+ streams.push(new ZlibTransform());
1140
+
1141
+ // add the unzipper to the body stream processing pipeline
1142
+ streams.push(zlib.createUnzip(zlibOptions));
1143
+
1144
+ // remove the content-encoding in order to not confuse downstream operations
1145
+ res.headers['content-encoding'] = undefined;
1146
+ break
1147
+
1148
+ case 'br':
1149
+ if (isBrotliSupported) {
1150
+ streams.push(zlib.createBrotliDecompress(brotliOptions));
1151
+ res.headers['content-encoding'] = undefined;
1152
+ }
1153
+ break
1154
+ }
1155
+ }
1156
+
1157
+ const responseStream = streams.length > 1 ? stream.pipeline(streams, utils.noop) : streams[0];
1158
+ // 将内部 responseStream 可读流 映射到 redirectReq
1159
+
1160
+ m.responseStream = responseStream;
1161
+ responseStream.redirectReq = m; // 事情触发时引用
1162
+
1163
+ // stream 模式,事件透传到 请求类
1164
+ if (opts.stream) {
1165
+ if (m._paused) responseStream.pause();
1166
+ // 写入目的流
1167
+ for (const dest of m.pipedests) m.pipeDest(dest);
1168
+
1169
+ // 接收responseStream事件,转发 到 redirectReq 发射
1170
+ for (const ev of readEvents) responseStream.on(ev, readEventEmit[ev]);
1171
+
1172
+ // @ts-ignore
1173
+ responseStream.on('data', chunk => {
1174
+ if (m.timing && !m.responseStarted) {
1175
+ m.responseStartTime = new Date().getTime();
1176
+ }
1177
+ m._destdata = true;
1178
+ m.emit('data', chunk); // 向上触发
1179
+ });
1180
+ }
1181
+
1182
+ // 可读流结束,触发 finished,方便上层清理
1183
+ // A cleanup function which removes all registered listeners.
1184
+ const offListeners = stream.finished(responseStream, () => {
1185
+ offListeners(); // cleanup
1186
+ this.emit('finished');
1187
+ });
1188
+
1189
+ return responseStream
1190
+ }
1191
+
1192
+ // Read Stream API
1193
+
1194
+ /**
1195
+ * read stream to write stream
1196
+ * pipe 只是建立连接管道,后续自动传输数据
1197
+ * @param {stream.Writable} dest
1198
+ * @param {*} opts
1199
+ * @returns {stream.Writable}
1200
+ */
1201
+ pipe(dest, opts) {
1202
+ const m = this;
1203
+
1204
+ // 请求已响应
1205
+ if (m.responseStream) {
1206
+ // 已有数据,不可pipe
1207
+ if (m._destdata) m.emit('error', new Error('You cannot pipe after data has been emitted from the response.'));
1208
+ else if (m._respended) m.emit('error', new Error('You cannot pipe after the response has been ended.'));
1209
+ else {
1210
+ // stream.Stream.prototype.pipe.call(self, dest, opts);
1211
+ super.pipe(dest, opts); // 建立连接管道,自动传输数据
1212
+ m.pipeDest(dest);
1213
+ return dest // 返回写入 stream
1214
+ }
1215
+ } else {
1216
+ // 已请求还未响应
1217
+ m.pipedests.push(dest);
1218
+ // stream.Stream.prototype.pipe.call(self, dest, opts);
1219
+ super.pipe(dest, opts); // 建立连接管道
1220
+ return dest // 返回写入 stream
1221
+ }
1222
+ }
1223
+
1224
+ /**
1225
+ * 分离先前使用pipe()方法附加的Writable流。
1226
+ * @param {stream.Writable} dest
1227
+ * @returns
1228
+ */
1229
+ unpipe(dest) {
1230
+ const m = this;
1231
+
1232
+ // 请求已响应
1233
+ if (m.responseStream) {
1234
+ // 已有数据,不可 unpipe
1235
+ if (m._destdata) m.emit('error', new Error('You cannot unpipe after data has been emitted from the response.'));
1236
+ else if (m._respended) m.emit('error', new Error('You cannot unpipe after the response has been ended.'));
1237
+ else {
1238
+ // stream.Stream.prototype.pipe.call(self, dest, opts);
1239
+ super.unpipe(dest); // 建立连接管道,自动传输数据
1240
+ m.pipedests = m.pipedests.filter(v => v !== dest);
1241
+ return m
1242
+ }
1243
+ } else {
1244
+ // 已请求还未响应
1245
+ m.pipedests = m.pipedests.filter(v => v !== dest);
1246
+ super.unpipe(dest); // 从连接管道中分离
1247
+ return m
1248
+ }
1249
+ }
1250
+
1251
+ /**
1252
+ * 收请求响应,传输数据到可写流之前,设置可写流 header
1253
+ * content-type 和 content-length,实现数据 透传,比如图片
1254
+ * 流模式透传,需设置 decompress = false,避免解压数据
1255
+ * (await req.stream('http://google.com/img.png')).pipe(await req.stream('http://mysite.com/img.png'))
1256
+ * pipe to dest
1257
+ * @param {*} dest
1258
+ */
1259
+ pipeDest(dest) {
1260
+ const m = this;
1261
+ const {response, _options} = m;
1262
+
1263
+ // Called after the response is received
1264
+ if (response?.headers && dest.headers && !dest.headersSent) {
1265
+ const caseless = new Caseless(response.headers);
1266
+ if (caseless.has('content-type')) {
1267
+ const ctname = caseless.has('content-type');
1268
+ if (dest.setHeader) {
1269
+ dest.setHeader(ctname, response.headers[ctname]);
1270
+ } else {
1271
+ dest.headers[ctname] = response.headers[ctname];
1272
+ }
1273
+ }
1274
+
1275
+ if (caseless.has('content-length')) {
1276
+ const clname = caseless.has('content-length');
1277
+ if (dest.setHeader) {
1278
+ dest.setHeader(clname, response.headers[clname]);
1279
+ } else {
1280
+ dest.headers[clname] = response.headers[clname];
1281
+ }
1282
+ }
1283
+ }
1284
+
1285
+ if (response?.headers && dest.setHeader && !dest.headersSent) {
1286
+ for (const h of response.headers) {
1287
+ dest.setHeader(h, response.headers[h]);
1288
+ }
1289
+ dest.statusCode = response.statusCode;
1290
+ }
1291
+
1292
+ // if (m.pipefilter) {
1293
+ // m.pipefilter(response, dest)
1294
+ // }
1295
+ }
1296
+
1297
+ /**
1298
+ * 暂停read流
1299
+ * @param {...any} args
1300
+ */
1301
+ pause(...args) {
1302
+ const m = this;
1303
+ // 没有流
1304
+ if (!m.responseStream) m._paused = true;
1305
+ else m.responseStream.pause(...args);
1306
+ return m
1307
+ }
1308
+
1309
+ /**
1310
+ * 继续read流
1311
+ * @param {...any} args
1312
+ */
1313
+ resume(...args) {
1314
+ const m = this;
1315
+ if (!m.responseStream) m._paused = false;
1316
+ else m.responseStream.resume(...args);
1317
+ return m
1318
+ }
1319
+
1320
+ isPaused() {
1321
+ return this._paused
1322
+ }
1323
+ }
1324
+
1325
+ /**
1326
+ *
1327
+ * @param {*} request
1328
+ * @param {*} error
1329
+ */
1330
+ function destroyRequest(request, error) {
1331
+ for (const ev of writeEvents) {
1332
+ request.removeListener(ev, writeEventEmit[ev]);
1333
+ }
1334
+ request.on('error', utils.noop);
1335
+ request.destroy(error);
1336
+ }
1337
+
1338
+ function removeMatchingHeaders(regex, headers) {
1339
+ let lastValue;
1340
+ Object.keys(headers).forEach(k => {
1341
+ if (regex.test(k)) {
1342
+ lastValue = headers[k];
1343
+ delete headers[k];
1344
+ }
1345
+ });
1346
+
1347
+ return lastValue === null || typeof lastValue === 'undefined' ? undefined : String(lastValue).trim()
1348
+ }
1349
+
1350
+ function isSubdomain(subdomain, domain) {
1351
+ assert(utils.isString(subdomain) && utils.isString(domain));
1352
+ const dot = subdomain.length - domain.length - 1;
1353
+ return dot > 0 && subdomain[dot] === '.' && subdomain.endsWith(domain)
1354
+ }
1355
+
1356
+ /**
1357
+ * from 'https://github.com/follow-redirects/follow-redirects'
1358
+ * 修改以支持http、https 代理服务器
1359
+ * 代理模式下,http or https 请求,取决于 proxy 代理服务器,而不是目的服务器。
1360
+ */
1361
+
1362
+
1363
+ const log = log$2({env: `wia:req:${name(__filename)}`})
1364
+
1365
+ // Preventive platform detection
1366
+ // istanbul ignore next
1367
+ ;(function detectUnsupportedEnvironment() {
1368
+ const looksLikeNode = typeof process !== 'undefined';
1369
+ const looksLikeBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
1370
+ const looksLikeV8 = utils.isFunction(Error.captureStackTrace);
1371
+ if (!looksLikeNode && (looksLikeBrowser || !looksLikeV8)) {
1372
+ log.warn('The follow-redirects package should be excluded from browser builds.');
1373
+ }
1374
+ })();
1375
+
1376
+ /**
1377
+ * 封装http(s),实现重定向
1378
+ * 重定向可能切换http、https
1379
+ * 支持隧道及非隧道、http(s)代理
1380
+ */
1381
+
1382
+ /**
1383
+ * 初始化参数
1384
+ * @param {*} uri
1385
+ * @param {*} options
1386
+ * @param {*} callback
1387
+ * @returns
1388
+ */
1389
+ function init(uri, options, callback) {
1390
+ try {
1391
+ // Parse parameters, ensuring that input is an object
1392
+ if (utils.isURL(uri)) uri = utils.spreadUrlObject(uri);
1393
+ else if (utils.isString(uri)) uri = utils.spreadUrlObject(utils.parseUrl(uri));
1394
+ else {
1395
+ callback = options;
1396
+ options = utils.validateUrl(uri);
1397
+ uri = {};
1398
+ }
1399
+
1400
+ if (utils.isFunction(options)) {
1401
+ callback = options;
1402
+ options = null;
1403
+ }
1404
+
1405
+ // copy options
1406
+ options = {
1407
+ ...uri,
1408
+ ...options,
1409
+ };
1410
+
1411
+ if (!utils.isString(options.host) && !utils.isString(options.hostname)) options.hostname = '::1';
1412
+
1413
+ R = {opts: options, cb: callback};
1414
+ } catch (e) {
1415
+ log.err(e, 'init');
1416
+ }
1417
+
1418
+ return R
1419
+ }
1420
+
1421
+ /**
1422
+ * Executes a request, following redirects
1423
+ * 替换原 http(s).request,参数类似
1424
+ * 注意变参 (options[, callback]) or (url[, options][, callback])
1425
+ maxRedirects: _.maxRedirects,
1426
+ maxBodyLength: _.maxBodyLength,
1427
+ * @param {*} uri/options
1428
+ * @param {*} options/callback
1429
+ * @param {*} callback/null
1430
+ * @returns
1431
+ */
1432
+ function request(uri, options, callback) {
1433
+ let R = null;
1434
+
1435
+ try {
1436
+ const {opts, cb} = init(uri, options, callback);
1437
+ // log.debug('request', {options})
1438
+ R = new Request(opts, cb);
1439
+ } catch (e) {
1440
+ log.err(e, 'request');
1441
+ }
1442
+
1443
+ return R
1444
+ }
1445
+
1446
+ /**
1447
+ * 执行简单的非stream数据请求
1448
+ * 复杂数据,请使用 @wiajs/req库(fork from axios),该库封装了当前库,提供了更多功能
1449
+ * organize params for patch, post, put, head, del
1450
+ * @param {string} verb
1451
+ * @returns {Request} Duplex 流
1452
+ */
1453
+ function fn(verb) {
1454
+ const method = verb.toUpperCase();
1455
+ return (uri, options, callback) => {
1456
+ const {opts, cb} = init(uri, options, callback);
1457
+ opts.method = method;
1458
+ const req = new Request(opts, cb);
1459
+ req.end();
1460
+ return req
1461
+ }
1462
+ }
1463
+
1464
+ // define like this to please codeintel/intellisense IDEs
1465
+ request.get = fn('get');
1466
+ request.head = fn('head');
1467
+ request.options = fn('options');
1468
+ request.post = fn('post');
1469
+ request.put = fn('put');
1470
+ request.patch = fn('patch');
1471
+ request.del = fn('delete');
1472
+ request['delete'] = fn('delete');
1473
+
1474
+ export { request as default };