urllib 3.0.0-alpha.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.
@@ -1,17 +1,46 @@
1
1
  "use strict";
2
- var _BlobFromStream_stream, _BlobFromStream_type;
2
+ var _a, _b, _c;
3
+ var _BlobFromStream_stream, _BlobFromStream_type, _HttpClient_instances, _HttpClient_defaultArgs, _HttpClient_dispatcher, _HttpClient_requestInternal;
3
4
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.HttpClient = void 0;
5
+ exports.HttpClient = exports.HEADER_USER_AGENT = void 0;
5
6
  const tslib_1 = require("tslib");
6
7
  const events_1 = require("events");
7
8
  const util_1 = require("util");
8
- const stream_1 = require("stream");
9
+ const zlib_1 = require("zlib");
9
10
  const buffer_1 = require("buffer");
10
- const fs_1 = require("fs");
11
+ const stream_1 = require("stream");
12
+ const stream_2 = tslib_1.__importDefault(require("stream"));
11
13
  const path_1 = require("path");
14
+ const fs_1 = require("fs");
15
+ const perf_hooks_1 = require("perf_hooks");
12
16
  const undici_1 = require("undici");
17
+ const formdata_node_1 = require("formdata-node");
18
+ const form_data_encoder_1 = require("form-data-encoder");
13
19
  const default_user_agent_1 = tslib_1.__importDefault(require("default-user-agent"));
14
20
  const mime_types_1 = tslib_1.__importDefault(require("mime-types"));
21
+ const pump_1 = tslib_1.__importDefault(require("pump"));
22
+ const HttpAgent_1 = require("./HttpAgent");
23
+ const utils_1 = require("./utils");
24
+ const FormData = undici_1.FormData !== null && undici_1.FormData !== void 0 ? undici_1.FormData : formdata_node_1.FormData;
25
+ // impl isReadable on Node.js 14
26
+ const isReadable = (_a = stream_2.default.isReadable) !== null && _a !== void 0 ? _a : function isReadable(stream) {
27
+ return stream && typeof stream.read === 'function';
28
+ };
29
+ // impl promise pipeline on Node.js 14
30
+ const pipelinePromise = (_c = (_b = stream_2.default.promises) === null || _b === void 0 ? void 0 : _b.pipeline) !== null && _c !== void 0 ? _c : function pipeline(...args) {
31
+ return new Promise((resolve, reject) => {
32
+ (0, pump_1.default)(...args, (err) => {
33
+ if (err)
34
+ return reject(err);
35
+ resolve();
36
+ });
37
+ });
38
+ };
39
+ function noop() {
40
+ // noop
41
+ }
42
+ const MAX_REQURE_ID_VALUE = Math.pow(2, 31) - 10;
43
+ let globalRequestId = 0;
15
44
  const debug = (0, util_1.debuglog)('urllib');
16
45
  // https://github.com/octet-stream/form-data
17
46
  class BlobFromStream {
@@ -39,7 +68,7 @@ class HttpClientRequestTimeoutError extends Error {
39
68
  Error.captureStackTrace(this, this.constructor);
40
69
  }
41
70
  }
