got 11.0.0-beta.1 → 11.0.0

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.
@@ -1,14 +1,13 @@
1
1
  /// <reference types="node" />
2
2
  import { URL } from 'url';
3
- import { Options, NormalizedOptions, Defaults, ResponseType } from './types';
4
- import Request, { RequestError } from '../core';
3
+ import { Options, NormalizedOptions, Defaults, ResponseType, Response } from './types';
4
+ import Request from '../core';
5
5
  export declare const knownBodyTypes: string[];
6
- export declare const parseBody: (body: Buffer, responseType: ResponseType, encoding?: string | undefined) => unknown;
6
+ export declare const parseBody: (response: Response<unknown>, responseType: ResponseType, encoding?: string | undefined) => unknown;
7
7
  export default class PromisableRequest extends Request {
8
8
  ['constructor']: typeof PromisableRequest;
9
9
  options: NormalizedOptions;
10
- _throwHttpErrors: boolean;
11
10
  static normalizeArguments(url?: string | URL, nonNormalizedOptions?: Options, defaults?: Defaults): NormalizedOptions;
12
11
  static mergeOptions(...sources: Options[]): NormalizedOptions;
13
- _beforeError(error: RequestError): Promise<void>;
12
+ _beforeError(error: Error): Promise<void>;
14
13
  }
@@ -1,24 +1,31 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const is_1 = require("@sindresorhus/is");
4
+ const types_1 = require("./types");
4
5
  const core_1 = require("../core");
5
6
  if (!core_1.knownHookEvents.includes('beforeRetry')) {
6
7
  core_1.knownHookEvents.push('beforeRetry', 'afterResponse');
7
8
  }
8
9
  exports.knownBodyTypes = ['json', 'buffer', 'text'];
9
10
  // @ts-ignore The error is: Not all code paths return a value.
