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