42
- const HEADER_USER_AGENT = (0, default_user_agent_1.default)('node-urllib', '3.0.0');
71
+ exports.HEADER_USER_AGENT = (0, default_user_agent_1.default)('node-urllib', '3.0.0');
43
72
  function getFileName(stream) {
44
73
  const filePath = stream.path;
45
74
  if (filePath) {
@@ -47,224 +76,391 @@ function getFileName(stream) {
47
76
  }
48
77
  return '';
49
78
  }
79
+ function defaultIsRetry(response) {
80
+ return response.status >= 500;
81
+ }
82
+ function performanceTime(startTime) {
83
+ return Math.floor((perf_hooks_1.performance.now() - startTime) * 1000) / 1000;
84
+ }
50
85
  class HttpClient extends events_1.EventEmitter {
51
86
  constructor(clientOptions) {
52
87
  super();
53
- this.defaultArgs = clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.defaultArgs;
88
+ _HttpClient_instances.add(this);
89
+ _HttpClient_defaultArgs.set(this, void 0);
90
+ _HttpClient_dispatcher.set(this, void 0);
91
+ tslib_1.__classPrivateFieldSet(this, _HttpClient_defaultArgs, clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.defaultArgs, "f");
92
+ if ((clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.lookup) || (clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.checkAddress) || (clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.connect)) {
93
+ tslib_1.__classPrivateFieldSet(this, _HttpClient_dispatcher, new HttpAgent_1.HttpAgent({
94
+ lookup: clientOptions.lookup,
95
+ checkAddress: clientOptions.checkAddress,
96
+ connect: clientOptions.connect,
97
+ }), "f");
98
+ }
54
99
  }
55
100
  async request(url, options) {
56
- var _a, _b, _c, _d;
57
- const requestUrl = typeof url === 'string' ? new URL(url) : url;
58
- const args = {
59
- ...this.defaultArgs,
60
- ...options,
61
- emitter: this,
62
- };
63
- const requestStartTime = Date.now();
64
- // keep urllib createCallbackResponse style
65
- const resHeaders = {};
66
- const res = {
67
- status: -1,
68
- statusCode: -1,
69
- statusMessage: '',
70
- headers: resHeaders,
71
- size: 0,
72
- aborted: false,
73
- rt: 0,
74
- keepAliveSocket: true,
75
- requestUrls: [url.toString()],
76
- timing: {
77
- contentDownload: 0,
78
- },
79
- // remoteAddress: remoteAddress,
80
- // remotePort: remotePort,
81
- // socketHandledRequests: socketHandledRequests,
82
- // socketHandledResponses: socketHandledResponses,
101
+ return await tslib_1.__classPrivateFieldGet(this, _HttpClient_instances, "m", _HttpClient_requestInternal).call(this, url, options);
102
+ }
103
+ }
104
+ exports.HttpClient = HttpClient;
105
+ _HttpClient_defaultArgs = new WeakMap(), _HttpClient_dispatcher = new WeakMap(), _HttpClient_instances = new WeakSet(), _HttpClient_requestInternal = async function _HttpClient_requestInternal(url, options, requestContext) {
106
+ var _a, _b, _c, _d, _e, _f;
107
+ if (globalRequestId >= MAX_REQURE_ID_VALUE) {
108
+ globalRequestId = 0;
109
+ }
110
+ const requestId = ++globalRequestId;
111
+ const requestUrl = typeof url === 'string' ? new URL(url) : url;
112
+ const args = {
113
+ retry: 0,
114
+ ...tslib_1.__classPrivateFieldGet(this, _HttpClient_defaultArgs, "f"),
115
+ ...options,
116
+ };
117
+ requestContext = {
118
+ retries: 0,
119
+ ...requestContext,
120
+ };
121
+ const requestStartTime = perf_hooks_1.performance.now();
122
+ const reqMeta = {
123
+ requestId,
124
+ url: requestUrl.href,
125
+ args,
126
+ ctx: args.ctx,
127
+ };
128
+ // keep urllib createCallbackResponse style
129
+ const resHeaders = {};
130
+ const res = {
131
+ status: -1,
132
+ statusCode: -1,
133
+ headers: resHeaders,
134
+ size: 0,
135
+ aborted: false,
136
+ rt: 0,
137
+ keepAliveSocket: true,
138
+ requestUrls: [],
139
+ timing: {
140
+ waiting: 0,
141
+ contentDownload: 0,
142
+ },
143
+ };
144
+ let headersTimeout = 5000;
145
+ let bodyTimeout = 5000;
146
+ if (args.timeout) {
147
+ if (Array.isArray(args.timeout)) {
148
+ headersTimeout = (_a = args.timeout[0]) !== null && _a !== void 0 ? _a : headersTimeout;
149
+ bodyTimeout = (_b = args.timeout[1]) !== null && _b !== void 0 ? _b : bodyTimeout;
150
+ }
151
+ else {
152
+ headersTimeout = bodyTimeout = args.timeout;
153
+ }
154
+ }
155
+ const method = ((_c = args.method) !== null && _c !== void 0 ? _c : 'GET').toUpperCase();
156
+ const headers = {};
157
+ if (args.headers) {
158
+ // convert headers to lower-case
159
+ for (const name in args.headers) {
160
+ headers[name.toLowerCase()] = args.headers[name];
161
+ }
162
+ }
163
+ // hidden user-agent
164
+ const hiddenUserAgent = 'user-agent' in headers && !headers['user-agent'];
165
+ if (hiddenUserAgent) {
166
+ delete headers['user-agent'];
167
+ }
168
+ else if (!headers['user-agent']) {
169
+ // need to set user-agent
170
+ headers['user-agent'] = exports.HEADER_USER_AGENT;
171
+ }
172
+ // Alias to dataType = 'stream'
173
+ if (args.streaming || args.customResponse) {
174
+ args.dataType = 'stream';
175
+ }
176
+ if (args.dataType === 'json' && !headers.accept) {
177
+ headers.accept = 'application/json';
178
+ }
179
+ // gzip alias to compressed
180
+ if (args.gzip && args.compressed !== false) {
181
+ args.compressed = true;
182
+ }
183
+ if (args.compressed && !headers['accept-encoding']) {
184
+ headers['accept-encoding'] = 'gzip, br';
185
+ }
186
+ if (requestContext.retries > 0) {
187
+ headers['x-urllib-retry'] = `${requestContext.retries}/${args.retry}`;
188
+ }
189
+ if (args.auth && !headers.authorization) {
190
+ headers.authorization = `Basic ${Buffer.from(args.auth).toString('base64')}`;
191
+ }
192
+ let opaque = args.opaque;
193
+ try {
194
+ const requestOptions = {
195
+ method,
196
+ keepalive: true,
197
+ maxRedirections: (_d = args.maxRedirects) !== null && _d !== void 0 ? _d : 10,
198
+ headersTimeout,
199
+ bodyTimeout,
200
+ opaque,
201
+ dispatcher: tslib_1.__classPrivateFieldGet(this, _HttpClient_dispatcher, "f"),
83
202
  };
84
- let requestTimeout = 5000;
85
- if (args.timeout) {
86
- if (Array.isArray(args.timeout)) {
87
- requestTimeout = (_a = args.timeout[args.timeout.length - 1]) !== null && _a !== void 0 ? _a : requestTimeout;
88
- }
89
- else {
90
- requestTimeout = args.timeout;
91
- }
203
+ if (args.followRedirect === false) {
204
+ requestOptions.maxRedirections = 0;
92
205
  }
93
- const requestTimeoutController = new AbortController();
94
- const requestTimerId = setTimeout(() => requestTimeoutController.abort(), requestTimeout);
95
- const method = ((_b = args.method) !== null && _b !== void 0 ? _b : 'GET').toUpperCase();
96
- try {
97
- const headers = new undici_1.Headers((_c = args.headers) !== null && _c !== void 0 ? _c : {});
98
- // don't set user-agent
99
- const disableUserAgent = args.headers &&
100
- (args.headers['User-Agent'] === null || args.headers['user-agent'] === null);
101
- if (!disableUserAgent && !headers.has('user-agent')) {
102
- // need to set user-agent
103
- headers.set('user-agent', HEADER_USER_AGENT);
206
+ const isGETOrHEAD = requestOptions.method === 'GET' || requestOptions.method === 'HEAD';
207
+ // alias to args.content
208
+ if (args.stream && !args.content) {
209
+ args.content = args.stream;
210
+ }
211
+ if (args.files) {
212
+ if (isGETOrHEAD) {
213
+ requestOptions.method = 'POST';
104
214
  }
105
- if (args.dataType === 'json' && !headers.has('accept')) {
106
- headers.set('accept', 'application/json');
215
+ const formData = new FormData();
216
+ const uploadFiles = [];
217
+ if (Array.isArray(args.files)) {
218
+ for (const [index, file] of args.files.entries()) {
219
+ const field = index === 0 ? 'file' : `file${index}`;
220
+ uploadFiles.push([field, file]);
221
+ }
107
222
  }
108
- const requestOptions = {
109
- method,
110
- keepalive: true,
111
- signal: requestTimeoutController.signal,
112
- };
113
- if (args.followRedirect === false) {
114
- requestOptions.redirect = 'manual';
223
+ else if (args.files instanceof stream_1.Readable || isReadable(args.files)) {
224
+ uploadFiles.push(['file', args.files]);
115
225
  }
116
- const isGETOrHEAD = requestOptions.method === 'GET' || requestOptions.method === 'HEAD';
117
- if (args.files) {
118
- if (isGETOrHEAD) {
119
- requestOptions.method = 'POST';
226
+ else if (typeof args.files === 'string' || Buffer.isBuffer(args.files)) {
227
+ uploadFiles.push(['file', args.files]);
228
+ }
229
+ else if (typeof args.files === 'object') {
230
+ for (const field in args.files) {
231
+ uploadFiles.push([field, args.files[field]]);
120
232
  }
121
- const formData = new undici_1.FormData();
122
- const uploadFiles = [];
123
- if (Array.isArray(args.files)) {
124
- for (const [index, file] of args.files.entries()) {
125
- const field = index === 0 ? 'file' : `file${index}`;
126
- uploadFiles.push([field, file]);
127
- }
233
+ }
234
+ // set normal fields first
235
+ if (args.data) {
236
+ for (const field in args.data) {
237
+ formData.append(field, args.data[field]);
128
238
  }
129
- else if (args.files instanceof stream_1.Readable || (0, stream_1.isReadable)(args.files)) {
130
- uploadFiles.push(['file', args.files]);
239
+ }
240
+ for (const [index, [field, file]] of uploadFiles.entries()) {
241
+ if (typeof file === 'string') {
242
+ // FIXME: support non-ascii filename
243
+ // const fileName = encodeURIComponent(basename(file));
244
+ // formData.append(field, await fileFromPath(file, `utf-8''${fileName}`, { type: mime.lookup(fileName) || '' }));
245
+ const fileName = (0, path_1.basename)(file);
246
+ const fileReadable = (0, fs_1.createReadStream)(file);
247
+ formData.append(field, new BlobFromStream(fileReadable, mime_types_1.default.lookup(fileName) || ''), fileName);
131
248
  }
132
- else if (typeof args.files === 'string' || Buffer.isBuffer(args.files)) {
133
- uploadFiles.push(['file', args.files]);
249
+ else if (Buffer.isBuffer(file)) {
250
+ formData.append(field, new buffer_1.Blob([file]), `bufferfile${index}`);
134
251
  }
135
- else if (typeof args.files === 'object') {
136
- for (const field in args.files) {
137
- uploadFiles.push([field, args.files[field]]);
138
- }
252
+ else if (file instanceof stream_1.Readable || isReadable(file)) {
253
+ const fileName = getFileName(file) || `streamfile${index}`;
254
+ formData.append(field, new BlobFromStream(file, mime_types_1.default.lookup(fileName) || ''), fileName);
139
255
  }
140
- // set normal fields first
141
- if (args.data) {
142
- for (const field in args.data) {
143
- formData.append(field, args.data[field]);
144
- }
256
+ }
257
+ if (undici_1.FormData) {
258
+ requestOptions.body = formData;
259
+ }
260
+ else {
261
+ // Node.js 14 does not support spec-compliant FormData
262
+ // https://github.com/octet-stream/form-data#usage
263
+ const encoder = new form_data_encoder_1.FormDataEncoder(formData);
264
+ Object.assign(headers, encoder.headers);
265
+ // fix "Content-Length":"NaN"
266
+ delete headers['Content-Length'];
267
+ requestOptions.body = stream_1.Readable.from(encoder);
268
+ }
269
+ }
270
+ else if (args.content) {
271
+ if (!isGETOrHEAD) {
272
+ // handle content
273
+ requestOptions.body = args.content;
274
+ if (args.contentType) {
275
+ headers['content-type'] = args.contentType;
145
276
  }
146
- for (const [index, [field, file]] of uploadFiles.entries()) {
147
- if (typeof file === 'string') {
148
- // FIXME: support non-ascii filename
149
- // const fileName = encodeURIComponent(basename(file));
150
- // formData.append(field, await fileFromPath(file, `utf-8''${fileName}`, { type: mime.lookup(fileName) || '' }));
151
- const fileName = (0, path_1.basename)(file);
152
- const fileReader = (0, fs_1.createReadStream)(file);
153
- formData.append(field, new BlobFromStream(fileReader, mime_types_1.default.lookup(fileName) || ''), fileName);
154
- }
155
- else if (Buffer.isBuffer(file)) {
156
- formData.append(field, new buffer_1.Blob([file]), `bufferfile${index}`);
157
- }
158
- else if (file instanceof stream_1.Readable || (0, stream_1.isReadable)(file)) {
159
- const fileName = getFileName(file) || `streamfile${index}`;
160
- formData.append(field, new BlobFromStream(file, mime_types_1.default.lookup(fileName) || ''), fileName);
161
- }
277
+ if (typeof args.content === 'string' && !headers['content-type']) {
278
+ headers['content-type'] = 'text/plain;charset=UTF-8';
162
279
  }
163
- requestOptions.body = formData;
164
280
  }
165
- else if (args.content) {
166
- if (!isGETOrHEAD) {
167
- if ((0, stream_1.isReadable)(args.content)) {
168
- // disable keepalive
169
- requestOptions.keepalive = false;
170
- }
171
- // handle content
172
- requestOptions.body = args.content;
173
- if (args.contentType) {
174
- headers.set('content-type', args.contentType);
281
+ }
282
+ else if (args.data) {
283
+ const isStringOrBufferOrReadable = typeof args.data === 'string'
284
+ || Buffer.isBuffer(args.data)
285
+ || isReadable(args.data);
286
+ if (isGETOrHEAD) {
287
+ if (!isStringOrBufferOrReadable) {
288
+ for (const field in args.data) {
289
+ requestUrl.searchParams.append(field, args.data[field]);
175
290
  }
176
291
  }
177
292
  }
178
- else if (args.data) {
179
- const isStringOrBufferOrReadable = typeof args.data === 'string'
180
- || Buffer.isBuffer(args.data)
181
- || (0, stream_1.isReadable)(args.data);
182
- if (isGETOrHEAD) {
183
- if (!isStringOrBufferOrReadable) {
184
- for (const field in args.data) {
185
- requestUrl.searchParams.append(field, args.data[field]);
186
- }
187
- }
293
+ else {
294
+ if (isStringOrBufferOrReadable) {
295
+ requestOptions.body = args.data;
188
296
  }
189
297
  else {
190
- if (isStringOrBufferOrReadable) {
191
- if ((0, stream_1.isReadable)(args.data)) {
192
- // disable keepalive
193
- requestOptions.keepalive = false;
298
+ if (args.contentType === 'json'
299
+ || args.contentType === 'application/json'
300
+ || ((_e = headers['content-type']) === null || _e === void 0 ? void 0 : _e.startsWith('application/json'))) {
301
+ requestOptions.body = JSON.stringify(args.data);
302
+ if (!headers['content-type']) {
303
+ headers['content-type'] = 'application/json';
194
304
  }
195
- requestOptions.body = args.data;
196
305
  }
197
306
  else {
198
- if (args.contentType === 'json'
199
- || args.contentType === 'application/json'
200
- || ((_d = headers.get('content-type')) === null || _d === void 0 ? void 0 : _d.startsWith('application/json'))) {
201
- requestOptions.body = JSON.stringify(args.data);
202
- if (!headers.has('content-type')) {
203
- headers.set('content-type', 'application/json');
204
- }
205
- }
206
- else {
207
- requestOptions.body = new URLSearchParams(args.data);
208
- }
307
+ headers['content-type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
308
+ requestOptions.body = new URLSearchParams(args.data).toString();
209
309
  }
210
310
  }
211
311
  }
212
- debug('%s %s, headers: %j, timeout: %s', requestOptions.method, url, headers, requestTimeout);
213
- requestOptions.headers = headers;
214
- const response = await (0, undici_1.fetch)(requestUrl, requestOptions);
215
- for (const [name, value] of response.headers) {
216
- res.headers[name] = value;
312
+ }
313
+ debug('Request#%d %s %s, headers: %j, headersTimeout: %s, bodyTimeout: %s', requestId, requestOptions.method, requestUrl.href, headers, headersTimeout, bodyTimeout);
314
+ requestOptions.headers = headers;
315
+ if (this.listenerCount('request') > 0) {
316
+ this.emit('request', reqMeta);
317
+ }
318
+ const response = await (0, undici_1.request)(requestUrl, requestOptions);
319
+ opaque = response.opaque;
320
+ if (args.timing) {
321
+ res.timing.waiting = performanceTime(requestStartTime);
322
+ }
323
+ const context = response.context;
324
+ let lastUrl = '';
325
+ if (context === null || context === void 0 ? void 0 : context.history) {
326
+ for (const urlObject of context === null || context === void 0 ? void 0 : context.history) {
327
+ res.requestUrls.push(urlObject.href);
328
+ lastUrl = urlObject.href;
329
+ }
330
+ }
331
+ else {
332
+ res.requestUrls.push(requestUrl.href);
333
+ lastUrl = requestUrl.href;
334
+ }
335
+ const contentEncoding = response.headers['content-encoding'];
336
+ const isCompressedContent = contentEncoding === 'gzip' || contentEncoding === 'br';
337
+ res.headers = response.headers;
338
+ res.status = res.statusCode = response.statusCode;
339
+ if (res.headers['content-length']) {
340
+ res.size = parseInt(res.headers['content-length']);
341
+ }
342
+ let data = null;
343
+ let responseBodyStream;
344
+ if (args.dataType === 'stream') {
345
+ // streaming mode will disable retry
346
+ args.retry = 0;
347
+ const meta = {
348
+ status: res.status,
349
+ statusCode: res.statusCode,
350
+ headers: res.headers,
351
+ };
352
+ if (isCompressedContent) {
353
+ // gzip or br
354
+ const decoder = contentEncoding === 'gzip' ? (0, zlib_1.createGunzip)() : (0, zlib_1.createBrotliDecompress)();
355
+ responseBodyStream = Object.assign((0, stream_1.pipeline)(response.body, decoder, noop), meta);
356
+ }
357
+ else {
358
+ responseBodyStream = Object.assign(response.body, meta);
217
359
  }
218
- res.status = res.statusCode = response.status;
219
- res.statusMessage = response.statusText;
220
- if (response.redirected) {
221
- res.requestUrls.push(response.url);
360
+ }
361
+ else if (args.writeStream) {
362
+ // streaming mode will disable retry
363
+ args.retry = 0;
364
+ if (isCompressedContent) {
365
+ const decoder = contentEncoding === 'gzip' ? (0, zlib_1.createGunzip)() : (0, zlib_1.createBrotliDecompress)();
366
+ await pipelinePromise(response.body, decoder, args.writeStream);
222
367
  }
223
- if (res.headers['content-length']) {
224
- res.size = parseInt(res.headers['content-length']);
368
+ else {
369
+ await pipelinePromise(response.body, args.writeStream);
225
370
  }
226
- let data;
227
- if (args.streaming || args.dataType === 'stream') {
228
- data = response.body;
371
+ }
372
+ else {
373
+ // buffer
374
+ data = Buffer.from(await response.body.arrayBuffer());
375
+ if (isCompressedContent) {
376
+ try {
377
+ data = contentEncoding === 'gzip' ? (0, zlib_1.gunzipSync)(data) : (0, zlib_1.brotliDecompressSync)(data);
378
+ }
379
+ catch (err) {
380
+ if (err.name === 'Error') {
381
+ err.name = 'UnzipError';
382
+ }
383
+ throw err;
384
+ }
229
385
  }
230
- else if (args.dataType === 'text') {
231
- data = await response.text();
386
+ if (args.dataType === 'text') {
387
+ data = data.toString();
232
388
  }
233
389
  else if (args.dataType === 'json') {
234
- if (requestOptions.method === 'HEAD') {
235
- data = {};
390
+ if (data.length === 0) {
391
+ data = null;
236
392
  }
237
393
  else {
238
- data = await response.json();
394
+ data = (0, utils_1.parseJSON)(data.toString(), args.fixJSONCtlChars);
239
395
  }
240
396
  }
241
- else {
242
- // buffer
243
- data = Buffer.from(await response.arrayBuffer());
397
+ }
398
+ res.rt = performanceTime(requestStartTime);
399
+ if (args.timing) {
400
+ res.timing.contentDownload = res.rt;
401
+ }
402
+ const clientResponse = {
403
+ opaque,
404
+ data,
405
+ status: res.status,
406
+ headers: res.headers,
407
+ url: lastUrl,
408
+ redirected: res.requestUrls.length > 1,
409
+ requestUrls: res.requestUrls,
410
+ res: responseBodyStream !== null && responseBodyStream !== void 0 ? responseBodyStream : res,
411
+ };
412
+ if (args.retry > 0 && requestContext.retries < args.retry) {
413
+ const isRetry = (_f = args.isRetry) !== null && _f !== void 0 ? _f : defaultIsRetry;
414
+ if (isRetry(clientResponse)) {
415
+ if (args.retryDelay) {
416
+ await (0, utils_1.sleep)(args.retryDelay);
417
+ }
418
+ requestContext.retries++;
419
+ return await tslib_1.__classPrivateFieldGet(this, _HttpClient_instances, "m", _HttpClient_requestInternal).call(this, url, options, requestContext);
244
420
  }
245
- res.rt = res.timing.contentDownload = Date.now() - requestStartTime;
246
- return {
247
- status: res.status,
248
- data,
249
- headers: res.headers,
250
- url: response.url,
251
- redirected: response.redirected,
421
+ }
422
+ if (this.listenerCount('response') > 0) {
423
+ this.emit('response', {
424
+ requestId,
425
+ error: null,
426
+ ctx: args.ctx,
427
+ req: reqMeta,
252
428
  res,
253
- };
429
+ });
254
430
  }
255
- catch (e) {
256
- let err = e;
257
- if (requestTimeoutController.signal.aborted) {
258
- err = new HttpClientRequestTimeoutError(requestTimeout, { cause: e });
259
- }
260
- err.res = res;
261
- // console.error(err);
262
- throw err;
431
+ return clientResponse;
432
+ }
433
+ catch (e) {
434
+ debug('Request#%d throw error: %s', requestId, e);
435
+ let err = e;
436
+ if (err.name === 'HeadersTimeoutError') {
437
+ err = new HttpClientRequestTimeoutError(headersTimeout, { cause: e });
438
+ }
439
+ else if (err.name === 'BodyTimeoutError') {
440
+ err = new HttpClientRequestTimeoutError(bodyTimeout, { cause: e });
263
441
  }
264
- finally {
265
- clearTimeout(requestTimerId);
442
+ err.opaque = opaque;
443
+ err.status = res.status;
444
+ err.headers = res.headers;
445
+ err.res = res;
446
+ // make sure requestUrls not empty
447
+ if (res.requestUrls.length === 0) {
448
+ res.requestUrls.push(requestUrl.href);
266
449
  }
450
+ res.rt = performanceTime(requestStartTime);
451
+ if (args.timing) {
452
+ res.timing.contentDownload = res.rt;
453
+ }
454
+ if (this.listenerCount('response') > 0) {
455
+ this.emit('response', {
456
+ requestId,
457
+ error: err,
458
+ ctx: args.ctx,
459
+ req: reqMeta,
460
+ res,
461
+ });
462
+ }
463
+ throw err;
267
464
  }
268
- }
269
- exports.HttpClient = HttpClient;
465
+ };
270
466
  //# sourceMappingURL=HttpClient.js.map