urllib 3.11.0 → 3.13.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,26 +1,14 @@
1
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
- if (kind === "m") throw new TypeError("Private method is not writable");
3
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
- };
7
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
- };
12
- var _BlobFromStream_stream, _BlobFromStream_type, _HttpClient_instances, _HttpClient_defaultArgs, _HttpClient_dispatcher, _HttpClient_requestInternal, _HttpClient_updateSocketInfo;
13
- import { EventEmitter } from 'events';
14
- import { STATUS_CODES } from 'http';
15
- import { debuglog } from 'util';
16
- import { createGunzip, createBrotliDecompress, gunzipSync, brotliDecompressSync, } from 'zlib';
17
- import { Blob } from 'buffer';
18
- import { Readable, pipeline } from 'stream';
19
- import stream from 'stream';
20
- import { basename } from 'path';
21
- import { createReadStream } from 'fs';
22
- import { format as urlFormat } from 'url';
23
- import { performance } from 'perf_hooks';
1
+ import { EventEmitter } from 'node:events';
2
+ import { STATUS_CODES } from 'node:http';
3
+ import { debuglog } from 'node:util';
4
+ import { createGunzip, createBrotliDecompress, gunzipSync, brotliDecompressSync, } from 'node:zlib';
5
+ import { Blob } from 'node:buffer';
6
+ import { Readable, pipeline } from 'node:stream';
7
+ import stream from 'node:stream';
8
+ import { basename } from 'node:path';
9
+ import { createReadStream } from 'node:fs';
10
+ import { format as urlFormat } from 'node:url';
11
+ import { performance } from 'node:perf_hooks';
24
12
  import { FormData as FormDataNative, request as undiciRequest, } from 'undici';
25
13
  import { FormData as FormDataNode } from 'formdata-node';
26
14
  import { FormDataEncoder } from 'form-data-encoder';
