urllib 3.1.3 โ†’ 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 CHANGED
@@ -1,4 +1,13 @@
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
+
2
11
  3.1.3 / 2022-09-09
3
12
  ==================
4
13
 
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/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/>|[<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/>|
327
- |[<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/>|[<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/>|
328
- |[<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/>|[<img src="https://avatars.githubusercontent.com/u/929179?v=4" width="100px;"/><br/><sub><b>rockdai</b></sub>](https://github.com/rockdai)<br/>|
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 `Tue Jul 05 2022 16:17:31 GMT+0800`.
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.1.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": "tsc --version && jest --coverage",
53
- "ci": "npm run lint && npm run test && npm run build:test",
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
- "ts-jest": "28",
85
- "typescript": "4"
83
+ "typescript": "^4.8.3",
84
+ "vitest": "^0.23.4"
86
85
  },
87
86
  "engines": {
88
- "node": ">= 14.0.0"
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 MAX_REQURE_ID_VALUE = Math.pow(2, 31) - 10;
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
- if (globalRequestId >= MAX_REQURE_ID_VALUE) {
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
- waiting: 0,
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) {
@@ -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
- if (args.timing) {
462
- res.timing.contentDownload = res.rt;
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 = 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
- if (args.timing) {
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
@@ -96,7 +96,6 @@ export type RequestOptions = {
96
96
  * */
97
97
  gzip?: boolean;
98
98
  /**
99
- * @deprecated
100
99
  * Enable timing or not, default is false.
101
100
  * */
102
101
  timing?: boolean;
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 HttpClientResponseMeta = {
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;
@@ -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 MAX_REQURE_ID_VALUE = Math.pow(2, 31) - 10;
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
- if (globalRequestId >= MAX_REQURE_ID_VALUE) {
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
- waiting: 0,
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) {
@@ -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
- if (args.timing) {
416
- res.timing.contentDownload = res.rt;
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 = 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
- if (args.timing) {
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