got 11.0.0-beta.1 → 11.0.3

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.
@@ -22,6 +22,7 @@ const proxy_events_1 = require("./utils/proxy-events");
22
22
  const timed_out_1 = require("./utils/timed-out");
23
23
  const url_to_options_1 = require("./utils/url-to-options");
24
24
  const options_to_url_1 = require("./utils/options-to-url");
25
+ const weakable_map_1 = require("./utils/weakable-map");
25
26
  const kRequest = Symbol('request');
26
27
  const kResponse = Symbol('response');
27
28
  const kResponseSize = Symbol('responseSize');
@@ -33,6 +34,10 @@ const kUnproxyEvents = Symbol('unproxyEvents');
33
34
  const kIsFromCache = Symbol('isFromCache');
34
35
  const kCancelTimeouts = Symbol('cancelTimeouts');
35
36
  const kStartedReading = Symbol('startedReading');
37
+ const kStopReading = Symbol('stopReading');
38
+ const kTriggerRead = Symbol('triggerRead');
39
+ const kBody = Symbol('body');
40
+ const kJobs = Symbol('jobs');
36
41
  exports.kIsNormalizedAlready = Symbol('isNormalizedAlready');
37
42
  const supportsBrotli = is_1.default.string(process.versions.brotli);
38
43
  exports.withoutBody = new Set(['GET', 'HEAD']);
