urllib 3.1.2 โ 3.2.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/History.md +15 -0
- package/README.md +6 -6
- package/package.json +8 -9
- package/src/HttpClient.ts +73 -36
- package/src/Request.ts +0 -1
- package/src/Response.ts +42 -18
- package/src/cjs/HttpClient.js +71 -32
- package/src/cjs/HttpClient.js.map +1 -1
- package/src/cjs/Request.d.ts +0 -1
- package/src/cjs/diagnosticsChannel.js +132 -0
- package/src/cjs/diagnosticsChannel.js.map +1 -0
- package/src/cjs/symbols.js +15 -0
- package/src/cjs/symbols.js.map +1 -0
- package/src/cjs/utils.js +15 -1
- package/src/cjs/utils.js.map +1 -1
- package/src/diagnosticsChannel.ts +123 -0
- package/src/esm/HttpClient.js +70 -31
- package/src/esm/HttpClient.js.map +1 -1
- package/src/esm/Request.d.ts +0 -1
- package/src/esm/Response.d.ts +29 -13
- package/src/esm/cjs/Response.d.ts +1 -0
- package/src/esm/cjs/Response.js +3 -0
- package/src/esm/cjs/Response.js.map +1 -0
- package/src/esm/diagnosticsChannel.d.ts +1 -0
- package/src/esm/diagnosticsChannel.js +127 -0
- package/src/esm/diagnosticsChannel.js.map +1 -0
- package/src/esm/symbols.d.ts +13 -0
- package/src/esm/symbols.js +13 -0
- package/src/esm/symbols.js.map +1 -0
- package/src/esm/utils.d.ts +2 -0
- package/src/esm/utils.js +12 -0
- package/src/esm/utils.js.map +1 -1
- package/src/symbols.ts +12 -0
- package/src/utils.ts +15 -0
- package/src/cjs/HttpAgent.d.ts +0 -16
- package/src/cjs/HttpClient.d.ts +0 -40
- package/src/cjs/Response.d.ts +0 -37
- package/src/cjs/index.d.ts +0 -9
- package/src/cjs/utils.d.ts +0 -4
package/History.md
CHANGED
@@ -1,4 +1,19 @@
|
|
1
1
|
|
2
|
+
3.2.0 / 2022-09-26
|
3
|
+
==================
|
4
|
+
|
5
|
+
**others**
|
6
|
+
* [[`315f4a0`](http://github.com/node-modules/urllib/commit/315f4a0bf69abc2b29247fcf1a606c44412a86f3)] - ๐ฆ NEW: Support timing and socket info (#400) (fengmk2 <<fengmk2@gmail.com>>)
|
7
|
+
* [[`414692b`](http://github.com/node-modules/urllib/commit/414692b183fb205acfb163f8aef4f853ce1b97e1)] - ๐งช๏ธ TEST: Add diagnostics_channel test cases (#398) (fengmk2 <<fengmk2@gmail.com>>)
|
8
|
+
* [[`65abb60`](http://github.com/node-modules/urllib/commit/65abb6074e5098834dd964a2bedaffd64a703ef9)] - ๐ค TEST: Use vitest instead of jest (#399) (fengmk2 <<fengmk2@gmail.com>>)
|
9
|
+
* [[`5f0c4a8`](http://github.com/node-modules/urllib/commit/5f0c4a8da84ef9954942bcd78721a5445e0cb095)] - ๐ DOC: Update contributors (fengmk2 <<fengmk2@gmail.com>>)
|
10
|
+
|
11
|
+
3.1.3 / 2022-09-09
|
12
|
+
==================
|
13
|
+
|
14
|
+
**others**
|
15
|
+
* [[`65c5112`](http://github.com/node-modules/urllib/commit/65c5112aaaa8a6b6a9e324b0522fe1635e3792ba)] - ๐ FIX: Include query parameters in digest uri (#396) (Chris Byrne <<github@adapt.gen.nz>>)
|
16
|
+
|
2
17
|
3.1.2 / 2022-08-24
|
3
18
|
==================
|
4
19
|
|
package/README.md
CHANGED
@@ -251,7 +251,7 @@ Response is normal object, it contains:
|
|
251
251
|
## Run test with debug log
|
252
252
|
|
253
253
|
```bash
|
254
|
-
NODE_DEBUG=urllib npm test
|
254
|
+
NODE_DEBUG=urllib:* npm test
|
255
255
|
```
|
256
256
|
|
257
257
|
## Mocking Request
|
@@ -323,12 +323,12 @@ Fork [undici benchmarks script](https://github.com/fengmk2/undici/blob/urllib-be
|
|
323
323
|
| :---: | :---: | :---: | :---: | :---: | :---: |
|
324
324
|
|[<img src="https://avatars.githubusercontent.com/u/14790466?v=4" width="100px;"/><br/><sub><b>greenkeeperio-bot</b></sub>](https://github.com/greenkeeperio-bot)<br/>|[<img src="https://avatars.githubusercontent.com/u/227713?v=4" width="100px;"/><br/><sub><b>atian25</b></sub>](https://github.com/atian25)<br/>|[<img src="https://avatars.githubusercontent.com/u/5381764?v=4" width="100px;"/><br/><sub><b>paambaati</b></sub>](https://github.com/paambaati)<br/>|[<img src="https://avatars.githubusercontent.com/u/1433247?v=4" width="100px;"/><br/><sub><b>denghongcai</b></sub>](https://github.com/denghongcai)<br/>|[<img src="https://avatars.githubusercontent.com/u/2842176?v=4" width="100px;"/><br/><sub><b>XadillaX</b></sub>](https://github.com/XadillaX)<br/>|[<img src="https://avatars.githubusercontent.com/u/1147375?v=4" width="100px;"/><br/><sub><b>alsotang</b></sub>](https://github.com/alsotang)<br/>|
|
325
325
|
|[<img src="https://avatars.githubusercontent.com/u/546535?v=4" width="100px;"/><br/><sub><b>leoner</b></sub>](https://github.com/leoner)<br/>|[<img src="https://avatars.githubusercontent.com/u/19908330?v=4" width="100px;"/><br/><sub><b>hyj1991</b></sub>](https://github.com/hyj1991)<br/>|[<img src="https://avatars.githubusercontent.com/u/1747852?v=4" width="100px;"/><br/><sub><b>isayme</b></sub>](https://github.com/isayme)<br/>|[<img src="https://avatars.githubusercontent.com/u/6897780?v=4" width="100px;"/><br/><sub><b>killagu</b></sub>](https://github.com/killagu)<br/>|[<img src="https://avatars.githubusercontent.com/u/252317?v=4" width="100px;"/><br/><sub><b>cyjake</b></sub>](https://github.com/cyjake)<br/>|[<img src="https://avatars.githubusercontent.com/u/5856440?v=4" width="100px;"/><br/><sub><b>whxaxes</b></sub>](https://github.com/whxaxes)<br/>|
|
326
|
-
|[<img src="https://avatars.githubusercontent.com/u/309219?v=4" width="100px;"/><br/><sub><b>chadxz</b></sub>](https://github.com/chadxz)<br/>|[<img src="https://avatars.githubusercontent.com/u/
|
327
|
-
|[<img src="https://avatars.githubusercontent.com/u/
|
328
|
-
|[<img src="https://avatars.githubusercontent.com/
|
329
|
-
[<img src="https://avatars.githubusercontent.com/u/2196373?v=4" width="100px;"/><br/><sub><b>dickeylth</b></sub>](https://github.com/dickeylth)<br/>|[<img src="https://avatars.githubusercontent.com/u/13050025?v=4" width="100px;"/><br/><sub><b>aladdin-add</b></sub>](https://github.com/aladdin-add)<br/>
|
326
|
+
|[<img src="https://avatars.githubusercontent.com/u/309219?v=4" width="100px;"/><br/><sub><b>chadxz</b></sub>](https://github.com/chadxz)<br/>|[<img src="https://avatars.githubusercontent.com/u/2055702?v=4" width="100px;"/><br/><sub><b>adapt0</b></sub>](https://github.com/adapt0)<br/>|[<img src="https://avatars.githubusercontent.com/u/5139554?v=4" width="100px;"/><br/><sub><b>danielwpz</b></sub>](https://github.com/danielwpz)<br/>|[<img src="https://avatars.githubusercontent.com/u/5127897?v=4" width="100px;"/><br/><sub><b>danielsss</b></sub>](https://github.com/danielsss)<br/>|[<img src="https://avatars.githubusercontent.com/u/3367820?v=4" width="100px;"/><br/><sub><b>Jeff-Tian</b></sub>](https://github.com/Jeff-Tian)<br/>|[<img src="https://avatars.githubusercontent.com/u/32407?v=4" width="100px;"/><br/><sub><b>jedahan</b></sub>](https://github.com/jedahan)<br/>|
|
327
|
+
|[<img src="https://avatars.githubusercontent.com/u/17075261?v=4" width="100px;"/><br/><sub><b>nick-ng</b></sub>](https://github.com/nick-ng)<br/>|[<img src="https://avatars.githubusercontent.com/u/1706595?v=4" width="100px;"/><br/><sub><b>rishavsharan</b></sub>](https://github.com/rishavsharan)<br/>|[<img src="https://avatars.githubusercontent.com/u/1886161?v=4" width="100px;"/><br/><sub><b>willizm</b></sub>](https://github.com/willizm)<br/>|[<img src="https://avatars.githubusercontent.com/u/7227589?v=4" width="100px;"/><br/><sub><b>davidkhala</b></sub>](https://github.com/davidkhala)<br/>|[<img src="https://avatars.githubusercontent.com/u/535479?v=4" width="100px;"/><br/><sub><b>aleafs</b></sub>](https://github.com/aleafs)<br/>|[<img src="https://avatars.githubusercontent.com/u/3689968?v=4" width="100px;"/><br/><sub><b>Amunu</b></sub>](https://github.com/Amunu)<br/>|
|
328
|
+
|[<img src="https://avatars.githubusercontent.com/in/9426?v=4" width="100px;"/><br/><sub><b>azure-pipelines[bot]</b></sub>](https://github.com/apps/azure-pipelines)<br/>|[<img src="https://avatars.githubusercontent.com/u/1281323?v=4" width="100px;"/><br/><sub><b>changzhiwin</b></sub>](https://github.com/changzhiwin)<br/>|[<img src="https://avatars.githubusercontent.com/u/929503?v=4" width="100px;"/><br/><sub><b>yuzhigang33</b></sub>](https://github.com/yuzhigang33)<br/>|[<img src="https://avatars.githubusercontent.com/u/981128?v=4" width="100px;"/><br/><sub><b>fishbar</b></sub>](https://github.com/fishbar)<br/>|[<img src="https://avatars.githubusercontent.com/u/1207064?v=4" width="100px;"/><br/><sub><b>gxcsoccer</b></sub>](https://github.com/gxcsoccer)<br/>|[<img src="https://avatars.githubusercontent.com/u/17476119?v=4" width="100px;"/><br/><sub><b>mars-coder</b></sub>](https://github.com/mars-coder)<br/>|
|
329
|
+
[<img src="https://avatars.githubusercontent.com/u/929179?v=4" width="100px;"/><br/><sub><b>rockdai</b></sub>](https://github.com/rockdai)<br/>|[<img src="https://avatars.githubusercontent.com/u/2196373?v=4" width="100px;"/><br/><sub><b>dickeylth</b></sub>](https://github.com/dickeylth)<br/>|[<img src="https://avatars.githubusercontent.com/u/13050025?v=4" width="100px;"/><br/><sub><b>aladdin-add</b></sub>](https://github.com/aladdin-add)<br/>
|
330
330
|
|
331
|
-
This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `
|
331
|
+
This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Fri Sep 09 2022 21:45:49 GMT+0800`.
|
332
332
|
|
333
333
|
<!-- GITCONTRIBUTOR_END -->
|
334
334
|
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "urllib",
|
3
|
-
"version": "3.
|
3
|
+
"version": "3.2.0",
|
4
4
|
"publishConfig": {
|
5
5
|
"tag": "latest"
|
6
6
|
},
|
@@ -49,8 +49,9 @@
|
|
49
49
|
"build:cjs:test": "cd test/cjs && rm -rf node_modules && npm link ../.. && node index.js",
|
50
50
|
"build:esm:test": "cd test/esm && rm -rf node_modules && npm link ../.. && node index.js",
|
51
51
|
"build:test": "npm run build && npm run build:cjs:test && npm run build:esm:test",
|
52
|
-
"test": "
|
53
|
-
"
|
52
|
+
"test": "vitest run",
|
53
|
+
"cov": "vitest run --coverage --no-threads",
|
54
|
+
"ci": "npm run lint && npm run cov && npm run build:test",
|
54
55
|
"contributor": "git-contributor",
|
55
56
|
"prepack": "npm run build && rm -rf src/*.tsbuildinfo"
|
56
57
|
},
|
@@ -67,10 +68,10 @@
|
|
67
68
|
"devDependencies": {
|
68
69
|
"@types/busboy": "^1.5.0",
|
69
70
|
"@types/default-user-agent": "^1.0.0",
|
70
|
-
"@types/jest": "28",
|
71
71
|
"@types/mime-types": "^2.1.1",
|
72
72
|
"@types/pump": "^1.1.1",
|
73
73
|
"@types/selfsigned": "^2.0.1",
|
74
|
+
"@vitest/coverage-c8": "^0.23.4",
|
74
75
|
"busboy": "^1.6.0",
|
75
76
|
"coffee": "5",
|
76
77
|
"egg-ci": "2",
|
@@ -78,14 +79,12 @@
|
|
78
79
|
"eslint-config-egg": "^12.0.0",
|
79
80
|
"git-contributor": "1",
|
80
81
|
"iconv-lite": "^0.6.3",
|
81
|
-
"jest": "28",
|
82
|
-
"jest-summary-reporter": "^0.0.2",
|
83
82
|
"selfsigned": "^2.0.1",
|
84
|
-
"
|
85
|
-
"
|
83
|
+
"typescript": "^4.8.3",
|
84
|
+
"vitest": "^0.23.4"
|
86
85
|
},
|
87
86
|
"engines": {
|
88
|
-
"node": ">= 14.
|
87
|
+
"node": ">= 14.17.0"
|
89
88
|
},
|
90
89
|
"ci": {
|
91
90
|
"version": "14, 16, 18"
|
package/src/HttpClient.ts
CHANGED
@@ -26,8 +26,10 @@ import mime from 'mime-types';
|
|
26
26
|
import pump from 'pump';
|
27
27
|
import { HttpAgent, CheckAddressFunction } from './HttpAgent';
|
28
28
|
import { RequestURL, RequestOptions, HttpMethod } from './Request';
|
29
|
-
import { HttpClientResponseMeta, HttpClientResponse, ReadableWithMeta } from './Response';
|
30
|
-
import { parseJSON, sleep, digestAuthHeader } from './utils';
|
29
|
+
import { HttpClientResponseMeta, HttpClientResponse, ReadableWithMeta, BaseResponseMeta, SocketInfo } from './Response';
|
30
|
+
import { parseJSON, sleep, digestAuthHeader, globalId, performanceTime } from './utils';
|
31
|
+
import symbols from './symbols';
|
32
|
+
import { initDiagnosticsChannel } from './diagnosticsChannel';
|
31
33
|
|
32
34
|
const FormData = FormDataNative ?? FormDataNode;
|
33
35
|
// impl isReadable on Node.js 14
|
@@ -48,10 +50,7 @@ function noop() {
|
|
48
50
|
// noop
|
49
51
|
}
|
50
52
|
|
51
|
-
const
|
52
|
-
let globalRequestId = 0;
|
53
|
-
|
54
|
-
const debug = debuglog('urllib');
|
53
|
+
const debug = debuglog('urllib:HttpClient');
|
55
54
|
|
56
55
|
export type ClientOptions = {
|
57
56
|
defaultArgs?: RequestOptions;
|
@@ -127,10 +126,6 @@ function defaultIsRetry(response: HttpClientResponse) {
|
|
127
126
|
return response.status >= 500;
|
128
127
|
}
|
129
128
|
|
130
|
-
function performanceTime(startTime: number) {
|
131
|
-
return Math.floor((performance.now() - startTime) * 1000) / 1000;
|
132
|
-
}
|
133
|
-
|
134
129
|
type RequestContext = {
|
135
130
|
retries: number;
|
136
131
|
};
|
@@ -149,6 +144,7 @@ export class HttpClient extends EventEmitter {
|
|
149
144
|
connect: clientOptions.connect,
|
150
145
|
});
|
151
146
|
}
|
147
|
+
initDiagnosticsChannel();
|
152
148
|
}
|
153
149
|
|
154
150
|
async request(url: RequestURL, options?: RequestOptions) {
|
@@ -156,11 +152,7 @@ export class HttpClient extends EventEmitter {
|
|
156
152
|
}
|
157
153
|
|
158
154
|
async #requestInternal(url: RequestURL, options?: RequestOptions, requestContext?: RequestContext): Promise<HttpClientResponse> {
|
159
|
-
|
160
|
-
globalRequestId = 0;
|
161
|
-
}
|
162
|
-
const requestId = ++globalRequestId;
|
163
|
-
|
155
|
+
const requestId = globalId('HttpClientRequest');
|
164
156
|
const requestUrl = typeof url === 'string' ? new URL(url) : url;
|
165
157
|
const args = {
|
166
158
|
retry: 0,
|
@@ -173,6 +165,32 @@ export class HttpClient extends EventEmitter {
|
|
173
165
|
};
|
174
166
|
const requestStartTime = performance.now();
|
175
167
|
|
168
|
+
// https://developer.chrome.com/docs/devtools/network/reference/?utm_source=devtools#timing-explanation
|
169
|
+
const timing = {
|
170
|
+
// socket assigned
|
171
|
+
queuing: 0,
|
172
|
+
// dns lookup time
|
173
|
+
// dnslookup: 0,
|
174
|
+
// socket connected
|
175
|
+
connected: 0,
|
176
|
+
// request headers sent
|
177
|
+
requestHeadersSent: 0,
|
178
|
+
// request sent, including headers and body
|
179
|
+
requestSent: 0,
|
180
|
+
// Time to first byte (TTFB), the response headers have been received
|
181
|
+
waiting: 0,
|
182
|
+
// the response body and trailers have been received
|
183
|
+
contentDownload: 0,
|
184
|
+
};
|
185
|
+
const orginalOpaque = args.opaque;
|
186
|
+
// using opaque to diagnostics channel, binding request and socket
|
187
|
+
const internalOpaque = {
|
188
|
+
[symbols.kRequestId]: requestId,
|
189
|
+
[symbols.kRequestStartTime]: requestStartTime,
|
190
|
+
[symbols.kEnableRequestTiming]: !!args.timing,
|
191
|
+
[symbols.kRequestTiming]: timing,
|
192
|
+
[symbols.kRequestOrginalOpaque]: orginalOpaque,
|
193
|
+
};
|
176
194
|
const reqMeta = {
|
177
195
|
requestId,
|
178
196
|
url: requestUrl.href,
|
@@ -180,6 +198,18 @@ export class HttpClient extends EventEmitter {
|
|
180
198
|
ctx: args.ctx,
|
181
199
|
retries: requestContext.retries,
|
182
200
|
};
|
201
|
+
const socketInfo = {
|
202
|
+
id: 0,
|
203
|
+
localAddress: '',
|
204
|
+
localPort: 0,
|
205
|
+
remoteAddress: '',
|
206
|
+
remotePort: 0,
|
207
|
+
remoteFamily: '',
|
208
|
+
bytesWritten: 0,
|
209
|
+
bytesRead: 0,
|
210
|
+
handledRequests: 0,
|
211
|
+
handledResponses: 0,
|
212
|
+
};
|
183
213
|
// keep urllib createCallbackResponse style
|
184
214
|
const resHeaders: IncomingHttpHeaders = {};
|
185
215
|
const res: HttpClientResponseMeta = {
|
@@ -191,10 +221,8 @@ export class HttpClient extends EventEmitter {
|
|
191
221
|
rt: 0,
|
192
222
|
keepAliveSocket: true,
|
193
223
|
requestUrls: [],
|
194
|
-
timing
|
195
|
-
|
196
|
-
contentDownload: 0,
|
197
|
-
},
|
224
|
+
timing,
|
225
|
+
socket: socketInfo,
|
198
226
|
};
|
199
227
|
|
200
228
|
let headersTimeout = 5000;
|
@@ -245,7 +273,6 @@ export class HttpClient extends EventEmitter {
|
|
245
273
|
headers.authorization = `Basic ${Buffer.from(args.auth).toString('base64')}`;
|
246
274
|
}
|
247
275
|
|
248
|
-
let opaque = args.opaque;
|
249
276
|
try {
|
250
277
|
const requestOptions: UndiciRquestOptions = {
|
251
278
|
method,
|
@@ -253,7 +280,7 @@ export class HttpClient extends EventEmitter {
|
|
253
280
|
maxRedirections: args.maxRedirects ?? 10,
|
254
281
|
headersTimeout,
|
255
282
|
bodyTimeout,
|
256
|
-
opaque,
|
283
|
+
opaque: internalOpaque,
|
257
284
|
dispatcher: this.#dispatcher,
|
258
285
|
};
|
259
286
|
if (args.followRedirect === false) {
|
@@ -373,7 +400,7 @@ export class HttpClient extends EventEmitter {
|
|
373
400
|
if (authenticate.startsWith('Digest ')) {
|
374
401
|
debug('Request#%d %s: got digest auth header WWW-Authenticate: %s', requestId, requestUrl.href, authenticate);
|
375
402
|
requestOptions.headers.authorization = digestAuthHeader(requestOptions.method!,
|
376
|
-
requestUrl.pathname
|
403
|
+
`${requestUrl.pathname}${requestUrl.search}`, authenticate, args.digestAuth);
|
377
404
|
debug('Request#%d %s: auth with digest header: %s', requestId, url, requestOptions.headers.authorization);
|
378
405
|
if (response.headers['set-cookie']) {
|
379
406
|
// FIXME: merge exists cookie header
|
@@ -383,11 +410,6 @@ export class HttpClient extends EventEmitter {
|
|
383
410
|
}
|
384
411
|
}
|
385
412
|
|
386
|
-
opaque = response.opaque;
|
387
|
-
if (args.timing) {
|
388
|
-
res.timing.waiting = performanceTime(requestStartTime);
|
389
|
-
}
|
390
|
-
|
391
413
|
const context = response.context as { history: URL[] };
|
392
414
|
let lastUrl = '';
|
393
415
|
if (context?.history) {
|
@@ -413,10 +435,12 @@ export class HttpClient extends EventEmitter {
|
|
413
435
|
if (args.dataType === 'stream') {
|
414
436
|
// streaming mode will disable retry
|
415
437
|
args.retry = 0;
|
416
|
-
const meta = {
|
438
|
+
const meta: BaseResponseMeta = {
|
417
439
|
status: res.status,
|
418
440
|
statusCode: res.statusCode,
|
419
441
|
headers: res.headers,
|
442
|
+
timing,
|
443
|
+
socket: socketInfo,
|
420
444
|
};
|
421
445
|
if (isCompressedContent) {
|
422
446
|
// gzip or br
|
@@ -458,12 +482,11 @@ export class HttpClient extends EventEmitter {
|
|
458
482
|
}
|
459
483
|
}
|
460
484
|
res.rt = performanceTime(requestStartTime);
|
461
|
-
|
462
|
-
|
463
|
-
}
|
485
|
+
// get real socket info from internalOpaque
|
486
|
+
this.#updateSocketInfo(socketInfo, internalOpaque);
|
464
487
|
|
465
488
|
const clientResponse: HttpClientResponse = {
|
466
|
-
opaque,
|
489
|
+
opaque: orginalOpaque,
|
467
490
|
data,
|
468
491
|
status: res.status,
|
469
492
|
statusCode: res.status,
|
@@ -507,7 +530,7 @@ export class HttpClient extends EventEmitter {
|
|
507
530
|
} else if (err.name === 'BodyTimeoutError') {
|
508
531
|
err = new HttpClientRequestTimeoutError(bodyTimeout, { cause: e });
|
509
532
|
}
|
510
|
-
err.opaque =
|
533
|
+
err.opaque = orginalOpaque;
|
511
534
|
err.status = res.status;
|
512
535
|
err.headers = res.headers;
|
513
536
|
err.res = res;
|
@@ -516,9 +539,7 @@ export class HttpClient extends EventEmitter {
|
|
516
539
|
res.requestUrls.push(requestUrl.href);
|
517
540
|
}
|
518
541
|
res.rt = performanceTime(requestStartTime);
|
519
|
-
|
520
|
-
res.timing.contentDownload = res.rt;
|
521
|
-
}
|
542
|
+
this.#updateSocketInfo(socketInfo, internalOpaque);
|
522
543
|
|
523
544
|
if (this.listenerCount('response') > 0) {
|
524
545
|
this.emit('response', {
|
@@ -532,4 +553,20 @@ export class HttpClient extends EventEmitter {
|
|
532
553
|
throw err;
|
533
554
|
}
|
534
555
|
}
|
556
|
+
|
557
|
+
#updateSocketInfo(socketInfo: SocketInfo, internalOpaque: any) {
|
558
|
+
const socket = internalOpaque[symbols.kRequestSocket];
|
559
|
+
if (socket) {
|
560
|
+
socketInfo.id = socket[symbols.kSocketId];
|
561
|
+
socketInfo.handledRequests = socket[symbols.kHandledRequests];
|
562
|
+
socketInfo.handledResponses = socket[symbols.kHandledResponses];
|
563
|
+
socketInfo.localAddress = socket.localAddress;
|
564
|
+
socketInfo.localPort = socket.localPort;
|
565
|
+
socketInfo.remoteAddress = socket.remoteAddress;
|
566
|
+
socketInfo.remotePort = socket.remotePort;
|
567
|
+
socketInfo.remoteFamily = socket.remoteFamily;
|
568
|
+
socketInfo.bytesRead = socket.bytesRead;
|
569
|
+
socketInfo.bytesWritten = socket.bytesWritten;
|
570
|
+
}
|
571
|
+
}
|
535
572
|
}
|
package/src/Request.ts
CHANGED
package/src/Response.ts
CHANGED
@@ -1,37 +1,61 @@
|
|
1
1
|
import { Readable } from 'stream';
|
2
2
|
import { IncomingHttpHeaders } from 'http';
|
3
3
|
|
4
|
-
export type
|
4
|
+
export type SocketInfo = {
|
5
|
+
id: number;
|
6
|
+
localAddress: string;
|
7
|
+
localPort: number;
|
8
|
+
remoteAddress: string;
|
9
|
+
remotePort: number;
|
10
|
+
remoteFamily: string;
|
11
|
+
bytesWritten: number;
|
12
|
+
bytesRead: number;
|
13
|
+
handledRequests: number;
|
14
|
+
handledResponses: number;
|
15
|
+
};
|
16
|
+
|
17
|
+
/**
|
18
|
+
* https://eggjs.org/en/core/httpclient.html#timing-boolean
|
19
|
+
*/
|
20
|
+
export type Timing = {
|
21
|
+
// socket assigned
|
22
|
+
queuing: number;
|
23
|
+
// dns lookup time
|
24
|
+
// dnslookup: number;
|
25
|
+
// socket connected
|
26
|
+
connected: number;
|
27
|
+
// request headers sent
|
28
|
+
requestHeadersSent: number;
|
29
|
+
// request sent, including headers and body
|
30
|
+
requestSent: number;
|
31
|
+
// Time to first byte (TTFB), the response headers have been received
|
32
|
+
waiting: number;
|
33
|
+
// the response body and trailers have been received
|
34
|
+
contentDownload: number;
|
35
|
+
};
|
36
|
+
|
37
|
+
export type BaseResponseMeta = {
|
5
38
|
status: number;
|
6
39
|
statusCode: number;
|
7
40
|
headers: IncomingHttpHeaders;
|
41
|
+
timing: Timing;
|
42
|
+
// SocketInfo
|
43
|
+
socket: SocketInfo;
|
44
|
+
};
|
45
|
+
|
46
|
+
export type HttpClientResponseMeta = BaseResponseMeta & {
|
8
47
|
size: number;
|
9
48
|
aborted: boolean;
|
10
49
|
rt: number;
|
11
50
|
keepAliveSocket: boolean;
|
12
51
|
requestUrls: string[];
|
13
|
-
/**
|
14
|
-
* https://eggjs.org/en/core/httpclient.html#timing-boolean
|
15
|
-
*/
|
16
|
-
timing: {
|
17
|
-
contentDownload: number;
|
18
|
-
waiting: number;
|
19
|
-
};
|
20
|
-
// remoteAddress: remoteAddress,
|
21
|
-
// remotePort: remotePort,
|
22
|
-
// socketHandledRequests: socketHandledRequests,
|
23
|
-
// socketHandledResponses: socketHandledResponses,
|
24
52
|
};
|
25
53
|
|
26
|
-
export type ReadableWithMeta = Readable &
|
27
|
-
status: number;
|
28
|
-
statusCode: number;
|
29
|
-
headers: IncomingHttpHeaders;
|
30
|
-
};
|
54
|
+
export type ReadableWithMeta = Readable & BaseResponseMeta;
|
31
55
|
|
32
56
|
export type HttpClientResponse = {
|
33
57
|
opaque: unknown;
|
34
|
-
data: any
|
58
|
+
data: any;
|
35
59
|
status: number;
|
36
60
|
// alias to status, keep compatibility
|
37
61
|
statusCode: number;
|
package/src/cjs/HttpClient.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
"use strict";
|
2
2
|
var _a, _b, _c;
|
3
|
-
var _BlobFromStream_stream, _BlobFromStream_type, _HttpClient_instances, _HttpClient_defaultArgs, _HttpClient_dispatcher, _HttpClient_requestInternal;
|
3
|
+
var _BlobFromStream_stream, _BlobFromStream_type, _HttpClient_instances, _HttpClient_defaultArgs, _HttpClient_dispatcher, _HttpClient_requestInternal, _HttpClient_updateSocketInfo;
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
5
5
|
exports.HttpClient = exports.HEADER_USER_AGENT = void 0;
|
6
6
|
const tslib_1 = require("tslib");
|
@@ -21,6 +21,8 @@ const mime_types_1 = tslib_1.__importDefault(require("mime-types"));
|
|
21
21
|
const pump_1 = tslib_1.__importDefault(require("pump"));
|
22
22
|
const HttpAgent_1 = require("./HttpAgent");
|
23
23
|
const utils_1 = require("./utils");
|
24
|
+
const symbols_1 = tslib_1.__importDefault(require("./symbols"));
|
25
|
+
const diagnosticsChannel_1 = require("./diagnosticsChannel");
|
24
26
|
const FormData = undici_1.FormData !== null && undici_1.FormData !== void 0 ? undici_1.FormData : formdata_node_1.FormData;
|
25
27
|
// impl isReadable on Node.js 14
|
26
28
|
const isReadable = (_a = stream_2.default.isReadable) !== null && _a !== void 0 ? _a : function isReadable(stream) {
|
@@ -39,9 +41,7 @@ const pipelinePromise = (_c = (_b = stream_2.default.promises) === null || _b ==
|
|
39
41
|
function noop() {
|
40
42
|
// noop
|
41
43
|
}
|
42
|
-
const
|
43
|
-
let globalRequestId = 0;
|
44
|
-
const debug = (0, util_1.debuglog)('urllib');
|
44
|
+
const debug = (0, util_1.debuglog)('urllib:HttpClient');
|
45
45
|
// https://github.com/octet-stream/form-data
|
46
46
|
class BlobFromStream {
|
47
47
|
constructor(stream, type) {
|
@@ -79,9 +79,6 @@ function getFileName(stream) {
|
|
79
79
|
function defaultIsRetry(response) {
|
80
80
|
return response.status >= 500;
|
81
81
|
}
|
82
|
-
function performanceTime(startTime) {
|
83
|
-
return Math.floor((perf_hooks_1.performance.now() - startTime) * 1000) / 1000;
|
84
|
-
}
|
85
82
|
class HttpClient extends events_1.EventEmitter {
|
86
83
|
constructor(clientOptions) {
|
87
84
|
super();
|
@@ -96,6 +93,7 @@ class HttpClient extends events_1.EventEmitter {
|
|
96
93
|
connect: clientOptions.connect,
|
97
94
|
}), "f");
|
98
95
|
}
|
96
|
+
(0, diagnosticsChannel_1.initDiagnosticsChannel)();
|
99
97
|
}
|
100
98
|
async request(url, options) {
|
101
99
|
return await tslib_1.__classPrivateFieldGet(this, _HttpClient_instances, "m", _HttpClient_requestInternal).call(this, url, options);
|
@@ -104,10 +102,7 @@ class HttpClient extends events_1.EventEmitter {
|
|
104
102
|
exports.HttpClient = HttpClient;
|
105
103
|
_HttpClient_defaultArgs = new WeakMap(), _HttpClient_dispatcher = new WeakMap(), _HttpClient_instances = new WeakSet(), _HttpClient_requestInternal = async function _HttpClient_requestInternal(url, options, requestContext) {
|
106
104
|
var _a, _b, _c, _d, _e, _f;
|
107
|
-
|
108
|
-
globalRequestId = 0;
|
109
|
-
}
|
110
|
-
const requestId = ++globalRequestId;
|
105
|
+
const requestId = (0, utils_1.globalId)('HttpClientRequest');
|
111
106
|
const requestUrl = typeof url === 'string' ? new URL(url) : url;
|
112
107
|
const args = {
|
113
108
|
retry: 0,
|
@@ -119,6 +114,32 @@ _HttpClient_defaultArgs = new WeakMap(), _HttpClient_dispatcher = new WeakMap(),
|
|
119
114
|
...requestContext,
|
120
115
|
};
|
121
116
|
const requestStartTime = perf_hooks_1.performance.now();
|
117
|
+
// https://developer.chrome.com/docs/devtools/network/reference/?utm_source=devtools#timing-explanation
|
118
|
+
const timing = {
|
119
|
+
// socket assigned
|
120
|
+
queuing: 0,
|
121
|
+
// dns lookup time
|
122
|
+
// dnslookup: 0,
|
123
|
+
// socket connected
|
124
|
+
connected: 0,
|
125
|
+
// request headers sent
|
126
|
+
requestHeadersSent: 0,
|
127
|
+
// request sent, including headers and body
|
128
|
+
requestSent: 0,
|
129
|
+
// Time to first byte (TTFB), the response headers have been received
|
130
|
+
waiting: 0,
|
131
|
+
// the response body and trailers have been received
|
132
|
+
contentDownload: 0,
|
133
|
+
};
|
134
|
+
const orginalOpaque = args.opaque;
|
135
|
+
// using opaque to diagnostics channel, binding request and socket
|
136
|
+
const internalOpaque = {
|
137
|
+
[symbols_1.default.kRequestId]: requestId,
|
138
|
+
[symbols_1.default.kRequestStartTime]: requestStartTime,
|
139
|
+
[symbols_1.default.kEnableRequestTiming]: !!args.timing,
|
140
|
+
[symbols_1.default.kRequestTiming]: timing,
|
141
|
+
[symbols_1.default.kRequestOrginalOpaque]: orginalOpaque,
|
142
|
+
};
|
122
143
|
const reqMeta = {
|
123
144
|
requestId,
|
124
145
|
url: requestUrl.href,
|
@@ -126,6 +147,18 @@ _HttpClient_defaultArgs = new WeakMap(), _HttpClient_dispatcher = new WeakMap(),
|
|
126
147
|
ctx: args.ctx,
|
127
148
|
retries: requestContext.retries,
|
128
149
|
};
|
150
|
+
const socketInfo = {
|
151
|
+
id: 0,
|
152
|
+
localAddress: '',
|
153
|
+
localPort: 0,
|
154
|
+
remoteAddress: '',
|
155
|
+
remotePort: 0,
|
156
|
+
remoteFamily: '',
|
157
|
+
bytesWritten: 0,
|
158
|
+
bytesRead: 0,
|
159
|
+
handledRequests: 0,
|
160
|
+
handledResponses: 0,
|
161
|
+
};
|
129
162
|
// keep urllib createCallbackResponse style
|
130
163
|
const resHeaders = {};
|
131
164
|
const res = {
|
@@ -137,10 +170,8 @@ _HttpClient_defaultArgs = new WeakMap(), _HttpClient_dispatcher = new WeakMap(),
|
|
137
170
|
rt: 0,
|
138
171
|
keepAliveSocket: true,
|
139
172
|
requestUrls: [],
|
140
|
-
timing
|
141
|
-
|
142
|
-
contentDownload: 0,
|
143
|
-
},
|
173
|
+
timing,
|
174
|
+
socket: socketInfo,
|
144
175
|
};
|
145
176
|
let headersTimeout = 5000;
|
146
177
|
let bodyTimeout = 5000;
|
@@ -190,7 +221,6 @@ _HttpClient_defaultArgs = new WeakMap(), _HttpClient_dispatcher = new WeakMap(),
|
|
190
221
|
if (args.auth && !headers.authorization) {
|
191
222
|
headers.authorization = `Basic ${Buffer.from(args.auth).toString('base64')}`;
|
192
223
|
}
|
193
|
-
let opaque = args.opaque;
|
194
224
|
try {
|
195
225
|
const requestOptions = {
|
196
226
|
method,
|
@@ -198,7 +228,7 @@ _HttpClient_defaultArgs = new WeakMap(), _HttpClient_dispatcher = new WeakMap(),
|
|
198
228
|
maxRedirections: (_d = args.maxRedirects) !== null && _d !== void 0 ? _d : 10,
|
199
229
|
headersTimeout,
|
200
230
|
bodyTimeout,
|
201
|
-
opaque,
|
231
|
+
opaque: internalOpaque,
|
202
232
|
dispatcher: tslib_1.__classPrivateFieldGet(this, _HttpClient_dispatcher, "f"),
|
203
233
|
};
|
204
234
|
if (args.followRedirect === false) {
|
@@ -323,7 +353,7 @@ _HttpClient_defaultArgs = new WeakMap(), _HttpClient_dispatcher = new WeakMap(),
|
|
323
353
|
const authenticate = response.headers['www-authenticate'];
|
324
354
|
if (authenticate.startsWith('Digest ')) {
|
325
355
|
debug('Request#%d %s: got digest auth header WWW-Authenticate: %s', requestId, requestUrl.href, authenticate);
|
326
|
-
requestOptions.headers.authorization = (0, utils_1.digestAuthHeader)(requestOptions.method, requestUrl.pathname
|
356
|
+
requestOptions.headers.authorization = (0, utils_1.digestAuthHeader)(requestOptions.method, `${requestUrl.pathname}${requestUrl.search}`, authenticate, args.digestAuth);
|
327
357
|
debug('Request#%d %s: auth with digest header: %s', requestId, url, requestOptions.headers.authorization);
|
328
358
|
if (response.headers['set-cookie']) {
|
329
359
|
// FIXME: merge exists cookie header
|
@@ -332,10 +362,6 @@ _HttpClient_defaultArgs = new WeakMap(), _HttpClient_dispatcher = new WeakMap(),
|
|
332
362
|
response = await (0, undici_1.request)(requestUrl, requestOptions);
|
333
363
|
}
|
334
364
|
}
|
335
|
-
opaque = response.opaque;
|
336
|
-
if (args.timing) {
|
337
|
-
res.timing.waiting = performanceTime(requestStartTime);
|
338
|
-
}
|
339
365
|
const context = response.context;
|
340
366
|
let lastUrl = '';
|
341
367
|
if (context === null || context === void 0 ? void 0 : context.history) {
|
@@ -364,6 +390,8 @@ _HttpClient_defaultArgs = new WeakMap(), _HttpClient_dispatcher = new WeakMap(),
|
|
364
390
|
status: res.status,
|
365
391
|
statusCode: res.statusCode,
|
366
392
|
headers: res.headers,
|
393
|
+
timing,
|
394
|
+
socket: socketInfo,
|
367
395
|
};
|
368
396
|
if (isCompressedContent) {
|
369
397
|
// gzip or br
|
@@ -411,12 +439,11 @@ _HttpClient_defaultArgs = new WeakMap(), _HttpClient_dispatcher = new WeakMap(),
|
|
411
439
|
}
|
412
440
|
}
|
413
441
|
}
|
414
|
-
res.rt = performanceTime(requestStartTime);
|
415
|
-
|
416
|
-
|
417
|
-
}
|
442
|
+
res.rt = (0, utils_1.performanceTime)(requestStartTime);
|
443
|
+
// get real socket info from internalOpaque
|
444
|
+
tslib_1.__classPrivateFieldGet(this, _HttpClient_instances, "m", _HttpClient_updateSocketInfo).call(this, socketInfo, internalOpaque);
|
418
445
|
const clientResponse = {
|
419
|
-
opaque,
|
446
|
+
opaque: orginalOpaque,
|
420
447
|
data,
|
421
448
|
status: res.status,
|
422
449
|
statusCode: res.status,
|
@@ -459,7 +486,7 @@ _HttpClient_defaultArgs = new WeakMap(), _HttpClient_dispatcher = new WeakMap(),
|
|
459
486
|
else if (err.name === 'BodyTimeoutError') {
|
460
487
|
err = new HttpClientRequestTimeoutError(bodyTimeout, { cause: e });
|
461
488
|
}
|
462
|
-
err.opaque =
|
489
|
+
err.opaque = orginalOpaque;
|
463
490
|
err.status = res.status;
|
464
491
|
err.headers = res.headers;
|
465
492
|
err.res = res;
|
@@ -467,10 +494,8 @@ _HttpClient_defaultArgs = new WeakMap(), _HttpClient_dispatcher = new WeakMap(),
|
|
467
494
|
if (res.requestUrls.length === 0) {
|
468
495
|
res.requestUrls.push(requestUrl.href);
|
469
496
|
}
|
470
|
-
res.rt = performanceTime(requestStartTime);
|
471
|
-
|
472
|
-
res.timing.contentDownload = res.rt;
|
473
|
-
}
|
497
|
+
res.rt = (0, utils_1.performanceTime)(requestStartTime);
|
498
|
+
tslib_1.__classPrivateFieldGet(this, _HttpClient_instances, "m", _HttpClient_updateSocketInfo).call(this, socketInfo, internalOpaque);
|
474
499
|
if (this.listenerCount('response') > 0) {
|
475
500
|
this.emit('response', {
|
476
501
|
requestId,
|
@@ -482,5 +507,19 @@ _HttpClient_defaultArgs = new WeakMap(), _HttpClient_dispatcher = new WeakMap(),
|
|
482
507
|
}
|
483
508
|
throw err;
|
484
509
|
}
|
510
|
+
}, _HttpClient_updateSocketInfo = function _HttpClient_updateSocketInfo(socketInfo, internalOpaque) {
|
511
|
+
const socket = internalOpaque[symbols_1.default.kRequestSocket];
|
512
|
+
if (socket) {
|
513
|
+
socketInfo.id = socket[symbols_1.default.kSocketId];
|
514
|
+
socketInfo.handledRequests = socket[symbols_1.default.kHandledRequests];
|
515
|
+
socketInfo.handledResponses = socket[symbols_1.default.kHandledResponses];
|
516
|
+
socketInfo.localAddress = socket.localAddress;
|
517
|
+
socketInfo.localPort = socket.localPort;
|
518
|
+
socketInfo.remoteAddress = socket.remoteAddress;
|
519
|
+
socketInfo.remotePort = socket.remotePort;
|
520
|
+
socketInfo.remoteFamily = socket.remoteFamily;
|
521
|
+
socketInfo.bytesRead = socket.bytesRead;
|
522
|
+
socketInfo.bytesWritten = socket.bytesWritten;
|
523
|
+
}
|
485
524
|
};
|
486
525
|
//# sourceMappingURL=HttpClient.js.map
|