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.
- package/package.json +5 -5
- package/src/HttpAgent.ts +2 -2
- package/src/HttpClient.ts +16 -13
- package/src/Request.ts +4 -2
- package/src/Response.ts +2 -2
- package/src/cjs/HttpAgent.d.ts +1 -1
- package/src/cjs/HttpAgent.js +11 -21
- package/src/cjs/HttpAgent.js.map +1 -1
- package/src/cjs/HttpClient.d.ts +2 -2
- package/src/cjs/HttpClient.js +436 -445
- package/src/cjs/HttpClient.js.map +1 -1
- package/src/cjs/Request.d.ts +4 -2
- package/src/cjs/Response.d.ts +2 -2
- package/src/cjs/diagnosticsChannel.js +11 -11
- package/src/cjs/diagnosticsChannel.js.map +1 -1
- package/src/cjs/utils.js +8 -8
- package/src/cjs/utils.js.map +1 -1
- package/src/diagnosticsChannel.ts +3 -3
- package/src/esm/HttpAgent.d.ts +1 -1
- package/src/esm/HttpAgent.js +9 -19
- package/src/esm/HttpAgent.js.map +1 -1
- package/src/esm/HttpClient.d.ts +2 -2
- package/src/esm/HttpClient.js +431 -440
- package/src/esm/HttpClient.js.map +1 -1
- package/src/esm/Request.d.ts +4 -2
- package/src/esm/Response.d.ts +2 -2
- package/src/esm/diagnosticsChannel.js +3 -3
- package/src/esm/diagnosticsChannel.js.map +1 -1
- package/src/esm/utils.js +3 -3
- package/src/esm/utils.js.map +1 -1
- package/src/utils.ts +3 -3
package/src/cjs/HttpClient.js
CHANGED
@@ -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
|
20
|
-
const
|
21
|
-
const
|
22
|
-
const
|
23
|
-
const
|
24
|
-
const
|
25
|
-
const
|
26
|
-
const
|
27
|
-
const
|
28
|
-
const
|
29
|
-
const
|
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 =
|
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,
|
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
|
-
|
62
|
-
|
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
|
55
|
+
return this.#stream;
|
68
56
|
}
|
69
57
|
get type() {
|
70
|
-
return
|
58
|
+
return this.#type;
|
71
59
|
}
|
72
|
-
get [
|
60
|
+
get [Symbol.toStringTag]() {
|
73
61
|
return 'Blob';
|
74
62
|
}
|
75
63
|
}
|
@@ -85,489 +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,
|
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
|
83
|
+
class HttpClient extends node_events_1.EventEmitter {
|
84
|
+
#defaultArgs;
|
85
|
+
#dispatcher;
|
96
86
|
constructor(clientOptions) {
|
97
87
|
super();
|
98
|
-
|
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
|
-
|
90
|
+
this.#dispatcher = new HttpAgent_1.HttpAgent({
|
104
91
|
lookup: clientOptions.lookup,
|
105
92
|
checkAddress: clientOptions.checkAddress,
|
106
93
|
connect: clientOptions.connect,
|
107
|
-
})
|
94
|
+
});
|
108
95
|
}
|
109
96
|
(0, diagnosticsChannel_1.initDiagnosticsChannel)();
|
110
97
|
}
|
111
98
|
async request(url, options) {
|
112
|
-
return await
|
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
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
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
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
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
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
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
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
//
|
279
|
-
|
280
|
-
|
281
|
-
|
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;
|
282
206
|
}
|
283
|
-
else
|
284
|
-
|
285
|
-
args.stream = new stream_1.Readable().wrap(args.stream);
|
207
|
+
else {
|
208
|
+
headersTimeout = bodyTimeout = args.timeout;
|
286
209
|
}
|
287
|
-
args.content = args.stream;
|
288
210
|
}
|
289
|
-
if (
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
const formData = new FormData();
|
294
|
-
const uploadFiles = [];
|
295
|
-
if (Array.isArray(args.files)) {
|
296
|
-
for (const [index, file] of args.files.entries()) {
|
297
|
-
const field = index === 0 ? 'file' : `file${index}`;
|
298
|
-
uploadFiles.push([field, file]);
|
299
|
-
}
|
211
|
+
if (originalHeaders) {
|
212
|
+
// convert headers to lower-case
|
213
|
+
for (const name in originalHeaders) {
|
214
|
+
headers[name.toLowerCase()] = originalHeaders[name];
|
300
215
|
}
|
301
|
-
|
302
|
-
|
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;
|
303
258
|
}
|
304
|
-
|
305
|
-
|
259
|
+
if (args.followRedirect === false) {
|
260
|
+
requestOptions.maxRedirections = 0;
|
306
261
|
}
|
307
|
-
|
308
|
-
|
309
|
-
|
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);
|
310
270
|
}
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
for (const field in args.data) {
|
315
|
-
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);
|
316
274
|
}
|
275
|
+
args.content = args.stream;
|
317
276
|
}
|
318
|
-
|
319
|
-
if (
|
320
|
-
|
321
|
-
// const fileName = encodeURIComponent(basename(file));
|
322
|
-
// formData.append(field, await fileFromPath(file, `utf-8''${fileName}`, { type: mime.lookup(fileName) || '' }));
|
323
|
-
const fileName = (0, path_1.basename)(file);
|
324
|
-
const fileReadable = (0, fs_1.createReadStream)(file);
|
325
|
-
formData.append(field, new BlobFromStream(fileReadable, mime_types_1.default.lookup(fileName) || ''), fileName);
|
277
|
+
if (args.files) {
|
278
|
+
if (isGETOrHEAD) {
|
279
|
+
requestOptions.method = 'POST';
|
326
280
|
}
|
327
|
-
|
328
|
-
|
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
|
+
}
|
329
288
|
}
|
330
|
-
else if (
|
331
|
-
|
332
|
-
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]);
|
333
291
|
}
|
334
|
-
|
335
|
-
|
336
|
-
requestOptions.body = formData;
|
337
|
-
}
|
338
|
-
else {
|
339
|
-
// Node.js 14 does not support spec-compliant FormData
|
340
|
-
// https://github.com/octet-stream/form-data#usage
|
341
|
-
const encoder = new form_data_encoder_1.FormDataEncoder(formData);
|
342
|
-
Object.assign(headers, encoder.headers);
|
343
|
-
// fix "Content-Length":"NaN"
|
344
|
-
delete headers['Content-Length'];
|
345
|
-
requestOptions.body = stream_1.Readable.from(encoder);
|
346
|
-
}
|
347
|
-
}
|
348
|
-
else if (args.content) {
|
349
|
-
if (!isGETOrHEAD) {
|
350
|
-
// handle content
|
351
|
-
requestOptions.body = args.content;
|
352
|
-
if (args.contentType) {
|
353
|
-
headers['content-type'] = args.contentType;
|
292
|
+
else if (typeof args.files === 'string' || Buffer.isBuffer(args.files)) {
|
293
|
+
uploadFiles.push(['file', args.files]);
|
354
294
|
}
|
355
|
-
else if (typeof args.
|
356
|
-
|
295
|
+
else if (typeof args.files === 'object') {
|
296
|
+
for (const field in args.files) {
|
297
|
+
uploadFiles.push([field, args.files[field]]);
|
298
|
+
}
|
357
299
|
}
|
358
|
-
|
359
|
-
|
360
|
-
else if (args.data) {
|
361
|
-
const isStringOrBufferOrReadable = typeof args.data === 'string'
|
362
|
-
|| Buffer.isBuffer(args.data)
|
363
|
-
|| (0, utils_1.isReadable)(args.data);
|
364
|
-
if (isGETOrHEAD) {
|
365
|
-
if (!isStringOrBufferOrReadable) {
|
300
|
+
// set normal fields first
|
301
|
+
if (args.data) {
|
366
302
|
for (const field in args.data) {
|
367
|
-
|
303
|
+
formData.append(field, args.data[field]);
|
368
304
|
}
|
369
305
|
}
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
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;
|
374
325
|
}
|
375
326
|
else {
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
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]);
|
382
356
|
}
|
383
357
|
}
|
358
|
+
}
|
359
|
+
else {
|
360
|
+
if (isStringOrBufferOrReadable) {
|
361
|
+
requestOptions.body = args.data;
|
362
|
+
}
|
384
363
|
else {
|
385
|
-
|
386
|
-
|
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
|
+
}
|
387
376
|
}
|
388
377
|
}
|
389
378
|
}
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
this.emit('request', reqMeta);
|
395
|
-
}
|
396
|
-
let response = await (0, undici_1.request)(requestUrl, requestOptions);
|
397
|
-
if (response.statusCode === 401 && response.headers['www-authenticate'] &&
|
398
|
-
!requestOptions.headers.authorization && args.digestAuth) {
|
399
|
-
// handle digest auth
|
400
|
-
const authenticateHeaders = response.headers['www-authenticate'];
|
401
|
-
const authenticate = Array.isArray(authenticateHeaders)
|
402
|
-
? authenticateHeaders.find(authHeader => authHeader.startsWith('Digest '))
|
403
|
-
: authenticateHeaders;
|
404
|
-
if (authenticate && authenticate.startsWith('Digest ')) {
|
405
|
-
debug('Request#%d %s: got digest auth header WWW-Authenticate: %s', requestId, requestUrl.href, authenticate);
|
406
|
-
requestOptions.headers.authorization = (0, utils_1.digestAuthHeader)(requestOptions.method, `${requestUrl.pathname}${requestUrl.search}`, authenticate, args.digestAuth);
|
407
|
-
debug('Request#%d %s: auth with digest header: %s', requestId, url, requestOptions.headers.authorization);
|
408
|
-
if (Array.isArray(response.headers['set-cookie'])) {
|
409
|
-
// FIXME: merge exists cookie header
|
410
|
-
requestOptions.headers.cookie = response.headers['set-cookie'].join(';');
|
411
|
-
}
|
412
|
-
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);
|
413
383
|
}
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
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
|
+
}
|
421
402
|
}
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
res.headers = response.headers;
|
430
|
-
res.status = res.statusCode = response.statusCode;
|
431
|
-
res.statusText = http_1.STATUS_CODES[res.status] || '';
|
432
|
-
if (res.headers['content-length']) {
|
433
|
-
res.size = parseInt(res.headers['content-length']);
|
434
|
-
}
|
435
|
-
let data = null;
|
436
|
-
if (args.dataType === 'stream') {
|
437
|
-
// streaming mode will disable retry
|
438
|
-
args.retry = 0;
|
439
|
-
// only auto decompress on request args.compressed = true
|
440
|
-
if (args.compressed === true && isCompressedContent) {
|
441
|
-
// gzip or br
|
442
|
-
const decoder = contentEncoding === 'gzip' ? (0, zlib_1.createGunzip)() : (0, zlib_1.createBrotliDecompress)();
|
443
|
-
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
|
+
}
|
444
410
|
}
|
445
411
|
else {
|
446
|
-
res
|
412
|
+
res.requestUrls.push(requestUrl.href);
|
413
|
+
lastUrl = requestUrl.href;
|
447
414
|
}
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
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']);
|
455
422
|
}
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
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);
|
466
432
|
}
|
467
|
-
|
468
|
-
|
469
|
-
err.name = 'UnzipError';
|
470
|
-
}
|
471
|
-
throw err;
|
433
|
+
else {
|
434
|
+
res = Object.assign(response.body, res);
|
472
435
|
}
|
473
436
|
}
|
474
|
-
if (args.
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
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);
|
480
443
|
}
|
481
444
|
else {
|
482
|
-
|
445
|
+
await pipelinePromise(response.body, args.writeStream);
|
483
446
|
}
|
484
447
|
}
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
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
|
+
}
|
506
472
|
}
|
507
|
-
requestContext.retries++;
|
508
|
-
return await __classPrivateFieldGet(this, _HttpClient_instances, "m", _HttpClient_requestInternal).call(this, url, options, requestContext);
|
509
473
|
}
|
510
|
-
|
511
|
-
|
512
|
-
this
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
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,
|
520
487
|
res,
|
521
|
-
}
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
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;
|
541
512
|
}
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
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;
|
555
545
|
}
|
556
|
-
throw err;
|
557
546
|
}
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
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
|
+
}
|
571
561
|
}
|
572
|
-
}
|
562
|
+
}
|
563
|
+
exports.HttpClient = HttpClient;
|
573
564
|
//# sourceMappingURL=HttpClient.js.map
|