10
- exports.parseBody = (body, responseType, encoding) => {
11
- if (responseType === 'text') {
12
- return body.toString(encoding);
13
- }
14
- if (responseType === 'json') {
15
- return body.length === 0 ? '' : JSON.parse(body.toString());
16
- }
17
- if (responseType === 'buffer') {
18
- return Buffer.from(body);
11
+ exports.parseBody = (response, responseType, encoding) => {
12
+ const { rawBody } = response;
13
+ try {
14
+ if (responseType === 'text') {
15
+ return rawBody.toString(encoding);
16
+ }
17
+ if (responseType === 'json') {
18
+ return rawBody.length === 0 ? '' : JSON.parse(rawBody.toString());
19
+ }
20
+ if (responseType === 'buffer') {
21
+ return Buffer.from(rawBody);
22
+ }
23
+ if (!exports.knownBodyTypes.includes(responseType)) {
24
+ throw new TypeError(`Unknown body type '${responseType}'`);
25
+ }
19
26
  }
20
- if (!exports.knownBodyTypes.includes(responseType)) {
21
- throw new TypeError(`Unknown body type '${responseType}'`);
27
+ catch (error) {
28
+ throw new types_1.ParseError(error, response);
22
29
  }
23
30
  };
24
31
  class PromisableRequest extends core_1.default {
@@ -83,6 +90,10 @@ class PromisableRequest extends core_1.default {
83
90
  throw new Error('`options.pagination.paginate` must be implemented');
84
91
  }
85
92
  }
93
+ // JSON mode
94
+ if (options.responseType === 'json' && options.headers.accept === undefined) {
95
+ options.headers.accept = 'application/json';
96
+ }
86
97
  return options;
87
98
  }
88
99
  static mergeOptions(...sources) {
@@ -93,7 +104,9 @@ class PromisableRequest extends core_1.default {
93
104
  return mergedOptions;
94
105
  }
95
106
  async _beforeError(error) {
96
- const isHTTPError = error instanceof core_1.HTTPError;
107
+ if (!(error instanceof core_1.RequestError)) {
108
+ error = new core_1.RequestError(error.message, error, this);
109
+ }
97
110
  try {
98
111
  for (const hook of this.options.hooks.beforeError) {
99
112
  // eslint-disable-next-line no-await-in-loop
@@ -101,15 +114,11 @@ class PromisableRequest extends core_1.default {
101
114
  }
102
115
  }
103
116
  catch (error_) {
104
- this.destroy(error_);
117
+ this.destroy(new core_1.RequestError(error_.message, error_, this));
105
118
  return;
106
119
  }
107
- if (this._throwHttpErrors && !isHTTPError) {
108
- this.destroy(error);
109
- }
110
- else {
111
- this.emit('error', error);
112
- }
120
+ // Let the promise decide whether to abort or not
121
+ this.emit('error', error);
113
122
  }
114
123
  }
115
124
  exports.default = PromisableRequest;
@@ -20,22 +20,23 @@ const proxiedRequestEvents = [
20
20
  ];
21
21
  function asPromise(options) {
22
22
  let retryCount = 0;
23
- let body;
23
+ let globalRequest;
24
+ let globalResponse;
24
25
  const emitter = new events_1.EventEmitter();
25
26
  const promise = new PCancelable((resolve, reject, onCancel) => {
26
27
  const makeRequest = () => {
27
- if (options.responseType === 'json' && options.headers.accept === undefined) {
28
- options.headers.accept = 'application/json';
29
- }
30
28
  // Support retries
29
+ // `options.throwHttpErrors` needs to be always true,
30
+ // so the HTTP errors are caught and the request is retried.
31
+ // The error is **eventually** thrown if the user value is true.
31
32
  const { throwHttpErrors } = options;
32
33
  if (!throwHttpErrors) {
33
34
  options.throwHttpErrors = true;
34
35
  }
35
36
  const request = new core_1.default(options.url, options);
36
- request._throwHttpErrors = throwHttpErrors;
37
37
  request._noPipe = true;
38
38
  onCancel(() => request.destroy());
39
+ globalRequest = request;
39
40
  request.once('response', async (response) => {
40
41
  response.retryCount = retryCount;
41
42
  if (response.request.aborted) {
@@ -48,23 +49,24 @@ function asPromise(options) {
48
49
  return (statusCode >= 200 && statusCode <= limitStatusCode) || statusCode === 304;
49
50
  };
50
51
  // Download body
52
+ let rawBody;
51
53
  try {
52
- body = await getStream.buffer(request);
54
+ rawBody = await getStream.buffer(request);
55
+ response.rawBody = rawBody;
53
56
  }
54
57
  catch (error) {
55
- request._beforeError(new types_1.ReadError(error, options, response));
58
+ request._beforeError(new types_1.ReadError(error, request));
56
59
  return;
57
60
  }
58
61
  // Parse body
59
62
  try {
60
- response.body = core_1.parseBody(body, options.responseType, options.encoding);
63
+ response.body = core_1.parseBody(response, options.responseType, options.encoding);
61
64
  }
62
65
  catch (error) {
63
66
  // Fallback to `utf8`
64
- response.body = body.toString('utf8');
67
+ response.body = rawBody.toString();
65
68
  if (isOk()) {
66
- const parseError = new types_1.ParseError(error, response, options);
67
- request._beforeError(parseError);
69
+ request._beforeError(error);
68
70
  return;
69
71
  }
70
72
  }
@@ -103,9 +105,10 @@ function asPromise(options) {
103
105
  return;
104
106
  }
105
107
  if (throwHttpErrors && !isOk()) {
106
- reject(new types_1.HTTPError(response, options));
108
+ reject(new types_1.HTTPError(response));
107
109
  return;
108
110
  }
111
+ globalResponse = response;
109
112
  resolve(options.resolveBodyOnly ? response.body : response);
110
113
  });
111
114
  request.once('error', (error) => {
@@ -134,7 +137,7 @@ function asPromise(options) {
134
137
  catch (error_) {
135
138
  // Don't emit the `response` event
136
139
  request.destroy();
137
- reject(new types_1.RequestError(error_.message, error, request.options));
140
+ reject(new types_1.RequestError(error_.message, error, request));
138
141
  return;
139
142
  }
140
143
  if (backoff) {
@@ -151,7 +154,7 @@ function asPromise(options) {
151
154
  catch (error_) {
152
155
  // Don't emit the `response` event
153
156
  request.destroy();
154
- reject(new types_1.RequestError(error_.message, error, request.options));
157
+ reject(new types_1.RequestError(error_.message, error, request));
155
158
  return;
156
159
  }
157
160
  makeRequest();
@@ -179,14 +182,15 @@ function asPromise(options) {
179
182
  };
180
183
  const shortcut = (responseType) => {
181
184
  const newPromise = (async () => {
185
+ // Wait until downloading has ended
182
186
  await promise;
183
- return core_1.parseBody(body, responseType);
187
+ return core_1.parseBody(globalResponse, responseType, options.encoding);
184
188
  })();
185
189
  Object.defineProperties(newPromise, Object.getOwnPropertyDescriptors(promise));
186
190
  return newPromise;
187
191
  };
188
192
  promise.json = () => {
189
- if (body === undefined && options.headers.accept === undefined) {
193
+ if (!globalRequest.writableFinished && options.headers.accept === undefined) {
190
194
  options.headers.accept = 'application/json';
191
195
  }
192
196
  return shortcut('json');
@@ -64,7 +64,7 @@ export interface Defaults extends RequestDefaults {
64
64
  }
65
65
  export declare class ParseError extends RequestError {
66
66
  readonly response: Response;
67
- constructor(error: Error, response: Response, options: NormalizedOptions);
67
+ constructor(error: Error, response: Response);
68
68
  }
69
69
  export interface CancelableRequest<T extends Response | Response['body'] = Response['body']> extends PCancelable<T>, RequestEvents<CancelableRequest<T>> {
70
70
  json<ReturnType>(): CancelableRequest<ReturnType>;
@@ -14,8 +14,9 @@ exports.HTTPError = core_1.HTTPError;
14
14
  exports.ReadError = core_1.ReadError;
15
15
  exports.UnsupportedProtocolError = core_1.UnsupportedProtocolError;
16
16
  class ParseError extends core_1.RequestError {
17
- constructor(error, response, options) {
18
- super(`${error.message} in "${options.url.toString()}"`, error, options);
17
+ constructor(error, response) {
18
+ const { options } = response.request;
19
+ super(`${error.message} in "${options.url.toString()}"`, error, response.request);
19
20
  this.name = 'ParseError';
20
21
  Object.defineProperty(this, 'response', {
21
22
  enumerable: false,
@@ -25,6 +25,8 @@ declare const kUnproxyEvents: unique symbol;
25
25
  declare const kIsFromCache: unique symbol;
26
26
  declare const kCancelTimeouts: unique symbol;
27
27
  declare const kStartedReading: unique symbol;
28
+ declare const kStopReading: unique symbol;
29
+ declare const kTriggerRead: unique symbol;
28
30
  export declare const kIsNormalizedAlready: unique symbol;
29
31
  export interface Agents {
30
32
  http?: http.Agent;
@@ -120,7 +122,6 @@ export interface NormalizedOptions extends Options {
120
122
  cache?: string | CacheableRequest.StorageAdapter;
121
123
  throwHttpErrors: boolean;
122
124
  dnsCache?: CacheableLookup;
123
- cacheableRequest?: (options: string | URL | http.RequestOptions, callback?: (response: http.ServerResponse | ResponseLike) => void) => CacheableRequest.Emitter;
124
125
  http2: boolean;
125
126
  allowGetBody: boolean;
126
127
  rejectUnauthorized: boolean;
@@ -174,6 +175,7 @@ export interface PlainResponse extends IncomingMessageWithTimings {
174
175
  }
175
176
  export interface Response<T = unknown> extends PlainResponse {
176
177
  body: T;
178
+ rawBody: Buffer;
177
179
  retryCount: number;
178
180
  }
179
181
  export interface RequestEvents<T> {
@@ -191,29 +193,29 @@ export declare class RequestError extends Error {
191
193
  readonly timings?: Timings;
192
194
  constructor(message: string, error: Partial<Error & {
193
195
  code?: string;
194
- }>, options: NormalizedOptions, requestOrResponse?: Request | Response);
196
+ }>, self: Request | NormalizedOptions);
195
197
  }
196
198
  export declare class MaxRedirectsError extends RequestError {
197
199
  readonly response: Response;
198
- constructor(response: Response, maxRedirects: number, options: NormalizedOptions);
200
+ constructor(request: Request);
199
201
  }
200
202
  export declare class HTTPError extends RequestError {
201
203
  readonly response: Response;
202
- constructor(response: Response, options: NormalizedOptions);
204
+ constructor(response: Response);
203
205
  }
204
206
  export declare class CacheError extends RequestError {
205
- constructor(error: Error, options: NormalizedOptions);
207
+ constructor(error: Error, request: Request);
206
208
  }
207
209
  export declare class UploadError extends RequestError {
208
- constructor(error: Error, options: NormalizedOptions, request: Request);
210
+ constructor(error: Error, request: Request);
209
211
  }
210
212
  export declare class TimeoutError extends RequestError {
211
213
  readonly timings: Timings;
212
214
  readonly event: string;
213
- constructor(error: TimedOutTimeoutError, timings: Timings, options: NormalizedOptions);
215
+ constructor(error: TimedOutTimeoutError, timings: Timings, request: Request);
214
216
  }
215
217
  export declare class ReadError extends RequestError {
216
- constructor(error: Error, options: NormalizedOptions, response: Response);
218
+ constructor(error: Error, request: Request);
217
219
  }
218
220
  export declare class UnsupportedProtocolError extends RequestError {
219
221
  constructor(options: NormalizedOptions);
@@ -224,6 +226,8 @@ export default class Request extends Duplex implements RequestEvents<Request> {
224
226
  _cannotHaveBody: boolean;
225
227
  [kDownloadedSize]: number;
226
228
  [kUploadedSize]: number;
229
+ [kStopReading]: boolean;
230
+ [kTriggerRead]: boolean;
227
231
  [kBodySize]?: number;
228
232
  [kServerResponsesPiped]: Set<ServerResponse>;
229
233
  [kIsFromCache]?: boolean;
@@ -238,7 +242,6 @@ export default class Request extends Duplex implements RequestEvents<Request> {
238
242
  requestUrl: string;
239
243
  finalized: boolean;
240
244
  redirects: string[];
241
- errored: boolean;
242
245
  constructor(url: string | URL, options?: Options, defaults?: Defaults);
243
246
  static normalizeArguments(url?: string | URL, options?: Options, defaults?: Defaults): NormalizedOptions;
244
247
  _lockWrite(): void;
@@ -246,6 +249,7 @@ export default class Request extends Duplex implements RequestEvents<Request> {
246
249
  _finalizeBody(): Promise<void>;
247
250
  _onResponse(response: IncomingMessage): Promise<void>;
248
251
  _onRequest(request: ClientRequest): void;
252
+ _createCacheableRequest(url: URL, options: RequestOptions): Promise<ClientRequest | ResponseLike>;
249
253
  _makeRequest(): Promise<void>;
250
254
  _beforeError(error: Error): Promise<void>;
251
255
  _read(): void;
@@ -255,6 +259,7 @@ export default class Request extends Duplex implements RequestEvents<Request> {
255
259
  _destroy(error: Error | null, callback: (error: Error | null) => void): void;
256
260
  get ip(): string | undefined;
257
261
  get aborted(): boolean;
262
+ get socket(): Socket | undefined;
258
263
  get downloadProgress(): Progress;
259
264
  get uploadProgress(): Progress;
260
265
  get timings(): Timings | undefined;
@@ -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,8 @@ 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');
36
39
  exports.kIsNormalizedAlready = Symbol('isNormalizedAlready');
37
40
  const supportsBrotli = is_1.default.string(process.versions.brotli);
38
41
  exports.withoutBody = new Set(['GET', 'HEAD']);
@@ -49,26 +52,7 @@ function validateSearchParameters(searchParameters) {
49
52
  function isClientRequest(clientRequest) {
50
53
  return is_1.default.object(clientRequest) && !('statusCode' in clientRequest);
51
54
  }
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
- });
55
+ const cacheableStore = new weakable_map_1.default();
72
56
  const waitForOpenFile = async (file) => new Promise((resolve, reject) => {
73
57
  const onError = (error) => {
74
58
  reject(error);
@@ -109,36 +93,34 @@ const setNonEnumerableProperties = (sources, to) => {
109
93
  Object.defineProperties(to, properties);
110
94
  };
111
95
  class RequestError extends Error {
112
- constructor(message, error, options, requestOrResponse) {
96
+ constructor(message, error, self) {
113
97
  var _a;
114
98
  super(message);
115
99
  Error.captureStackTrace(this, this.constructor);
116
100
  this.name = 'RequestError';
117
101
  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', {
102
+ if (self instanceof Request) {
103
+ Object.defineProperty(this, 'request', {
126
104
  enumerable: false,
127
- value: requestOrResponse
105
+ value: self
128
106
  });
129
- Object.defineProperty(this, 'request', {
107
+ Object.defineProperty(this, 'response', {
130
108
  enumerable: false,
131
- value: requestOrResponse.request
109
+ value: self[kResponse]
132
110
  });
133
- }
134
- else if (requestOrResponse instanceof Request) {
135
- Object.defineProperty(this, 'request', {
111
+ Object.defineProperty(this, 'options', {
112
+ // This fails because of TS 3.7.2 useDefineForClassFields
113
+ // Ref: https://github.com/microsoft/TypeScript/issues/34972
136
114
  enumerable: false,
137
- value: requestOrResponse
115
+ value: self.options
138
116
  });
139
- Object.defineProperty(this, 'response', {
117
+ }
118
+ else {
119
+ Object.defineProperty(this, 'options', {
120
+ // This fails because of TS 3.7.2 useDefineForClassFields
121
+ // Ref: https://github.com/microsoft/TypeScript/issues/34972
140
122
  enumerable: false,
141
- value: requestOrResponse[kResponse]
123
+ value: self
142
124
  });
143
125
  }
144
126
  this.timings = (_a = this.request) === null || _a === void 0 ? void 0 : _a.timings;
@@ -157,44 +139,36 @@ class RequestError extends Error {
157
139
  }
158
140
  exports.RequestError = RequestError;
159
141
  class MaxRedirectsError extends RequestError {
160
- constructor(response, maxRedirects, options) {
161
- super(`Redirected ${maxRedirects} times. Aborting.`, {}, options);
142
+ constructor(request) {
143
+ super(`Redirected ${request.options.maxRedirects} times. Aborting.`, {}, request);
162
144
  this.name = 'MaxRedirectsError';
163
- Object.defineProperty(this, 'response', {
164
- enumerable: false,
165
- value: response
166
- });
167
145
  }
168
146
  }
169
147
  exports.MaxRedirectsError = MaxRedirectsError;
170
148
  class HTTPError extends RequestError {
171
- constructor(response, options) {
172
- super(`Response code ${response.statusCode} (${response.statusMessage})`, {}, options);
149
+ constructor(response) {
150
+ super(`Response code ${response.statusCode} (${response.statusMessage})`, {}, response.request);
173
151
  this.name = 'HTTPError';
174
- Object.defineProperty(this, 'response', {
175
- enumerable: false,
176
- value: response
177
- });
178
152
  }
179
153
  }
180
154
  exports.HTTPError = HTTPError;
181
155
  class CacheError extends RequestError {
182
- constructor(error, options) {
183
- super(error.message, error, options);
156
+ constructor(error, request) {
157
+ super(error.message, error, request);
184
158
  this.name = 'CacheError';
185
159
  }
186
160
  }
187
161
  exports.CacheError = CacheError;
188
162
  class UploadError extends RequestError {
189
- constructor(error, options, request) {
190
- super(error.message, error, options, request);
163
+ constructor(error, request) {
164
+ super(error.message, error, request);
191
165
  this.name = 'UploadError';
192
166
  }
193
167
  }
194
168
  exports.UploadError = UploadError;
195
169
  class TimeoutError extends RequestError {
196
- constructor(error, timings, options) {
197
- super(error.message, error, options);
170
+ constructor(error, timings, request) {
171
+ super(error.message, error, request);
198
172
  this.name = 'TimeoutError';
199
173
  this.event = error.event;
200
174
  this.timings = timings;
@@ -202,8 +176,8 @@ class TimeoutError extends RequestError {
202
176
  }
203
177
  exports.TimeoutError = TimeoutError;
204
178
  class ReadError extends RequestError {
205
- constructor(error, options, response) {
206
- super(error.message, error, options, response);
179
+ constructor(error, request) {
180
+ super(error.message, error, request);
207
181
  this.name = 'ReadError';
208
182
  }
209
183
  }
@@ -235,7 +209,8 @@ class Request extends stream_1.Duplex {
235
209
  this.finalized = false;
236
210
  this[kServerResponsesPiped] = new Set();
237
211
  this.redirects = [];
238
- this.errored = false;
212
+ this[kStopReading] = false;
213
+ this[kTriggerRead] = false;
239
214
  // TODO: Remove this when targeting Node.js >= 12
240
215
  this._progressCallbacks = [];
241
216
  const unlockWrite = () => this._unlockWrite();
@@ -421,20 +396,20 @@ class Request extends stream_1.Duplex {
421
396
  options.url.password = options.password;
422
397
  }
423
398
  // `options.cookieJar`
424
- if (options.cookieJar) {
425
- let { setCookie, getCookieString } = options.cookieJar;
426
- // Horrible `tough-cookie` check
399
+ const { cookieJar } = options;
400
+ if (cookieJar) {
401
+ let { setCookie, getCookieString } = cookieJar;
402
+ is_1.assert.function_(setCookie);
403
+ is_1.assert.function_(getCookieString);
404
+ /* istanbul ignore next: Horrible `tough-cookie` v3 check */
427
405
  if (setCookie.length === 4 && getCookieString.length === 0) {
428
406
  setCookie = util_1.promisify(setCookie.bind(options.cookieJar));
429
407
  getCookieString = util_1.promisify(getCookieString.bind(options.cookieJar));
408
+ options.cookieJar = {
409
+ setCookie: setCookie.bind(cookieJar),
410
+ getCookieString: getCookieString.bind(getCookieString)
411
+ };
430
412
  }
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
413
  }
439
414
  // `options.searchParams`
440
415
  if (options.searchParams) {
@@ -454,9 +429,11 @@ class Request extends stream_1.Duplex {
454
429
  }
455
430
  }
456
431
  // `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);
432
+ const { cache } = options;
433
+ if (cache) {
434
+ if (!cacheableStore.has(cache)) {
435
+ cacheableStore.set(cache, new CacheableRequest(((requestOptions, handler) => requestOptions[kRequest](requestOptions, handler)), cache));
436
+ }
460
437
  }
461
438
  // `options.dnsCache`
462
439
  if (options.dnsCache === true) {
@@ -632,7 +609,16 @@ class Request extends stream_1.Duplex {
632
609
  this.emit('downloadProgress', this.downloadProgress);
633
610
  });
634
611
  response.on('error', (error) => {
635
- this._beforeError(new ReadError(error, options, response));
612
+ this._beforeError(new ReadError(error, this));
613
+ });
614
+ response.once('aborted', () => {
615
+ if (this.aborted) {
616
+ return;
617
+ }
618
+ this._beforeError(new ReadError({
619
+ name: 'Error',
620
+ message: 'The server aborted the pending request'
621
+ }, this));
636
622
  });
637
623
  this.emit('downloadProgress', this.downloadProgress);
638
624
  const rawCookies = response.headers['set-cookie'];
@@ -676,7 +662,7 @@ class Request extends stream_1.Duplex {
676
662
  }
677
663
  }
678
664
  if (this.redirects.length >= options.maxRedirects) {
679
- this._beforeError(new MaxRedirectsError(typedResponse, options.maxRedirects, options));
665
+ this._beforeError(new MaxRedirectsError(this));
680
666
  return;
681
667
  }
682
668
  try {
@@ -716,14 +702,13 @@ class Request extends stream_1.Duplex {
716
702
  const limitStatusCode = options.followRedirect ? 299 : 399;
717
703
  const isOk = (statusCode >= 200 && statusCode <= limitStatusCode) || statusCode === 304;
718
704
  if (options.throwHttpErrors && !isOk) {
719
- await this._beforeError(new HTTPError(typedResponse, options));
705
+ await this._beforeError(new HTTPError(typedResponse));
720
706
  if (this.destroyed) {
721
707
  return;
722
708
  }
723
709
  }
724
- // We need to call `_read()` only when the Request stream is flowing
725
710
  response.on('readable', () => {
726
- if (this.readableFlowing) {
711
+ if (this[kTriggerRead]) {
727
712
  this._read();
728
713
  }
729
714
  });
@@ -762,10 +747,10 @@ class Request extends stream_1.Duplex {
762
747
  });
763
748
  request.once('error', (error) => {
764
749
  if (error instanceof timed_out_1.TimeoutError) {
765
- error = new TimeoutError(error, this.timings, options);
750
+ error = new TimeoutError(error, this.timings, this);
766
751
  }
767
752
  else {
768
- error = new RequestError(error.message, error, options, this);
753
+ error = new RequestError(error.message, error, this);
769
754
  }
770
755
  this._beforeError(error);
771
756
  });
@@ -777,7 +762,7 @@ class Request extends stream_1.Duplex {
777
762
  if (is_1.default.nodeStream(options.body)) {
778
763
  options.body.pipe(currentRequest);
779
764
  options.body.once('error', (error) => {
780
- this._beforeError(new UploadError(error, options, this));
765
+ this._beforeError(new UploadError(error, this));
781
766
  });
782
767
  options.body.once('end', () => {
783
768
  delete options.body;
@@ -797,6 +782,26 @@ class Request extends stream_1.Duplex {
797
782
  }
798
783
  this.emit('request', request);
799
784
  }
785
+ async _createCacheableRequest(url, options) {
786
+ return new Promise((resolve, reject) => {
787
+ // TODO: Remove `utils/url-to-options.ts` when `cacheable-request` is fixed
788
+ Object.assign(options, url_to_options_1.default(url));
789
+ // `http-cache-semantics` checks this
790
+ delete options.url;
791
+ // This is ugly
792
+ const cacheRequest = cacheableStore.get(options.cache)(options, resolve);
793
+ // Restore options
794
+ options.url = url;
795
+ cacheRequest.once('error', (error) => {
796
+ if (error instanceof CacheableRequest.CacheError) {
797
+ reject(new CacheError(error, this));
798
+ return;
799
+ }
800
+ reject(error);
801
+ });
802
+ cacheRequest.once('request', resolve);
803
+ });
804
+ }
800
805
  async _makeRequest() {
801
806
  var _a, _b;
802
807
  const { options } = this;
@@ -853,7 +858,7 @@ class Request extends stream_1.Duplex {
853
858
  fallbackFn = isHttps ? https.request : http.request;
854
859
  }
855
860
  const realFn = (_b = options.request, (_b !== null && _b !== void 0 ? _b : fallbackFn));
856
- const fn = options.cacheableRequest ? cacheFn : realFn;
861
+ const fn = options.cache ? this._createCacheableRequest.bind(this) : realFn;
857
862
  if (agent && !options.http2) {
858
863
  options.agent = agent[isHttps ? 'https' : 'http'];
859
864
  }
@@ -890,21 +895,20 @@ class Request extends stream_1.Duplex {
890
895
  if (error instanceof RequestError) {
891
896
  throw error;
892
897
  }
893
- throw new RequestError(error.message, error, options, this);
898
+ throw new RequestError(error.message, error, this);
894
899
  }
895
900
  }
896
901
  async _beforeError(error) {
897
- this.errored = true;
902
+ this[kStopReading] = true;
898
903
  if (!(error instanceof RequestError)) {
899
- error = new RequestError(error.message, error, this.options, this);
904
+ error = new RequestError(error.message, error, this);
900
905
  }
901
906
  try {
902
907
  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
- });
908
+ if (response) {
909
+ response.setEncoding(this._readableState.encoding);
910
+ response.rawBody = await getStream.buffer(response);
911
+ response.body = response.rawBody.toString();
908
912
  }
909
913
  }
910
914
  catch (_) { }
@@ -915,14 +919,21 @@ class Request extends stream_1.Duplex {
915
919
  }
916
920
  }
917
921
  catch (error_) {
918
- error = new RequestError(error_.message, error_, this.options, this);
922
+ error = new RequestError(error_.message, error_, this);
919
923
  }
920
924
  this.destroy(error);
921
925
  }
922
926
  _read() {
923
- if (kResponse in this && !this.errored) {
927
+ this[kTriggerRead] = true;
928
+ const response = this[kResponse];
929
+ if (response && !this[kStopReading]) {
930
+ // We cannot put this in the `if` above
931
+ // because `.read()` also triggers the `end` event
932
+ if (response.readableLength) {
933
+ this[kTriggerRead] = false;
934
+ }
924
935
  let data;
925
- while ((data = this[kResponse].read()) !== null) {
936
+ while ((data = response.read()) !== null) {
926
937
  this[kDownloadedSize] += data.length;
927
938
  this[kStartedReading] = true;
928
939
  const progress = this.downloadProgress;
@@ -988,8 +999,12 @@ class Request extends stream_1.Duplex {
988
999
  }
989
1000
  }
990
1001
  _destroy(error, callback) {
1002
+ var _a;
991
1003
  if (kRequest in this) {
992
- this[kRequest].abort();
1004
+ // TODO: Remove the next `if` when https://github.com/nodejs/node/issues/32851 gets fixed
1005
+ if (!((_a = this[kResponse]) === null || _a === void 0 ? void 0 : _a.complete)) {
1006
+ this[kRequest].abort();
1007
+ }
993
1008
  }
994
1009
  else {
995
1010
  this.once('finalized', () => {
@@ -999,7 +1014,7 @@ class Request extends stream_1.Duplex {
999
1014
  });
1000
1015
  }
1001
1016
  if (error !== null && !is_1.default.undefined(error) && !(error instanceof RequestError)) {
1002
- error = new RequestError(error.message, error, this.options, this);
1017
+ error = new RequestError(error.message, error, this);
1003
1018
  }
1004
1019
  callback(error);
1005
1020
  }
@@ -1011,6 +1026,10 @@ class Request extends stream_1.Duplex {
1011
1026
  var _a;
1012
1027
  return Boolean((_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.aborted);
1013
1028
  }
1029
+ get socket() {
1030
+ var _a;
1031
+ return (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.socket;
1032
+ }
1014
1033
  get downloadProgress() {
1015
1034
  let percent;
1016
1035
  if (this[kResponseSize]) {
@@ -0,0 +1,8 @@
1
+ export default class WeakableMap<K, V> {
2
+ weakMap: WeakMap<object, V>;
3
+ map: Map<K, V>;
4
+ constructor();
5
+ set(key: K, value: V): void;
6
+ get(key: K): V | undefined;
7
+ has(key: K): boolean;
8
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ class WeakableMap {
4
+ constructor() {
5
+ this.weakMap = new WeakMap();
6
+ this.map = new Map();
7
+ }
8
+ set(key, value) {
9
+ if (typeof key === 'object') {
10
+ this.weakMap.set(key, value);
11
+ }
12
+ else {
13
+ this.map.set(key, value);
14
+ }
15
+ }
16
+ get(key) {
17
+ if (typeof key === 'object') {
18
+ return this.weakMap.get(key);
19
+ }
20
+ return this.map.get(key);
21
+ }
22
+ has(key) {
23
+ if (typeof key === 'object') {
24
+ return this.weakMap.has(key);
25
+ }
26
+ return this.map.has(key);
27
+ }
28
+ }
29
+ exports.default = WeakableMap;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "got",
3
- "version": "11.0.0-beta.1",
3
+ "version": "11.0.0",
4
4
  "description": "Human-friendly and powerful HTTP request library for Node.js",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/got",
@@ -66,7 +66,7 @@
66
66
  "@types/node-fetch": "^2.5.5",
67
67
  "@types/request": "^2.48.4",
68
68
  "@types/sinon": "^9.0.0",
69
- "@types/tough-cookie": "^2.3.5",
69
+ "@types/tough-cookie": "^4.0.0",
70
70
  "@typescript-eslint/eslint-plugin": "^2.27.0",
71
71
  "@typescript-eslint/parser": "^2.27.0",
72
72
  "ava": "^3.6.0",
@@ -89,7 +89,7 @@
89
89
  "slow-stream": "0.0.4",
90
90
  "tempy": "^0.5.0",
91
91
  "to-readable-stream": "^2.1.0",
92
- "tough-cookie": "^3.0.0",
92
+ "tough-cookie": "^4.0.0",
93
93
  "typescript": "3.7.5",
94
94
  "xo": "^0.29.0"
95
95
  },
package/readme.md CHANGED
@@ -784,6 +784,12 @@ Type: `string | object | Buffer` *(Depending on `options.responseType`)*
784
784
 
785
785
  The result of the request.
786
786
 
787
+ ##### rawBody
788
+
789
+ Type: `Buffer`
790
+
791
+ The raw result of the request.
792
+
787
793
  ##### url
788
794
 
789
795
  Type: `string`
@@ -934,6 +940,10 @@ The same as `response.timings`.
934
940
 
935
941
  The same as `response.isFromCache`.
936
942
 
943
+ ##### .socket
944
+
945
+ The same as `response.socket`.
946
+
937
947
  ##### .on('error', error)
938
948
 
939
949
  The emitted `error` is an instance of [`RequestError`](#got.requesterror).
@@ -1039,7 +1049,7 @@ const mergedHandlers = got.extend({
1039
1049
 
1040
1050
  ```js
1041
1051
  const handler = (options, next) => {
1042
- if (options.stream) {
1052
+ if (options.isStream) {
1043
1053
  // It's a Stream
1044
1054
  return next(options);
1045
1055
  }
@@ -1311,7 +1321,7 @@ stream.destroy();
1311
1321
  <a name="cache-adapters"></a>
1312
1322
  ## Cache
1313
1323
 
1314
- Got implements [RFC 7234](http://httpwg.org/specs/rfc7234.html) compliant HTTP caching which works out of the box in-memory and is easily pluggable with a wide range of storage adapters. Fresh cache entries are served directly from the cache, and stale cache entries are revalidated with `If-None-Match`/`If-Modified-Since` headers. You can read more about the underlying cache behavior in the [`cacheable-request` documentation](https://github.com/lukechilds/cacheable-request). For DNS cache, Got uses [`cacheable-lookup`](https://github.com/szmarczak/cacheable-lookup).
1324
+ Got implements [RFC 7234](https://httpwg.org/specs/rfc7234.html) compliant HTTP caching which works out of the box in-memory and is easily pluggable with a wide range of storage adapters. Fresh cache entries are served directly from the cache, and stale cache entries are revalidated with `If-None-Match`/`If-Modified-Since` headers. You can read more about the underlying cache behavior in the [`cacheable-request` documentation](https://github.com/lukechilds/cacheable-request). For DNS cache, Got uses [`cacheable-lookup`](https://github.com/szmarczak/cacheable-lookup).
1315
1325
 
1316
1326
  You can use the JavaScript `Map` type as an in-memory cache:
1317
1327