@@ -49,26 +54,7 @@ function validateSearchParameters(searchParameters) {
49
54
  function isClientRequest(clientRequest) {
50
55
  return is_1.default.object(clientRequest) && !('statusCode' in clientRequest);
51
56
  }
52
- const cacheFn = async (url, options) => new Promise((resolve, reject) => {
53
- // TODO: Remove `utils/url-to-options.ts` when `cacheable-request` is fixed
54
- Object.assign(options, url_to_options_1.default(url));
55
- // `http-cache-semantics` checks this
56
- delete options.url;
57
- // TODO: `cacheable-request` is incorrectly typed
58
- const cacheRequest = options.cacheableRequest(options, resolve);
59
- // Restore options
60
- options.url = url;
61
- cacheRequest.once('error', (error) => {
62
- if (error instanceof CacheableRequest.RequestError) {
63
- // TODO: `options` should be `normalizedOptions`
64
- reject(new RequestError(error.message, error, options));
65
- return;
66
- }
67
- // TODO: `options` should be `normalizedOptions`
68
- reject(new CacheError(error, options));
69
- });
70
- cacheRequest.once('request', resolve);
71
- });
57
+ const cacheableStore = new weakable_map_1.default();
72
58
  const waitForOpenFile = async (file) => new Promise((resolve, reject) => {
73
59
  const onError = (error) => {
74
60
  reject(error);
@@ -109,36 +95,34 @@ const setNonEnumerableProperties = (sources, to) => {
109
95
  Object.defineProperties(to, properties);
110
96
  };
111
97
  class RequestError extends Error {
112
- constructor(message, error, options, requestOrResponse) {
98
+ constructor(message, error, self) {
113
99
  var _a;
114
100
  super(message);
115
101
  Error.captureStackTrace(this, this.constructor);
116
102
  this.name = 'RequestError';
117
103
  this.code = error.code;
118
- Object.defineProperty(this, 'options', {
119
- // This fails because of TS 3.7.2 useDefineForClassFields
120
- // Ref: https://github.com/microsoft/TypeScript/issues/34972
121
- enumerable: false,
122
- value: options
123
- });
124
- if (requestOrResponse instanceof http_1.IncomingMessage) {
125
- Object.defineProperty(this, 'response', {
104
+ if (self instanceof Request) {
105
+ Object.defineProperty(this, 'request', {
126
106
  enumerable: false,
127
- value: requestOrResponse
107
+ value: self
128
108
  });
129
- Object.defineProperty(this, 'request', {
109
+ Object.defineProperty(this, 'response', {
130
110
  enumerable: false,
131
- value: requestOrResponse.request
111
+ value: self[kResponse]
132
112
  });
133
- }
134
- else if (requestOrResponse instanceof Request) {
135
- Object.defineProperty(this, 'request', {
113
+ Object.defineProperty(this, 'options', {
114
+ // This fails because of TS 3.7.2 useDefineForClassFields
115
+ // Ref: https://github.com/microsoft/TypeScript/issues/34972
136
116
  enumerable: false,
137
- value: requestOrResponse
117
+ value: self.options
138
118
  });
139
- Object.defineProperty(this, 'response', {
119
+ }
120
+ else {
121
+ Object.defineProperty(this, 'options', {
122
+ // This fails because of TS 3.7.2 useDefineForClassFields
123
+ // Ref: https://github.com/microsoft/TypeScript/issues/34972
140
124
  enumerable: false,
141
- value: requestOrResponse[kResponse]
125
+ value: self
142
126
  });
143
127
  }
144
128
  this.timings = (_a = this.request) === null || _a === void 0 ? void 0 : _a.timings;
@@ -157,44 +141,36 @@ class RequestError extends Error {
157
141
  }
158
142
  exports.RequestError = RequestError;
159
143
  class MaxRedirectsError extends RequestError {
160
- constructor(response, maxRedirects, options) {
161
- super(`Redirected ${maxRedirects} times. Aborting.`, {}, options);
144
+ constructor(request) {
145
+ super(`Redirected ${request.options.maxRedirects} times. Aborting.`, {}, request);
162
146
  this.name = 'MaxRedirectsError';
163
- Object.defineProperty(this, 'response', {
164
- enumerable: false,
165
- value: response
166
- });
167
147
  }
168
148
  }
169
149
  exports.MaxRedirectsError = MaxRedirectsError;
170
150
  class HTTPError extends RequestError {
171
- constructor(response, options) {
172
- super(`Response code ${response.statusCode} (${response.statusMessage})`, {}, options);
151
+ constructor(response) {
152
+ super(`Response code ${response.statusCode} (${response.statusMessage})`, {}, response.request);
173
153
  this.name = 'HTTPError';
174
- Object.defineProperty(this, 'response', {
175
- enumerable: false,
176
- value: response
177
- });
178
154
  }
179
155
  }
180
156
  exports.HTTPError = HTTPError;
181
157
  class CacheError extends RequestError {
182
- constructor(error, options) {
183
- super(error.message, error, options);
158
+ constructor(error, request) {
159
+ super(error.message, error, request);
184
160
  this.name = 'CacheError';
185
161
  }
186
162
  }
187
163
  exports.CacheError = CacheError;
188
164
  class UploadError extends RequestError {
189
- constructor(error, options, request) {
190
- super(error.message, error, options, request);
165
+ constructor(error, request) {
166
+ super(error.message, error, request);
191
167
  this.name = 'UploadError';
192
168
  }
193
169
  }
194
170
  exports.UploadError = UploadError;
195
171
  class TimeoutError extends RequestError {
196
- constructor(error, timings, options) {
197
- super(error.message, error, options);
172
+ constructor(error, timings, request) {
173
+ super(error.message, error, request);
198
174
  this.name = 'TimeoutError';
199
175
  this.event = error.event;
200
176
  this.timings = timings;
@@ -202,8 +178,8 @@ class TimeoutError extends RequestError {
202
178
  }
203
179
  exports.TimeoutError = TimeoutError;
204
180
  class ReadError extends RequestError {
205
- constructor(error, options, response) {
206
- super(error.message, error, options, response);
181
+ constructor(error, request) {
182
+ super(error.message, error, request);
207
183
  this.name = 'ReadError';
208
184
  }
209
185
  }
@@ -232,10 +208,12 @@ class Request extends stream_1.Duplex {
232
208
  });
233
209
  this[kDownloadedSize] = 0;
234
210
  this[kUploadedSize] = 0;
235
- this.finalized = false;
211
+ this.requestInitialized = false;
236
212
  this[kServerResponsesPiped] = new Set();
237
213
  this.redirects = [];
238
- this.errored = false;
214
+ this[kStopReading] = false;
215
+ this[kTriggerRead] = false;
216
+ this[kJobs] = [];
239
217
  // TODO: Remove this when targeting Node.js >= 12
240
218
  this._progressCallbacks = [];
241
219
  const unlockWrite = () => this._unlockWrite();
@@ -265,6 +243,7 @@ class Request extends stream_1.Duplex {
265
243
  this._lockWrite();
266
244
  }
267
245
  (async (nonNormalizedOptions) => {
246
+ var _a;
268
247
  try {
269
248
  if (nonNormalizedOptions.body instanceof fs_1.ReadStream) {
270
249
  await waitForOpenFile(nonNormalizedOptions.body);
@@ -284,8 +263,15 @@ class Request extends stream_1.Duplex {
284
263
  decodeURI(this.requestUrl);
285
264
  await this._finalizeBody();
286
265
  await this._makeRequest();
287
- this.finalized = true;
288
- this.emit('finalized');
266
+ if (this.destroyed) {
267
+ (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.abort();
268
+ return;
269
+ }
270
+ // Queued writes etc.
271
+ for (const job of this[kJobs]) {
272
+ job();
273
+ }
274
+ this.requestInitialized = true;
289
275
  }
290
276
  catch (error) {
291
277
  if (error instanceof RequestError) {
@@ -299,6 +285,8 @@ class Request extends stream_1.Duplex {
299
285
  static normalizeArguments(url, options, defaults) {
300
286
  var _a, _b, _c, _d;
301
287
  const rawOptions = options;
288
+ const searchParameters = options === null || options === void 0 ? void 0 : options.searchParams;
289
+ const hooks = options === null || options === void 0 ? void 0 : options.hooks;
302
290
  if (is_1.default.object(url) && !is_1.default.urlInstance(url)) {
303
291
  options = { ...defaults, ...url, ...options };
304
292
  }
@@ -311,6 +299,20 @@ class Request extends stream_1.Duplex {
311
299
  options.url = url;
312
300
  }
313
301
  }
302
+ // Prevent duplicating default search params & hooks
303
+ if (searchParameters === undefined) {
304
+ delete options.searchParams;
305
+ }
306
+ else {
307
+ options.searchParams = searchParameters;
308
+ }
309
+ if (hooks === undefined) {
310
+ delete options.hooks;
311
+ }
312
+ else {
313
+ options.hooks = hooks;
314
+ }
315
+ // Setting options to `undefined` turns off its functionalities
314
316
  if (rawOptions && defaults) {
315
317
  for (const key in rawOptions) {
316
318
  // @ts-ignore Dear TypeScript, all object keys are strings (or symbols which are NOT enumerable).
@@ -354,8 +356,11 @@ class Request extends stream_1.Duplex {
354
356
  if (is_1.default.undefined(options.headers)) {
355
357
  options.headers = {};
356
358
  }
359
+ else if (options.headers === (defaults === null || defaults === void 0 ? void 0 : defaults.headers)) {
360
+ options.headers = { ...options.headers };
361
+ }
357
362
  else {
358
- options.headers = lowercaseKeys({ ...((_a = defaults) === null || _a === void 0 ? void 0 : _a.headers), ...options.headers });
363
+ options.headers = lowercaseKeys({ ...(defaults === null || defaults === void 0 ? void 0 : defaults.headers), ...options.headers });
359
364
  }
360
365
  // Disallow legacy `url.Url`
361
366
  if ('slashes' in options) {
@@ -365,6 +370,23 @@ class Request extends stream_1.Duplex {
365
370
  if ('auth' in options) {
366
371
  throw new TypeError('Parameter `auth` is deprecated. Use `username` / `password` instead.');
367
372
  }
373
+ // `options.searchParams`
374
+ if (options.searchParams) {
375
+ if (!is_1.default.string(options.searchParams) && !(options.searchParams instanceof url_1.URLSearchParams)) {
376
+ validateSearchParameters(options.searchParams);
377
+ }
378
+ options.searchParams = new url_1.URLSearchParams(options.searchParams);
379
+ // `normalizeArguments()` is also used to merge options
380
+ (_a = defaults === null || defaults === void 0 ? void 0 : defaults.searchParams) === null || _a === void 0 ? void 0 : _a.forEach((value, key) => {
381
+ options.searchParams.append(key, value);
382
+ });
383
+ }
384
+ else {
385
+ options.searchParams = defaults === null || defaults === void 0 ? void 0 : defaults.searchParams;
386
+ }
387
+ // `options.username` & `options.password`
388
+ options.username = (_b = options.username) !== null && _b !== void 0 ? _b : '';
389
+ options.password = (_c = options.password) !== null && _c !== void 0 ? _c : '';
368
390
  // `options.prefixUrl` & `options.url`
369
391
  if (options.prefixUrl) {
370
392
  options.prefixUrl = options.prefixUrl.toString();
@@ -398,65 +420,52 @@ class Request extends stream_1.Duplex {
398
420
  },
399
421
  get: () => prefixUrl
400
422
  });
401
- // Protocol check
423
+ // Support UNIX sockets
402
424
  let { protocol } = options.url;
403
425
  if (protocol === 'unix:') {
404
426
  protocol = 'http:';
405
427
  options.url = new url_1.URL(`http://unix${options.url.pathname}${options.url.search}`);
406
428
  }
429
+ // Set search params
430
+ if (options.searchParams) {
431
+ options.url.search = options.searchParams.toString();
432
+ }
433
+ // Trigger search params normalization
407
434
  if (options.url.search) {
408
435
  const triggerSearchParameters = '_GOT_INTERNAL_TRIGGER_NORMALIZATION';
409
436
  options.url.searchParams.append(triggerSearchParameters, '');
410
437
  options.url.searchParams.delete(triggerSearchParameters);
411
438
  }
439
+ // Protocol check
412
440
  if (protocol !== 'http:' && protocol !== 'https:') {
413
441
  throw new UnsupportedProtocolError(options);
414
442
  }
415
- }
416
- // `options.username` & `options.password`
417
- options.username = (_b = options.username, (_b !== null && _b !== void 0 ? _b : ''));
418
- options.password = (_c = options.password, (_c !== null && _c !== void 0 ? _c : ''));
419
- if (options.url) {
443
+ // Update `username` & `password`
420
444
  options.url.username = options.username;
421
445
  options.url.password = options.password;
422
446
  }
423
447
  // `options.cookieJar`
424
- if (options.cookieJar) {
425
- let { setCookie, getCookieString } = options.cookieJar;
426
- // Horrible `tough-cookie` check
448
+ const { cookieJar } = options;
449
+ if (cookieJar) {
450
+ let { setCookie, getCookieString } = cookieJar;
451
+ is_1.assert.function_(setCookie);
452
+ is_1.assert.function_(getCookieString);
453
+ /* istanbul ignore next: Horrible `tough-cookie` v3 check */
427
454
  if (setCookie.length === 4 && getCookieString.length === 0) {
428
455
  setCookie = util_1.promisify(setCookie.bind(options.cookieJar));
429
456
  getCookieString = util_1.promisify(getCookieString.bind(options.cookieJar));
430
- }
431
- else if (setCookie.length !== 2) {
432
- throw new TypeError('`options.cookieJar.setCookie` needs to be an async function with 2 arguments');
433
- }
434
- else if (getCookieString.length !== 1) {
435
- throw new TypeError('`options.cookieJar.getCookieString` needs to be an async function with 1 argument');
436
- }
437
- options.cookieJar = { setCookie, getCookieString };
438
- }
439
- // `options.searchParams`
440
- if (options.searchParams) {
441
- if (!is_1.default.string(options.searchParams) && !(options.searchParams instanceof url_1.URLSearchParams)) {
442
- validateSearchParameters(options.searchParams);
443
- }
444
- options.searchParams = new url_1.URLSearchParams(options.searchParams);
445
- // `normalizeArguments()` is also used to merge options
446
- const defaultsAsOptions = defaults;
447
- if (defaultsAsOptions && defaultsAsOptions.searchParams instanceof url_1.URLSearchParams) {
448
- defaultsAsOptions.searchParams.forEach((value, key) => {
449
- options.searchParams.append(key, value);
450
- });
451
- }
452
- if (options.url) {
453
- options.url.search = options.searchParams.toString();
457
+ options.cookieJar = {
458
+ setCookie,
459
+ getCookieString
460
+ };
454
461
  }
455
462
  }
456
463
  // `options.cache`
457
- if (options.cache && !options.cacheableRequest) {
458
- // Better memory management, so we don't have to generate a new object every time
459
- options.cacheableRequest = new CacheableRequest(((requestOptions, handler) => requestOptions[kRequest](requestOptions, handler)), options.cache);
464
+ const { cache } = options;
465
+ if (cache) {
466
+ if (!cacheableStore.has(cache)) {
467
+ cacheableStore.set(cache, new CacheableRequest(((requestOptions, handler) => requestOptions[kRequest](requestOptions, handler)), cache));
468
+ }
460
469
  }
461
470
  // `options.dnsCache`
462
471
  if (options.dnsCache === true) {
@@ -469,7 +478,7 @@ class Request extends stream_1.Duplex {
469
478
  if (is_1.default.number(options.timeout)) {
470
479
  options.timeout = { request: options.timeout };
471
480
  }
472
- else if (defaults) {
481
+ else if (defaults && options.timeout !== defaults.timeout) {
473
482
  options.timeout = {
474
483
  ...defaults.timeout,
475
484
  ...options.timeout
@@ -521,7 +530,7 @@ class Request extends stream_1.Duplex {
521
530
  }
522
531
  }
523
532
  }
524
- options.maxRedirects = (_d = options.maxRedirects, (_d !== null && _d !== void 0 ? _d : 0));
533
+ options.maxRedirects = (_d = options.maxRedirects) !== null && _d !== void 0 ? _d : 0;
525
534
  // Set non-enumerable properties
526
535
  setNonEnumerableProperties([defaults, options], options);
527
536
  return options;
@@ -571,20 +580,21 @@ class Request extends stream_1.Duplex {
571
580
  if (is_form_data_1.default(options.body) && noContentType) {
572
581
  headers['content-type'] = `multipart/form-data; boundary=${options.body.getBoundary()}`;
573
582
  }
583
+ this[kBody] = options.body;
574
584
  }
575
585
  else if (isForm) {
576
586
  if (noContentType) {
577
587
  headers['content-type'] = 'application/x-www-form-urlencoded';
578
588
  }
579
- options.body = (new url_1.URLSearchParams(options.form)).toString();
589
+ this[kBody] = (new url_1.URLSearchParams(options.form)).toString();
580
590
  }
581
591
  else {
582
592
  if (noContentType) {
583
593
  headers['content-type'] = 'application/json';
584
594
  }
585
- options.body = JSON.stringify(options.json);
595
+ this[kBody] = JSON.stringify(options.json);
586
596
  }
587
- const uploadBodySize = await get_body_size_1.default(options);
597
+ const uploadBodySize = await get_body_size_1.default(this[kBody], options.headers);
588
598
  // See https://tools.ietf.org/html/rfc7230#section-3.3.2
589
599
  // A user agent SHOULD send a Content-Length in a request message when
590
600
  // no Transfer-Encoding is sent and the request method defines a meaning
@@ -617,7 +627,7 @@ class Request extends stream_1.Duplex {
617
627
  }
618
628
  const statusCode = response.statusCode;
619
629
  const typedResponse = response;
620
- typedResponse.statusMessage = typedResponse.statusMessage === '' ? http.STATUS_CODES[statusCode] : typedResponse.statusMessage;
630
+ typedResponse.statusMessage = typedResponse.statusMessage ? typedResponse.statusMessage : http.STATUS_CODES[statusCode];
621
631
  typedResponse.url = options.url.toString();
622
632
  typedResponse.requestUrl = this.requestUrl;
623
633
  typedResponse.redirectUrls = this.redirects;
@@ -631,8 +641,17 @@ class Request extends stream_1.Duplex {
631
641
  this[kResponseSize] = this[kDownloadedSize];
632
642
  this.emit('downloadProgress', this.downloadProgress);
633
643
  });
634
- response.on('error', (error) => {
635
- this._beforeError(new ReadError(error, options, response));
644
+ response.once('error', (error) => {
645
+ this._beforeError(new ReadError(error, this));
646
+ });
647
+ response.once('aborted', () => {
648
+ if (this.aborted) {
649
+ return;
650
+ }
651
+ this._beforeError(new ReadError({
652
+ name: 'Error',
653
+ message: 'The server aborted the pending request'
654
+ }, this));
636
655
  });
637
656
  this.emit('downloadProgress', this.downloadProgress);
638
657
  const rawCookies = response.headers['set-cookie'];
@@ -676,7 +695,7 @@ class Request extends stream_1.Duplex {
676
695
  }
677
696
  }
678
697
  if (this.redirects.length >= options.maxRedirects) {
679
- this._beforeError(new MaxRedirectsError(typedResponse, options.maxRedirects, options));
698
+ this._beforeError(new MaxRedirectsError(this));
680
699
  return;
681
700
  }
682
701
  try {
@@ -716,14 +735,13 @@ class Request extends stream_1.Duplex {
716
735
  const limitStatusCode = options.followRedirect ? 299 : 399;
717
736
  const isOk = (statusCode >= 200 && statusCode <= limitStatusCode) || statusCode === 304;
718
737
  if (options.throwHttpErrors && !isOk) {
719
- await this._beforeError(new HTTPError(typedResponse, options));
738
+ await this._beforeError(new HTTPError(typedResponse));
720
739
  if (this.destroyed) {
721
740
  return;
722
741
  }
723
742
  }
724
- // We need to call `_read()` only when the Request stream is flowing
725
743
  response.on('readable', () => {
726
- if (this.readableFlowing) {
744
+ if (this[kTriggerRead]) {
727
745
  this._read();
728
746
  }
729
747
  });
@@ -762,10 +780,10 @@ class Request extends stream_1.Duplex {
762
780
  });
763
781
  request.once('error', (error) => {
764
782
  if (error instanceof timed_out_1.TimeoutError) {
765
- error = new TimeoutError(error, this.timings, options);
783
+ error = new TimeoutError(error, this.timings, this);
766
784
  }
767
785
  else {
768
- error = new RequestError(error.message, error, options, this);
786
+ error = new RequestError(error.message, error, this);
769
787
  }
770
788
  this._beforeError(error);
771
789
  });
@@ -773,20 +791,21 @@ class Request extends stream_1.Duplex {
773
791
  this[kRequest] = request;
774
792
  this.emit('uploadProgress', this.uploadProgress);
775
793
  // Send body
794
+ const body = this[kBody];
776
795
  const currentRequest = this.redirects.length === 0 ? this : request;
777
- if (is_1.default.nodeStream(options.body)) {
778
- options.body.pipe(currentRequest);
779
- options.body.once('error', (error) => {
780
- this._beforeError(new UploadError(error, options, this));
796
+ if (is_1.default.nodeStream(body)) {
797
+ body.pipe(currentRequest);
798
+ body.once('error', (error) => {
799
+ this._beforeError(new UploadError(error, this));
781
800
  });
782
- options.body.once('end', () => {
801
+ body.once('end', () => {
783
802
  delete options.body;
784
803
  });
785
804
  }
786
805
  else {
787
806
  this._unlockWrite();
788
- if (!is_1.default.undefined(options.body)) {
789
- this._writeRequest(options.body, null, () => { });
807
+ if (!is_1.default.undefined(body)) {
808
+ this._writeRequest(body, null, () => { });
790
809
  currentRequest.end();
791
810
  this._lockWrite();
792
811
  }
@@ -797,8 +816,28 @@ class Request extends stream_1.Duplex {
797
816
  }
798
817
  this.emit('request', request);
799
818
  }
819
+ async _createCacheableRequest(url, options) {
820
+ return new Promise((resolve, reject) => {
821
+ // TODO: Remove `utils/url-to-options.ts` when `cacheable-request` is fixed
822
+ Object.assign(options, url_to_options_1.default(url));
823
+ // `http-cache-semantics` checks this
824
+ delete options.url;
825
+ // This is ugly
826
+ const cacheRequest = cacheableStore.get(options.cache)(options, resolve);
827
+ // Restore options
828
+ options.url = url;
829
+ cacheRequest.once('error', (error) => {
830
+ if (error instanceof CacheableRequest.CacheError) {
831
+ reject(new CacheError(error, this));
832
+ return;
833
+ }
834
+ reject(error);
835
+ });
836
+ cacheRequest.once('request', resolve);
837
+ });
838
+ }
800
839
  async _makeRequest() {
801
- var _a, _b;
840
+ var _a;
802
841
  const { options } = this;
803
842
  const { url, headers, request, agent, timeout } = options;
804
843
  for (const key in headers) {
@@ -835,7 +874,7 @@ class Request extends stream_1.Duplex {
835
874
  // UNIX sockets
836
875
  if (url.hostname === 'unix') {
837
876
  const matches = /(?<socketPath>.+?):(?<path>.+)/.exec(`${url.pathname}${url.search}`);
838
- if ((_a = matches) === null || _a === void 0 ? void 0 : _a.groups) {
877
+ if (matches === null || matches === void 0 ? void 0 : matches.groups) {
839
878
  const { socketPath, path } = matches.groups;
840
879
  Object.assign(options, {
841
880
  socketPath,
@@ -852,8 +891,8 @@ class Request extends stream_1.Duplex {
852
891
  else {
853
892
  fallbackFn = isHttps ? https.request : http.request;
854
893
  }
855
- const realFn = (_b = options.request, (_b !== null && _b !== void 0 ? _b : fallbackFn));
856
- const fn = options.cacheableRequest ? cacheFn : realFn;
894
+ const realFn = (_a = options.request) !== null && _a !== void 0 ? _a : fallbackFn;
895
+ const fn = options.cache ? this._createCacheableRequest.bind(this) : realFn;
857
896
  if (agent && !options.http2) {
858
897
  options.agent = agent[isHttps ? 'https' : 'http'];
859
898
  }
@@ -890,21 +929,20 @@ class Request extends stream_1.Duplex {
890
929
  if (error instanceof RequestError) {
891
930
  throw error;
892
931
  }
893
- throw new RequestError(error.message, error, options, this);
932
+ throw new RequestError(error.message, error, this);
894
933
  }
895
934
  }
896
935
  async _beforeError(error) {
897
- this.errored = true;
936
+ this[kStopReading] = true;
898
937
  if (!(error instanceof RequestError)) {
899
- error = new RequestError(error.message, error, this.options, this);
938
+ error = new RequestError(error.message, error, this);
900
939
  }
901
940
  try {
902
941
  const { response } = error;
903
- if (response && is_1.default.undefined(response.body)) {
904
- response.body = await getStream(response, {
905
- ...this.options,
906
- encoding: this._readableState.encoding
907
- });
942
+ if (response) {
943
+ response.setEncoding(this._readableState.encoding);
944
+ response.rawBody = await getStream.buffer(response);
945
+ response.body = response.rawBody.toString();
908
946
  }
909
947
  }
910
948
  catch (_) { }
@@ -915,14 +953,21 @@ class Request extends stream_1.Duplex {
915
953
  }
916
954
  }
917
955
  catch (error_) {
918
- error = new RequestError(error_.message, error_, this.options, this);
956
+ error = new RequestError(error_.message, error_, this);
919
957
  }
920
958
  this.destroy(error);
921
959
  }
922
960
  _read() {
923
- if (kResponse in this && !this.errored) {
961
+ this[kTriggerRead] = true;
962
+ const response = this[kResponse];
963
+ if (response && !this[kStopReading]) {
964
+ // We cannot put this in the `if` above
965
+ // because `.read()` also triggers the `end` event
966
+ if (response.readableLength) {
967
+ this[kTriggerRead] = false;
968
+ }
924
969
  let data;
925
- while ((data = this[kResponse].read()) !== null) {
970
+ while ((data = response.read()) !== null) {
926
971
  this[kDownloadedSize] += data.length;
927
972
  this[kStartedReading] = true;
928
973
  const progress = this.downloadProgress;
@@ -937,11 +982,11 @@ class Request extends stream_1.Duplex {
937
982
  const write = () => {
938
983
  this._writeRequest(chunk, encoding, callback);
939
984
  };
940
- if (this.finalized) {
985
+ if (this.requestInitialized) {
941
986
  write();
942
987
  }
943
988
  else {
944
- this.once('finalized', write);
989
+ this[kJobs].push(write);
945
990
  }
946
991
  }
947
992
  _writeRequest(chunk, encoding, callback) {
@@ -952,6 +997,7 @@ class Request extends stream_1.Duplex {
952
997
  this.emit('uploadProgress', progress);
953
998
  }
954
999
  });
1000
+ // TODO: What happens if it's from cache? Then this[kRequest] won't be defined.
955
1001
  this[kRequest].write(chunk, encoding, (error) => {
956
1002
  if (!error && this._progressCallbacks.length !== 0) {
957
1003
  this._progressCallbacks.shift()();
@@ -980,26 +1026,26 @@ class Request extends stream_1.Duplex {
980
1026
  callback(error);
981
1027
  });
982
1028
  };
983
- if (this.finalized) {
1029
+ if (this.requestInitialized) {
984
1030
  endRequest();
985
1031
  }
986
1032
  else {
987
- this.once('finalized', endRequest);
1033
+ this[kJobs].push(endRequest);
988
1034
  }
989
1035
  }
990
1036
  _destroy(error, callback) {
1037
+ var _a;
991
1038
  if (kRequest in this) {
992
- this[kRequest].abort();
993
- }
994
- else {
995
- this.once('finalized', () => {
996
- if (kRequest in this) {
997
- this[kRequest].abort();
998
- }
999
- });
1039
+ this[kCancelTimeouts]();
1040
+ // TODO: Remove the next `if` when these get fixed:
1041
+ // - https://github.com/nodejs/node/issues/32851
1042
+ // - https://github.com/nock/nock/issues/1981
1043
+ if (!((_a = this[kResponse]) === null || _a === void 0 ? void 0 : _a.complete) && !this[kRequest].destroyed) {
1044
+ this[kRequest].abort();
1045
+ }
1000
1046
  }
1001
1047
  if (error !== null && !is_1.default.undefined(error) && !(error instanceof RequestError)) {
1002
- error = new RequestError(error.message, error, this.options, this);
1048
+ error = new RequestError(error.message, error, this);
1003
1049
  }
1004
1050
  callback(error);
1005
1051
  }
@@ -1011,6 +1057,10 @@ class Request extends stream_1.Duplex {
1011
1057
  var _a;
1012
1058
  return Boolean((_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.aborted);
1013
1059
  }
1060
+ get socket() {
1061
+ var _a;
1062
+ return (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.socket;
1063
+ }
1014
1064
  get downloadProgress() {
1015
1065
  let percent;
1016
1066
  if (this[kResponseSize]) {