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