@@ -51,19 +39,19 @@ function noop() {
51
39
  const debug = debuglog('urllib:HttpClient');
52
40
  // https://github.com/octet-stream/form-data
53
41
  class BlobFromStream {
42
+ #stream;
43
+ #type;
54
44
  constructor(stream, type) {
55
- _BlobFromStream_stream.set(this, void 0);
56
- _BlobFromStream_type.set(this, void 0);
57
- __classPrivateFieldSet(this, _BlobFromStream_stream, stream, "f");
58
- __classPrivateFieldSet(this, _BlobFromStream_type, type, "f");
45
+ this.#stream = stream;
46
+ this.#type = type;
59
47
  }
60
48
  stream() {
61
- return __classPrivateFieldGet(this, _BlobFromStream_stream, "f");
49
+ return this.#stream;
62
50
  }
63
51
  get type() {
64
- return __classPrivateFieldGet(this, _BlobFromStream_type, "f");
52
+ return this.#type;
65
53
  }
66
- get [(_BlobFromStream_stream = new WeakMap(), _BlobFromStream_type = new WeakMap(), Symbol.toStringTag)]() {
54
+ get [Symbol.toStringTag]() {
67
55
  return 'Blob';
68
56
  }
69
57
  }
@@ -87,480 +75,483 @@ function defaultIsRetry(response) {
87
75
  return response.status >= 500;
88
76
  }
89
77
  export class HttpClient extends EventEmitter {
78
+ #defaultArgs;
79
+ #dispatcher;
90
80
  constructor(clientOptions) {
91
81
  super();
92
- _HttpClient_instances.add(this);
93
- _HttpClient_defaultArgs.set(this, void 0);
94
- _HttpClient_dispatcher.set(this, void 0);
95
- __classPrivateFieldSet(this, _HttpClient_defaultArgs, clientOptions?.defaultArgs, "f");
82
+ this.#defaultArgs = clientOptions?.defaultArgs;
96
83
  if (clientOptions?.lookup || clientOptions?.checkAddress || clientOptions?.connect) {
97
- __classPrivateFieldSet(this, _HttpClient_dispatcher, new HttpAgent({
84
+ this.#dispatcher = new HttpAgent({
98
85
  lookup: clientOptions.lookup,
99
86
  checkAddress: clientOptions.checkAddress,
100
87
  connect: clientOptions.connect,
101
- }), "f");
88
+ });
102
89
  }
103
90
  initDiagnosticsChannel();
104
91
  }
105
92
  async request(url, options) {
106
- return await __classPrivateFieldGet(this, _HttpClient_instances, "m", _HttpClient_requestInternal).call(this, url, options);
93
+ return await this.#requestInternal(url, options);
107
94
  }
108
95
  // alias to request, keep compatible with urlib@2 HttpClient.curl
109
96
  async curl(url, options) {
110
97
  return await this.request(url, options);
111
98
  }
112
- }
113
- _HttpClient_defaultArgs = new WeakMap(), _HttpClient_dispatcher = new WeakMap(), _HttpClient_instances = new WeakSet(), _HttpClient_requestInternal = async function _HttpClient_requestInternal(url, options, requestContext) {
114
- const requestId = globalId('HttpClientRequest');
115
- let requestUrl;
116
- if (typeof url === 'string') {
117
- if (!PROTO_RE.test(url)) {
118
- // Support `request('www.server.com')`
119
- url = 'http://' + url;
120
- }
121
- requestUrl = new URL(url);
122
- }
123
- else {
124
- if (!url.searchParams) {
125
- // url maybe url.parse(url) object in urllib2
126
- requestUrl = new URL(urlFormat(url));
127
- }
128
- else {
129
- requestUrl = url;
130
- }
131
- }
132
- const method = (options?.method ?? 'GET').toUpperCase();
133
- const originalHeaders = options?.headers;
134
- const headers = {};
135
- const args = {
136
- retry: 0,
137
- ...__classPrivateFieldGet(this, _HttpClient_defaultArgs, "f"),
138
- ...options,
139
- // keep method and headers exists on args for request event handler to easy use
140
- method,
141
- headers,
142
- };
143
- requestContext = {
144
- retries: 0,
145
- ...requestContext,
146
- };
147
- const requestStartTime = performance.now();
148
- // https://developer.chrome.com/docs/devtools/network/reference/?utm_source=devtools#timing-explanation
149
- const timing = {
150
- // socket assigned
151
- queuing: 0,
152
- // dns lookup time
153
- // dnslookup: 0,
154
- // socket connected
155
- connected: 0,
156
- // request headers sent
157
- requestHeadersSent: 0,
158
- // request sent, including headers and body
159
- requestSent: 0,
160
- // Time to first byte (TTFB), the response headers have been received
161
- waiting: 0,
162
- // the response body and trailers have been received
163
- contentDownload: 0,
164
- };
165
- const orginalOpaque = args.opaque;
166
- // using opaque to diagnostics channel, binding request and socket
167
- const internalOpaque = {
168
- [symbols.kRequestId]: requestId,
169
- [symbols.kRequestStartTime]: requestStartTime,
170
- [symbols.kEnableRequestTiming]: !!args.timing,
171
- [symbols.kRequestTiming]: timing,
172
- [symbols.kRequestOrginalOpaque]: orginalOpaque,
173
- };
174
- const reqMeta = {
175
- requestId,
176
- url: requestUrl.href,
177
- args,
178
- ctx: args.ctx,
179
- retries: requestContext.retries,
180
- };
181
- const socketInfo = {
182
- id: 0,
183
- localAddress: '',
184
- localPort: 0,
185
- remoteAddress: '',
186
- remotePort: 0,
187
- remoteFamily: '',
188
- bytesWritten: 0,
189
- bytesRead: 0,
190
- handledRequests: 0,
191
- handledResponses: 0,
192
- };
193
- // keep urllib createCallbackResponse style
194
- const resHeaders = {};
195
- let res = {
196
- status: -1,
197
- statusCode: -1,
198
- statusText: '',
199
- headers: resHeaders,
200
- size: 0,
201
- aborted: false,
202
- rt: 0,
203
- keepAliveSocket: true,
204
- requestUrls: [],
205
- timing,
206
- socket: socketInfo,
207
- };
208
- let headersTimeout = 5000;
209
- let bodyTimeout = 5000;
210
- if (args.timeout) {
211
- if (Array.isArray(args.timeout)) {
212
- headersTimeout = args.timeout[0] ?? headersTimeout;
213
- bodyTimeout = args.timeout[1] ?? bodyTimeout;
99
+ async #requestInternal(url, options, requestContext) {
100
+ const requestId = globalId('HttpClientRequest');
101
+ let requestUrl;
102
+ if (typeof url === 'string') {
103
+ if (!PROTO_RE.test(url)) {
104
+ // Support `request('www.server.com')`
105
+ url = 'http://' + url;
106
+ }
107
+ requestUrl = new URL(url);
214
108
  }
215
109
  else {
216
- headersTimeout = bodyTimeout = args.timeout;
217
- }
218
- }
219
- if (originalHeaders) {
220
- // convert headers to lower-case
221
- for (const name in originalHeaders) {
222
- headers[name.toLowerCase()] = originalHeaders[name];
110
+ if (!url.searchParams) {
111
+ // url maybe url.parse(url) object in urllib2
112
+ requestUrl = new URL(urlFormat(url));
113
+ }
114
+ else {
115
+ requestUrl = url;
116
+ }
223
117
  }
224
- }
225
- // hidden user-agent
226
- const hiddenUserAgent = 'user-agent' in headers && !headers['user-agent'];
227
- if (hiddenUserAgent) {
228
- delete headers['user-agent'];
229
- }
230
- else if (!headers['user-agent']) {
231
- // need to set user-agent
232
- headers['user-agent'] = HEADER_USER_AGENT;
233
- }
234
- // Alias to dataType = 'stream'
235
- if (args.streaming || args.customResponse) {
236
- args.dataType = 'stream';
237
- }
238
- if (args.dataType === 'json' && !headers.accept) {
239
- headers.accept = 'application/json';
240
- }
241
- // gzip alias to compressed
242
- if (args.gzip && args.compressed !== false) {
243
- args.compressed = true;
244
- }
245
- if (args.compressed && !headers['accept-encoding']) {
246
- headers['accept-encoding'] = 'gzip, br';
247
- }
248
- if (requestContext.retries > 0) {
249
- headers['x-urllib-retry'] = `${requestContext.retries}/${args.retry}`;
250
- }
251
- if (args.auth && !headers.authorization) {
252
- headers.authorization = `Basic ${Buffer.from(args.auth).toString('base64')}`;
253
- }
254
- try {
255
- const requestOptions = {
118
+ const method = (options?.method ?? 'GET').toUpperCase();
119
+ const originalHeaders = options?.headers;
120
+ const headers = {};
121
+ const args = {
122
+ retry: 0,
123
+ ...this.#defaultArgs,
124
+ ...options,
125
+ // keep method and headers exists on args for request event handler to easy use
256
126
  method,
257
- maxRedirections: args.maxRedirects ?? 10,
258
- headersTimeout,
259
127
  headers,
260
- bodyTimeout,
261
- opaque: internalOpaque,
262
- dispatcher: args.dispatcher ?? __classPrivateFieldGet(this, _HttpClient_dispatcher, "f"),
263
128
  };
264
- if (args.followRedirect === false) {
265
- requestOptions.maxRedirections = 0;
266
- }
267
- const isGETOrHEAD = requestOptions.method === 'GET' || requestOptions.method === 'HEAD';
268
- // alias to args.content
269
- if (args.stream && !args.content) {
270
- // convert old style stream to new stream
271
- // https://nodejs.org/dist/latest-v18.x/docs/api/stream.html#readablewrapstream
272
- if (isReadable(args.stream) && !(args.stream instanceof Readable)) {
273
- debug('Request#%d convert old style stream to Readable', requestId);
274
- args.stream = new Readable().wrap(args.stream);
129
+ requestContext = {
130
+ retries: 0,
131
+ ...requestContext,
132
+ };
133
+ const requestStartTime = performance.now();
134
+ // https://developer.chrome.com/docs/devtools/network/reference/?utm_source=devtools#timing-explanation
135
+ const timing = {
136
+ // socket assigned
137
+ queuing: 0,
138
+ // dns lookup time
139
+ // dnslookup: 0,
140
+ // socket connected
141
+ connected: 0,
142
+ // request headers sent
143
+ requestHeadersSent: 0,
144
+ // request sent, including headers and body
145
+ requestSent: 0,
146
+ // Time to first byte (TTFB), the response headers have been received
147
+ waiting: 0,
148
+ // the response body and trailers have been received
149
+ contentDownload: 0,
150
+ };
151
+ const orginalOpaque = args.opaque;
152
+ // using opaque to diagnostics channel, binding request and socket
153
+ const internalOpaque = {
154
+ [symbols.kRequestId]: requestId,
155
+ [symbols.kRequestStartTime]: requestStartTime,
156
+ [symbols.kEnableRequestTiming]: !!args.timing,
157
+ [symbols.kRequestTiming]: timing,
158
+ [symbols.kRequestOrginalOpaque]: orginalOpaque,
159
+ };
160
+ const reqMeta = {
161
+ requestId,
162
+ url: requestUrl.href,
163
+ args,
164
+ ctx: args.ctx,
165
+ retries: requestContext.retries,
166
+ };
167
+ const socketInfo = {
168
+ id: 0,
169
+ localAddress: '',
170
+ localPort: 0,
171
+ remoteAddress: '',
172
+ remotePort: 0,
173
+ remoteFamily: '',
174
+ bytesWritten: 0,
175
+ bytesRead: 0,
176
+ handledRequests: 0,
177
+ handledResponses: 0,
178
+ };
179
+ // keep urllib createCallbackResponse style
180
+ const resHeaders = {};
181
+ let res = {
182
+ status: -1,
183
+ statusCode: -1,
184
+ statusText: '',
185
+ headers: resHeaders,
186
+ size: 0,
187
+ aborted: false,
188
+ rt: 0,
189
+ keepAliveSocket: true,
190
+ requestUrls: [],
191
+ timing,
192
+ socket: socketInfo,
193
+ };
194
+ let headersTimeout = 5000;
195
+ let bodyTimeout = 5000;
196
+ if (args.timeout) {
197
+ if (Array.isArray(args.timeout)) {
198
+ headersTimeout = args.timeout[0] ?? headersTimeout;
199
+ bodyTimeout = args.timeout[1] ?? bodyTimeout;
275
200
  }
276
- else if (args.stream instanceof FormStream) {
277
- debug('Request#%d convert formstream to Readable', requestId);
278
- args.stream = new Readable().wrap(args.stream);
201
+ else {
202
+ headersTimeout = bodyTimeout = args.timeout;
279
203
  }
280
- args.content = args.stream;
281
204
  }
282
- if (args.files) {
283
- if (isGETOrHEAD) {
284
- requestOptions.method = 'POST';
285
- }
286
- const formData = new FormData();
287
- const uploadFiles = [];
288
- if (Array.isArray(args.files)) {
289
- for (const [index, file] of args.files.entries()) {
290
- const field = index === 0 ? 'file' : `file${index}`;
291
- uploadFiles.push([field, file]);
292
- }
205
+ if (originalHeaders) {
206
+ // convert headers to lower-case
207
+ for (const name in originalHeaders) {
208
+ headers[name.toLowerCase()] = originalHeaders[name];
293
209
  }
294
- else if (args.files instanceof Readable || isReadable(args.files)) {
295
- uploadFiles.push(['file', args.files]);
210
+ }
211
+ // hidden user-agent
212
+ const hiddenUserAgent = 'user-agent' in headers && !headers['user-agent'];
213
+ if (hiddenUserAgent) {
214
+ delete headers['user-agent'];
215
+ }
216
+ else if (!headers['user-agent']) {
217
+ // need to set user-agent
218
+ headers['user-agent'] = HEADER_USER_AGENT;
219
+ }
220
+ // Alias to dataType = 'stream'
221
+ if (args.streaming || args.customResponse) {
222
+ args.dataType = 'stream';
223
+ }
224
+ if (args.dataType === 'json' && !headers.accept) {
225
+ headers.accept = 'application/json';
226
+ }
227
+ // gzip alias to compressed
228
+ if (args.gzip && args.compressed !== false) {
229
+ args.compressed = true;
230
+ }
231
+ if (args.compressed && !headers['accept-encoding']) {
232
+ headers['accept-encoding'] = 'gzip, br';
233
+ }
234
+ if (requestContext.retries > 0) {
235
+ headers['x-urllib-retry'] = `${requestContext.retries}/${args.retry}`;
236
+ }
237
+ if (args.auth && !headers.authorization) {
238
+ headers.authorization = `Basic ${Buffer.from(args.auth).toString('base64')}`;
239
+ }
240
+ try {
241
+ const requestOptions = {
242
+ method,
243
+ maxRedirections: args.maxRedirects ?? 10,
244
+ headersTimeout,
245
+ headers,
246
+ bodyTimeout,
247
+ opaque: internalOpaque,
248
+ dispatcher: args.dispatcher ?? this.#dispatcher,
249
+ };
250
+ if (typeof args.reset === 'boolean') {
251
+ requestOptions.reset = args.reset;
296
252
  }
297
- else if (typeof args.files === 'string' || Buffer.isBuffer(args.files)) {
298
- uploadFiles.push(['file', args.files]);
253
+ if (args.followRedirect === false) {
254
+ requestOptions.maxRedirections = 0;
299
255
  }
300
- else if (typeof args.files === 'object') {
301
- for (const field in args.files) {
302
- uploadFiles.push([field, args.files[field]]);
256
+ const isGETOrHEAD = requestOptions.method === 'GET' || requestOptions.method === 'HEAD';
257
+ // alias to args.content
258
+ if (args.stream && !args.content) {
259
+ // convert old style stream to new stream
260
+ // https://nodejs.org/dist/latest-v18.x/docs/api/stream.html#readablewrapstream
261
+ if (isReadable(args.stream) && !(args.stream instanceof Readable)) {
262
+ debug('Request#%d convert old style stream to Readable', requestId);
263
+ args.stream = new Readable().wrap(args.stream);
303
264
  }
304
- }
305
- // set normal fields first
306
- if (args.data) {
307
- for (const field in args.data) {
308
- formData.append(field, args.data[field]);
265
+ else if (args.stream instanceof FormStream) {
266
+ debug('Request#%d convert formstream to Readable', requestId);
267
+ args.stream = new Readable().wrap(args.stream);
309
268
  }
269
+ args.content = args.stream;
310
270
  }
311
- for (const [index, [field, file]] of uploadFiles.entries()) {
312
- if (typeof file === 'string') {
313
- // FIXME: support non-ascii filename
314
- // const fileName = encodeURIComponent(basename(file));
315
- // formData.append(field, await fileFromPath(file, `utf-8''${fileName}`, { type: mime.lookup(fileName) || '' }));
316
- const fileName = basename(file);
317
- const fileReadable = createReadStream(file);
318
- formData.append(field, new BlobFromStream(fileReadable, mime.lookup(fileName) || ''), fileName);
271
+ if (args.files) {
272
+ if (isGETOrHEAD) {
273
+ requestOptions.method = 'POST';
319
274
  }
320
- else if (Buffer.isBuffer(file)) {
321
- formData.append(field, new Blob([file]), `bufferfile${index}`);
275
+ const formData = new FormData();
276
+ const uploadFiles = [];
277
+ if (Array.isArray(args.files)) {
278
+ for (const [index, file] of args.files.entries()) {
279
+ const field = index === 0 ? 'file' : `file${index}`;
280
+ uploadFiles.push([field, file]);
281
+ }
322
282
  }
323
- else if (file instanceof Readable || isReadable(file)) {
324
- const fileName = getFileName(file) || `streamfile${index}`;
325
- formData.append(field, new BlobFromStream(file, mime.lookup(fileName) || ''), fileName);
283
+ else if (args.files instanceof Readable || isReadable(args.files)) {
284
+ uploadFiles.push(['file', args.files]);
326
285
  }
327
- }
328
- if (FormDataNative) {
329
- requestOptions.body = formData;
330
- }
331
- else {
332
- // Node.js 14 does not support spec-compliant FormData
333
- // https://github.com/octet-stream/form-data#usage
334
- const encoder = new FormDataEncoder(formData);
335
- Object.assign(headers, encoder.headers);
336
- // fix "Content-Length":"NaN"
337
- delete headers['Content-Length'];
338
- requestOptions.body = Readable.from(encoder);
339
- }
340
- }
341
- else if (args.content) {
342
- if (!isGETOrHEAD) {
343
- // handle content
344
- requestOptions.body = args.content;
345
- if (args.contentType) {
346
- headers['content-type'] = args.contentType;
286
+ else if (typeof args.files === 'string' || Buffer.isBuffer(args.files)) {
287
+ uploadFiles.push(['file', args.files]);
347
288
  }
348
- else if (typeof args.content === 'string' && !headers['content-type']) {
349
- headers['content-type'] = 'text/plain;charset=UTF-8';
289
+ else if (typeof args.files === 'object') {
290
+ for (const field in args.files) {
291
+ uploadFiles.push([field, args.files[field]]);
292
+ }
350
293
  }
351
- }
352
- }
353
- else if (args.data) {
354
- const isStringOrBufferOrReadable = typeof args.data === 'string'
355
- || Buffer.isBuffer(args.data)
356
- || isReadable(args.data);
357
- if (isGETOrHEAD) {
358
- if (!isStringOrBufferOrReadable) {
294
+ // set normal fields first
295
+ if (args.data) {
359
296
  for (const field in args.data) {
360
- requestUrl.searchParams.append(field, args.data[field]);
297
+ formData.append(field, args.data[field]);
361
298
  }
362
299
  }
363
- }
364
- else {
365
- if (isStringOrBufferOrReadable) {
366
- requestOptions.body = args.data;
300
+ for (const [index, [field, file]] of uploadFiles.entries()) {
301
+ if (typeof file === 'string') {
302
+ // FIXME: support non-ascii filename
303
+ // const fileName = encodeURIComponent(basename(file));
304
+ // formData.append(field, await fileFromPath(file, `utf-8''${fileName}`, { type: mime.lookup(fileName) || '' }));
305
+ const fileName = basename(file);
306
+ const fileReadable = createReadStream(file);
307
+ formData.append(field, new BlobFromStream(fileReadable, mime.lookup(fileName) || ''), fileName);
308
+ }
309
+ else if (Buffer.isBuffer(file)) {
310
+ formData.append(field, new Blob([file]), `bufferfile${index}`);
311
+ }
312
+ else if (file instanceof Readable || isReadable(file)) {
313
+ const fileName = getFileName(file) || `streamfile${index}`;
314
+ formData.append(field, new BlobFromStream(file, mime.lookup(fileName) || ''), fileName);
315
+ }
316
+ }
317
+ if (FormDataNative) {
318
+ requestOptions.body = formData;
367
319
  }
368
320
  else {
369
- if (args.contentType === 'json'
370
- || args.contentType === 'application/json'
371
- || headers['content-type']?.startsWith('application/json')) {
372
- requestOptions.body = JSON.stringify(args.data);
373
- if (!headers['content-type']) {
374
- headers['content-type'] = 'application/json';
321
+ // Node.js 14 does not support spec-compliant FormData
322
+ // https://github.com/octet-stream/form-data#usage
323
+ const encoder = new FormDataEncoder(formData);
324
+ Object.assign(headers, encoder.headers);
325
+ // fix "Content-Length":"NaN"
326
+ delete headers['Content-Length'];
327
+ requestOptions.body = Readable.from(encoder);
328
+ }
329
+ }
330
+ else if (args.content) {
331
+ if (!isGETOrHEAD) {
332
+ // handle content
333
+ requestOptions.body = args.content;
334
+ if (args.contentType) {
335
+ headers['content-type'] = args.contentType;
336
+ }
337
+ else if (typeof args.content === 'string' && !headers['content-type']) {
338
+ headers['content-type'] = 'text/plain;charset=UTF-8';
339
+ }
340
+ }
341
+ }
342
+ else if (args.data) {
343
+ const isStringOrBufferOrReadable = typeof args.data === 'string'
344
+ || Buffer.isBuffer(args.data)
345
+ || isReadable(args.data);
346
+ if (isGETOrHEAD) {
347
+ if (!isStringOrBufferOrReadable) {
348
+ for (const field in args.data) {
349
+ requestUrl.searchParams.append(field, args.data[field]);
375
350
  }
376
351
  }
352
+ }
353
+ else {
354
+ if (isStringOrBufferOrReadable) {
355
+ requestOptions.body = args.data;
356
+ }
377
357
  else {
378
- headers['content-type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
379
- requestOptions.body = new URLSearchParams(args.data).toString();
358
+ if (args.contentType === 'json'
359
+ || args.contentType === 'application/json'
360
+ || headers['content-type']?.startsWith('application/json')) {
361
+ requestOptions.body = JSON.stringify(args.data);
362
+ if (!headers['content-type']) {
363
+ headers['content-type'] = 'application/json';
364
+ }
365
+ }
366
+ else {
367
+ headers['content-type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
368
+ requestOptions.body = new URLSearchParams(args.data).toString();
369
+ }
380
370
  }
381
371
  }
382
372
  }
383
- }
384
- debug('Request#%d %s %s, headers: %j, headersTimeout: %s, bodyTimeout: %s', requestId, requestOptions.method, requestUrl.href, headers, headersTimeout, bodyTimeout);
385
- requestOptions.headers = headers;
386
- if (this.listenerCount('request') > 0) {
387
- this.emit('request', reqMeta);
388
- }
389
- let response = await undiciRequest(requestUrl, requestOptions);
390
- if (response.statusCode === 401 && response.headers['www-authenticate'] &&
391
- !requestOptions.headers.authorization && args.digestAuth) {
392
- // handle digest auth
393
- const authenticateHeaders = response.headers['www-authenticate'];
394
- const authenticate = Array.isArray(authenticateHeaders)
395
- ? authenticateHeaders.find(authHeader => authHeader.startsWith('Digest '))
396
- : authenticateHeaders;
397
- if (authenticate && authenticate.startsWith('Digest ')) {
398
- debug('Request#%d %s: got digest auth header WWW-Authenticate: %s', requestId, requestUrl.href, authenticate);
399
- requestOptions.headers.authorization = digestAuthHeader(requestOptions.method, `${requestUrl.pathname}${requestUrl.search}`, authenticate, args.digestAuth);
400
- debug('Request#%d %s: auth with digest header: %s', requestId, url, requestOptions.headers.authorization);
401
- if (Array.isArray(response.headers['set-cookie'])) {
402
- // FIXME: merge exists cookie header
403
- requestOptions.headers.cookie = response.headers['set-cookie'].join(';');
404
- }
405
- response = await undiciRequest(requestUrl, requestOptions);
373
+ debug('Request#%d %s %s, headers: %j, headersTimeout: %s, bodyTimeout: %s', requestId, requestOptions.method, requestUrl.href, headers, headersTimeout, bodyTimeout);
374
+ requestOptions.headers = headers;
375
+ if (this.listenerCount('request') > 0) {
376
+ this.emit('request', reqMeta);
406
377
  }
407
- }
408
- const context = response.context;
409
- let lastUrl = '';
410
- if (context?.history) {
411
- for (const urlObject of context?.history) {
412
- res.requestUrls.push(urlObject.href);
413
- lastUrl = urlObject.href;
378
+ let response = await undiciRequest(requestUrl, requestOptions);
379
+ if (response.statusCode === 401 && response.headers['www-authenticate'] &&
380
+ !requestOptions.headers.authorization && args.digestAuth) {
381
+ // handle digest auth
382
+ const authenticateHeaders = response.headers['www-authenticate'];
383
+ const authenticate = Array.isArray(authenticateHeaders)
384
+ ? authenticateHeaders.find(authHeader => authHeader.startsWith('Digest '))
385
+ : authenticateHeaders;
386
+ if (authenticate && authenticate.startsWith('Digest ')) {
387
+ debug('Request#%d %s: got digest auth header WWW-Authenticate: %s', requestId, requestUrl.href, authenticate);
388
+ requestOptions.headers.authorization = digestAuthHeader(requestOptions.method, `${requestUrl.pathname}${requestUrl.search}`, authenticate, args.digestAuth);
389
+ debug('Request#%d %s: auth with digest header: %s', requestId, url, requestOptions.headers.authorization);
390
+ if (Array.isArray(response.headers['set-cookie'])) {
391
+ // FIXME: merge exists cookie header
392
+ requestOptions.headers.cookie = response.headers['set-cookie'].join(';');
393
+ }
394
+ response = await undiciRequest(requestUrl, requestOptions);
395
+ }
414
396
  }
415
- }
416
- else {
417
- res.requestUrls.push(requestUrl.href);
418
- lastUrl = requestUrl.href;
419
- }
420
- const contentEncoding = response.headers['content-encoding'];
421
- const isCompressedContent = contentEncoding === 'gzip' || contentEncoding === 'br';
422
- res.headers = response.headers;
423
- res.status = res.statusCode = response.statusCode;
424
- res.statusText = STATUS_CODES[res.status] || '';
425
- if (res.headers['content-length']) {
426
- res.size = parseInt(res.headers['content-length']);
427
- }
428
- let data = null;
429
- if (args.dataType === 'stream') {
430
- // streaming mode will disable retry
431
- args.retry = 0;
432
- // only auto decompress on request args.compressed = true
433
- if (args.compressed === true && isCompressedContent) {
434
- // gzip or br
435
- const decoder = contentEncoding === 'gzip' ? createGunzip() : createBrotliDecompress();
436
- res = Object.assign(pipeline(response.body, decoder, noop), res);
397
+ const context = response.context;
398
+ let lastUrl = '';
399
+ if (context?.history) {
400
+ for (const urlObject of context?.history) {
401
+ res.requestUrls.push(urlObject.href);
402
+ lastUrl = urlObject.href;
403
+ }
437
404
  }
438
405
  else {
439
- res = Object.assign(response.body, res);
406
+ res.requestUrls.push(requestUrl.href);
407
+ lastUrl = requestUrl.href;
440
408
  }
441
- }
442
- else if (args.writeStream) {
443
- // streaming mode will disable retry
444
- args.retry = 0;
445
- if (args.compressed === true && isCompressedContent) {
446
- const decoder = contentEncoding === 'gzip' ? createGunzip() : createBrotliDecompress();
447
- await pipelinePromise(response.body, decoder, args.writeStream);
409
+ const contentEncoding = response.headers['content-encoding'];
410
+ const isCompressedContent = contentEncoding === 'gzip' || contentEncoding === 'br';
411
+ res.headers = response.headers;
412
+ res.status = res.statusCode = response.statusCode;
413
+ res.statusText = STATUS_CODES[res.status] || '';
414
+ if (res.headers['content-length']) {
415
+ res.size = parseInt(res.headers['content-length']);
448
416
  }
449
- else {
450
- await pipelinePromise(response.body, args.writeStream);
451
- }
452
- }
453
- else {
454
- // buffer
455
- data = Buffer.from(await response.body.arrayBuffer());
456
- if (isCompressedContent && data.length > 0) {
457
- try {
458
- data = contentEncoding === 'gzip' ? gunzipSync(data) : brotliDecompressSync(data);
417
+ let data = null;
418
+ if (args.dataType === 'stream') {
419
+ // streaming mode will disable retry
420
+ args.retry = 0;
421
+ // only auto decompress on request args.compressed = true
422
+ if (args.compressed === true && isCompressedContent) {
423
+ // gzip or br
424
+ const decoder = contentEncoding === 'gzip' ? createGunzip() : createBrotliDecompress();
425
+ res = Object.assign(pipeline(response.body, decoder, noop), res);
459
426
  }
460
- catch (err) {
461
- if (err.name === 'Error') {
462
- err.name = 'UnzipError';
463
- }
464
- throw err;
427
+ else {
428
+ res = Object.assign(response.body, res);
465
429
  }
466
430
  }
467
- if (args.dataType === 'text' || args.dataType === 'html') {
468
- data = data.toString();
469
- }
470
- else if (args.dataType === 'json') {
471
- if (data.length === 0) {
472
- data = null;
431
+ else if (args.writeStream) {
432
+ // streaming mode will disable retry
433
+ args.retry = 0;
434
+ if (args.compressed === true && isCompressedContent) {
435
+ const decoder = contentEncoding === 'gzip' ? createGunzip() : createBrotliDecompress();
436
+ await pipelinePromise(response.body, decoder, args.writeStream);
473
437
  }
474
438
  else {
475
- data = parseJSON(data.toString(), args.fixJSONCtlChars);
439
+ await pipelinePromise(response.body, args.writeStream);
476
440
  }
477
441
  }
478
- }
479
- res.rt = performanceTime(requestStartTime);
480
- // get real socket info from internalOpaque
481
- __classPrivateFieldGet(this, _HttpClient_instances, "m", _HttpClient_updateSocketInfo).call(this, socketInfo, internalOpaque);
482
- const clientResponse = {
483
- opaque: orginalOpaque,
484
- data,
485
- status: res.status,
486
- statusCode: res.status,
487
- statusText: res.statusText,
488
- headers: res.headers,
489
- url: lastUrl,
490
- redirected: res.requestUrls.length > 1,
491
- requestUrls: res.requestUrls,
492
- res,
493
- };
494
- if (args.retry > 0 && requestContext.retries < args.retry) {
495
- const isRetry = args.isRetry ?? defaultIsRetry;
496
- if (isRetry(clientResponse)) {
497
- if (args.retryDelay) {
498
- await sleep(args.retryDelay);
442
+ else {
443
+ // buffer
444
+ data = Buffer.from(await response.body.arrayBuffer());
445
+ if (isCompressedContent && data.length > 0) {
446
+ try {
447
+ data = contentEncoding === 'gzip' ? gunzipSync(data) : brotliDecompressSync(data);
448
+ }
449
+ catch (err) {
450
+ if (err.name === 'Error') {
451
+ err.name = 'UnzipError';
452
+ }
453
+ throw err;
454
+ }
455
+ }
456
+ if (args.dataType === 'text' || args.dataType === 'html') {
457
+ data = data.toString();
458
+ }
459
+ else if (args.dataType === 'json') {
460
+ if (data.length === 0) {
461
+ data = null;
462
+ }
463
+ else {
464
+ data = parseJSON(data.toString(), args.fixJSONCtlChars);
465
+ }
499
466
  }
500
- requestContext.retries++;
501
- return await __classPrivateFieldGet(this, _HttpClient_instances, "m", _HttpClient_requestInternal).call(this, url, options, requestContext);
502
467
  }
503
- }
504
- if (this.listenerCount('response') > 0) {
505
- this.emit('response', {
506
- requestId,
507
- error: null,
508
- ctx: args.ctx,
509
- req: {
510
- ...reqMeta,
511
- options: args,
512
- },
468
+ res.rt = performanceTime(requestStartTime);
469
+ // get real socket info from internalOpaque
470
+ this.#updateSocketInfo(socketInfo, internalOpaque);
471
+ const clientResponse = {
472
+ opaque: orginalOpaque,
473
+ data,
474
+ status: res.status,
475
+ statusCode: res.status,
476
+ statusText: res.statusText,
477
+ headers: res.headers,
478
+ url: lastUrl,
479
+ redirected: res.requestUrls.length > 1,
480
+ requestUrls: res.requestUrls,
513
481
  res,
514
- });
515
- }
516
- return clientResponse;
517
- }
518
- catch (e) {
519
- debug('Request#%d throw error: %s', requestId, e);
520
- let err = e;
521
- if (err.name === 'HeadersTimeoutError') {
522
- err = new HttpClientRequestTimeoutError(headersTimeout, { cause: e });
523
- }
524
- else if (err.name === 'BodyTimeoutError') {
525
- err = new HttpClientRequestTimeoutError(bodyTimeout, { cause: e });
526
- }
527
- err.opaque = orginalOpaque;
528
- err.status = res.status;
529
- err.headers = res.headers;
530
- err.res = res;
531
- // make sure requestUrls not empty
532
- if (res.requestUrls.length === 0) {
533
- res.requestUrls.push(requestUrl.href);
482
+ };
483
+ if (args.retry > 0 && requestContext.retries < args.retry) {
484
+ const isRetry = args.isRetry ?? defaultIsRetry;
485
+ if (isRetry(clientResponse)) {
486
+ if (args.retryDelay) {
487
+ await sleep(args.retryDelay);
488
+ }
489
+ requestContext.retries++;
490
+ return await this.#requestInternal(url, options, requestContext);
491
+ }
492
+ }
493
+ if (this.listenerCount('response') > 0) {
494
+ this.emit('response', {
495
+ requestId,
496
+ error: null,
497
+ ctx: args.ctx,
498
+ req: {
499
+ ...reqMeta,
500
+ options: args,
501
+ },
502
+ res,
503
+ });
504
+ }
505
+ return clientResponse;
534
506
  }
535
- res.rt = performanceTime(requestStartTime);
536
- __classPrivateFieldGet(this, _HttpClient_instances, "m", _HttpClient_updateSocketInfo).call(this, socketInfo, internalOpaque);
537
- if (this.listenerCount('response') > 0) {
538
- this.emit('response', {
539
- requestId,
540
- error: err,
541
- ctx: args.ctx,
542
- req: {
543
- ...reqMeta,
544
- options: args,
545
- },
546
- res,
547
- });
507
+ catch (e) {
508
+ debug('Request#%d throw error: %s', requestId, e);
509
+ let err = e;
510
+ if (err.name === 'HeadersTimeoutError') {
511
+ err = new HttpClientRequestTimeoutError(headersTimeout, { cause: e });
512
+ }
513
+ else if (err.name === 'BodyTimeoutError') {
514
+ err = new HttpClientRequestTimeoutError(bodyTimeout, { cause: e });
515
+ }
516
+ err.opaque = orginalOpaque;
517
+ err.status = res.status;
518
+ err.headers = res.headers;
519
+ err.res = res;
520
+ // make sure requestUrls not empty
521
+ if (res.requestUrls.length === 0) {
522
+ res.requestUrls.push(requestUrl.href);
523
+ }
524
+ res.rt = performanceTime(requestStartTime);
525
+ this.#updateSocketInfo(socketInfo, internalOpaque);
526
+ if (this.listenerCount('response') > 0) {
527
+ this.emit('response', {
528
+ requestId,
529
+ error: err,
530
+ ctx: args.ctx,
531
+ req: {
532
+ ...reqMeta,
533
+ options: args,
534
+ },
535
+ res,
536
+ });
537
+ }
538
+ throw err;
548
539
  }
549
- throw err;
550
540
  }
551
- }, _HttpClient_updateSocketInfo = function _HttpClient_updateSocketInfo(socketInfo, internalOpaque) {
552
- const socket = internalOpaque[symbols.kRequestSocket];
553
- if (socket) {
554
- socketInfo.id = socket[symbols.kSocketId];
555
- socketInfo.handledRequests = socket[symbols.kHandledRequests];
556
- socketInfo.handledResponses = socket[symbols.kHandledResponses];
557
- socketInfo.localAddress = socket.localAddress;
558
- socketInfo.localPort = socket.localPort;
559
- socketInfo.remoteAddress = socket.remoteAddress;
560
- socketInfo.remotePort = socket.remotePort;
561
- socketInfo.remoteFamily = socket.remoteFamily;
562
- socketInfo.bytesRead = socket.bytesRead;
563
- socketInfo.bytesWritten = socket.bytesWritten;
541
+ #updateSocketInfo(socketInfo, internalOpaque) {
542
+ const socket = internalOpaque[symbols.kRequestSocket];
543
+ if (socket) {
544
+ socketInfo.id = socket[symbols.kSocketId];
545
+ socketInfo.handledRequests = socket[symbols.kHandledRequests];
546
+ socketInfo.handledResponses = socket[symbols.kHandledResponses];
547
+ socketInfo.localAddress = socket.localAddress;
548
+ socketInfo.localPort = socket.localPort;
549
+ socketInfo.remoteAddress = socket.remoteAddress;
550
+ socketInfo.remotePort = socket.remotePort;
551
+ socketInfo.remoteFamily = socket.remoteFamily;
552
+ socketInfo.bytesRead = socket.bytesRead;
553
+ socketInfo.bytesWritten = socket.bytesWritten;
554
+ }
564
555
  }
565
- };
556
+ }
566
557
  //# sourceMappingURL=HttpClient.js.map