urllib 3.25.0 → 3.26.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/README.md CHANGED
@@ -1,19 +1,19 @@
1
- # urllib
1
+ # urllib@3
2
2
 
3
3
  [![NPM version][npm-image]][npm-url]
4
- [![Node.js CI](https://github.com/node-modules/urllib/actions/workflows/nodejs.yml/badge.svg)](https://github.com/node-modules/urllib/actions/workflows/nodejs.yml)
4
+ [![CI for 3.x](https://github.com/node-modules/urllib/actions/workflows/nodejs-3.x.yml/badge.svg?branch=3.x)](https://github.com/node-modules/urllib/actions/workflows/nodejs-3.x.yml)
5
5
  [![Test coverage][codecov-image]][codecov-url]
6
6
  [![Known Vulnerabilities][snyk-image]][snyk-url]
7
7
  [![npm download][download-image]][download-url]
8
8
 
9
- [npm-image]: https://img.shields.io/npm/v/urllib.svg?style=flat-square
10
- [npm-url]: https://npmjs.org/package/urllib
11
- [codecov-image]: https://codecov.io/gh/node-modules/urllib/branch/master/graph/badge.svg
12
- [codecov-url]: https://codecov.io/gh/node-modules/urllib
9
+ [npm-image]: https://img.shields.io/npm/v/urllib/release-3.x.svg?style=flat-square
10
+ [npm-url]: https://npmjs.org/package/urllib/v/release-3.x
11
+ [codecov-image]: https://codecov.io/gh/node-modules/urllib/branch/3.x/graph/badge.svg
12
+ [codecov-url]: https://app.codecov.io/gh/node-modules/urllib/tree/3.x
13
13
  [snyk-image]: https://snyk.io/test/npm/urllib/badge.svg?style=flat-square
14
14
  [snyk-url]: https://snyk.io/test/npm/urllib
15
15
  [download-image]: https://img.shields.io/npm/dm/urllib.svg?style=flat-square
16
- [download-url]: https://npmjs.org/package/urllib
16
+ [download-url]: https://npmjs.org/package/urllib/v/release-3.x
17
17
 
18
18
  Request HTTP URLs in a complex world — basic
19
19
  and digest authentication, redirections, timeout and more.
@@ -21,7 +21,7 @@ and digest authentication, redirections, timeout and more.
21
21
  ## Install
22
22
 
23
23
  ```bash
24
- npm install urllib
24
+ npm install urllib@3
25
25
  ```
26
26
 
27
27
  ## Usage
@@ -209,6 +209,22 @@ Response is normal object, it contains:
209
209
  NODE_DEBUG=urllib:* npm test
210
210
  ```
211
211
 
212
+ ## Request with HTTP2
213
+
214
+ Create a HttpClient with `options.allowH2 = true`
215
+
216
+ ```ts
217
+ import { HttpClient } from 'urllib';
218
+
219
+ const httpClient = new HttpClient({
220
+ allowH2: true,
221
+ });
222
+
223
+ const response = await httpClient.request('https://node.js.org');
224
+ console.log(response.status);
225
+ console.log(response.headers);
226
+ ```
227
+
212
228
  ## Mocking Request
213
229
 
214
230
  export from [undici](https://undici.nodejs.org/#/docs/best-practices/mocking-request)
@@ -283,6 +299,9 @@ Fork [undici benchmarks script](https://github.com/fengmk2/undici/blob/urllib-be
283
299
  | undici - stream | 45 | 12523.45 req/sec | ± 2.97 % | + 754.61 % |
284
300
  | undici - dispatch | 51 | 12970.18 req/sec | ± 3.15 % | + 785.10 % |
285
301
 
302
+ ## License
303
+
304
+ [MIT](LICENSE)
286
305
 
287
306
  <!-- GITCONTRIBUTOR_START -->
288
307
 
@@ -301,7 +320,3 @@ Fork [undici benchmarks script](https://github.com/fengmk2/undici/blob/urllib-be
301
320
  This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Mon Dec 04 2023 00:13:39 GMT+0800`.
302
321
 
303
322
  <!-- GITCONTRIBUTOR_END -->
304
-
305
- ## License
306
-
307
- [MIT](LICENSE)
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  import { LookupFunction } from 'node:net';
3
2
  import { Agent, Dispatcher, buildConnector } from 'undici';
4
3
  export type CheckAddressFunction = (ip: string, family: number | string) => boolean;
@@ -6,6 +5,7 @@ export type HttpAgentOptions = {
6
5
  lookup?: LookupFunction;
7
6
  checkAddress?: CheckAddressFunction;
8
7
  connect?: buildConnector.BuildOptions;
8
+ allowH2?: boolean;
9
9
  };
10
10
  export declare class HttpAgent extends Agent {
11
11
  #private;
@@ -54,7 +54,7 @@ class HttpAgent extends undici_1.Agent {
54
54
  });
55
55
  };
56
56
  super({
57
- connect: { ...options.connect, lookup },
57
+ connect: { ...options.connect, lookup, allowH2: options.allowH2 },
58
58
  });
59
59
  this.#checkAddress = options.checkAddress;
60
60
  }
@@ -1,6 +1,3 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
- /// <reference types="node" />
4
1
  import { EventEmitter } from 'node:events';
5
2
  import { LookupFunction } from 'node:net';
6
3
  import { Dispatcher } from 'undici';
@@ -9,6 +6,8 @@ import { RequestURL, RequestOptions, RequestMeta } from './Request.js';
9
6
  import { RawResponseWithMeta, HttpClientResponse } from './Response.js';
10
7
  export type ClientOptions = {
11
8
  defaultArgs?: RequestOptions;
9
+ /** Allow to use HTTP2 first. Default is `false` */
10
+ allowH2?: boolean;
12
11
  /**
13
12
  * Custom DNS lookup function, default is `dns.lookup`.
14
13
  */
@@ -68,7 +68,7 @@ class BlobFromStream {
68
68
  return 'Blob';
69
69
  }
70
70
  }
71
- exports.HEADER_USER_AGENT = (0, default_user_agent_1.default)('node-urllib', '3.25.0');
71
+ exports.HEADER_USER_AGENT = (0, default_user_agent_1.default)('node-urllib', '3.26.0');
72
72
  function getFileName(stream) {
73
73
  const filePath = stream.path;
74
74
  if (filePath) {
@@ -94,11 +94,19 @@ class HttpClient extends node_events_1.EventEmitter {
94
94
  lookup: clientOptions.lookup,
95
95
  checkAddress: clientOptions.checkAddress,
96
96
  connect: clientOptions.connect,
97
+ allowH2: clientOptions.allowH2,
97
98
  });
98
99
  }
99
100
  else if (clientOptions?.connect) {
100
101
  this.#dispatcher = new undici_1.Agent({
101
102
  connect: clientOptions.connect,
103
+ allowH2: clientOptions.allowH2,
104
+ });
105
+ }
106
+ else if (clientOptions?.allowH2) {
107
+ // Support HTTP2
108
+ this.#dispatcher = new undici_1.Agent({
109
+ allowH2: clientOptions.allowH2,
102
110
  });
103
111
  }
104
112
  (0, diagnosticsChannel_js_1.initDiagnosticsChannel)();
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  import type { Except } from 'type-fest';
3
2
  import type { IncomingHttpHeaders as HTTPIncomingHttpHeaders } from 'node:http';
4
3
  export interface IncomingHttpHeaders extends Except<HTTPIncomingHttpHeaders, 'set-cookie'> {
@@ -1,7 +1,3 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
- /// <reference types="node" />
4
- /// <reference types="node" />
5
1
  import type { Readable, Writable } from 'node:stream';
6
2
  import type { EventEmitter } from 'node:events';
7
3
  import type { Dispatcher } from 'undici';
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  import type { Readable } from 'node:stream';
3
2
  import type { IncomingHttpHeaders } from './IncomingHttpHeaders.js';
4
3
  export type SocketInfo = {
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.initDiagnosticsChannel = void 0;
6
+ exports.initDiagnosticsChannel = initDiagnosticsChannel;
7
7
  const node_diagnostics_channel_1 = __importDefault(require("node:diagnostics_channel"));
8
8
  const node_perf_hooks_1 = require("node:perf_hooks");
9
9
  const node_util_1 = require("node:util");
@@ -179,8 +179,13 @@ function initDiagnosticsChannel() {
179
179
  return;
180
180
  // get socket from opaque
181
181
  const socket = opaque[symbols_js_1.default.kRequestSocket];
182
- socket[symbols_js_1.default.kHandledResponses]++;
183
- debug('[%s] Request#%d get %s response headers on Socket#%d (handled %d responses, sock: %o)', name, opaque[symbols_js_1.default.kRequestId], response.statusCode, socket[symbols_js_1.default.kSocketId], socket[symbols_js_1.default.kHandledResponses], formatSocket(socket));
182
+ if (socket) {
183
+ socket[symbols_js_1.default.kHandledResponses]++;
184
+ debug('[%s] Request#%d get %s response headers on Socket#%d (handled %d responses, sock: %o)', name, opaque[symbols_js_1.default.kRequestId], response.statusCode, socket[symbols_js_1.default.kSocketId], socket[symbols_js_1.default.kHandledResponses], formatSocket(socket));
185
+ }
186
+ else {
187
+ debug('[%s] Request#%d get %s response headers on Unknown Socket', name, opaque[symbols_js_1.default.kRequestId], response.statusCode);
188
+ }
184
189
  if (!opaque[symbols_js_1.default.kEnableRequestTiming])
185
190
  return;
186
191
  opaque[symbols_js_1.default.kRequestTiming].waiting = (0, utils_js_1.performanceTime)(opaque[symbols_js_1.default.kRequestStartTime]);
@@ -189,8 +194,9 @@ function initDiagnosticsChannel() {
189
194
  subscribe('undici:request:trailers', (message, name) => {
190
195
  const { request } = message;
191
196
  const opaque = getRequestOpaque(request, kHandler);
192
- if (!opaque || !opaque[symbols_js_1.default.kRequestId])
197
+ if (!opaque || !opaque[symbols_js_1.default.kRequestId]) {
193
198
  return;
199
+ }
194
200
  debug('[%s] Request#%d get response body and trailers', name, opaque[symbols_js_1.default.kRequestId]);
195
201
  if (!opaque[symbols_js_1.default.kEnableRequestTiming])
196
202
  return;
@@ -207,4 +213,3 @@ function initDiagnosticsChannel() {
207
213
  // formatSocket(socket), error);
208
214
  // });
209
215
  }
210
- exports.initDiagnosticsChannel = initDiagnosticsChannel;
@@ -17,7 +17,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.USER_AGENT = exports.HttpClient2 = exports.HttpClient = exports.getGlobalDispatcher = exports.setGlobalDispatcher = exports.Dispatcher = exports.Agent = exports.ProxyAgent = exports.MockAgent = exports.curl = exports.request = exports.getDefaultHttpClient = void 0;
20
+ exports.USER_AGENT = exports.HttpClient2 = exports.HttpClient = exports.getGlobalDispatcher = exports.setGlobalDispatcher = exports.Dispatcher = exports.Agent = exports.ProxyAgent = exports.MockAgent = void 0;
21
+ exports.getDefaultHttpClient = getDefaultHttpClient;
22
+ exports.request = request;
23
+ exports.curl = curl;
21
24
  const ylru_1 = __importDefault(require("ylru"));
22
25
  const HttpClient_js_1 = require("./HttpClient.js");
23
26
  let httpClient;
@@ -28,7 +31,6 @@ function getDefaultHttpClient() {
28
31
  }
29
32
  return httpClient;
30
33
  }
31
- exports.getDefaultHttpClient = getDefaultHttpClient;
32
34
  async function request(url, options) {
33
35
  if (options?.socketPath) {
34
36
  let domainSocketHttpclient = domainSocketHttpClients.get(options.socketPath);
@@ -42,7 +44,6 @@ async function request(url, options) {
42
44
  }
43
45
  return await getDefaultHttpClient().request(url, options);
44
46
  }
45
- exports.request = request;
46
47
  // export curl method is keep compatible with urllib.curl()
47
48
  // ```ts
48
49
  // import * as urllib from 'urllib';
@@ -51,7 +52,6 @@ exports.request = request;
51
52
  async function curl(url, options) {
52
53
  return await request(url, options);
53
54
  }
54
- exports.curl = curl;
55
55
  var undici_1 = require("undici");
56
56
  Object.defineProperty(exports, "MockAgent", { enumerable: true, get: function () { return undici_1.MockAgent; } });
57
57
  Object.defineProperty(exports, "ProxyAgent", { enumerable: true, get: function () { return undici_1.ProxyAgent; } });
@@ -1,6 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isReadable = exports.performanceTime = exports.globalId = exports.digestAuthHeader = exports.sleep = exports.parseJSON = void 0;
3
+ exports.parseJSON = parseJSON;
4
+ exports.sleep = sleep;
5
+ exports.digestAuthHeader = digestAuthHeader;
6
+ exports.globalId = globalId;
7
+ exports.performanceTime = performanceTime;
8
+ exports.isReadable = isReadable;
4
9
  const node_crypto_1 = require("node:crypto");
5
10
  const node_stream_1 = require("node:stream");
6
11
  const node_perf_hooks_1 = require("node:perf_hooks");
@@ -49,13 +54,11 @@ function parseJSON(data, fixJSONCtlChars) {
49
54
  }
50
55
  return data;
51
56
  }
52
- exports.parseJSON = parseJSON;
53
57
  function sleep(ms) {
54
58
  return new Promise(resolve => {
55
59
  setTimeout(resolve, ms);
56
60
  });
57
61
  }
58
- exports.sleep = sleep;
59
62
  function md5(s) {
60
63
  const sum = (0, node_crypto_1.createHash)('md5');
61
64
  sum.update(s, 'utf8');
@@ -123,7 +126,6 @@ function digestAuthHeader(method, uri, wwwAuthenticate, userpass) {
123
126
  }
124
127
  return authstring;
125
128
  }
126
- exports.digestAuthHeader = digestAuthHeader;
127
129
  const MAX_ID_VALUE = Math.pow(2, 31) - 10;
128
130
  const globalIds = {};
129
131
  function globalId(category) {
@@ -132,11 +134,9 @@ function globalId(category) {
132
134
  }
133
135
  return ++globalIds[category];
134
136
  }
135
- exports.globalId = globalId;
136
137
  function performanceTime(startTime, now) {
137
138
  return Math.floor(((now ?? node_perf_hooks_1.performance.now()) - startTime) * 1000) / 1000;
138
139
  }
139
- exports.performanceTime = performanceTime;
140
140
  function isReadable(stream) {
141
141
  if (typeof node_stream_1.Readable.isReadable === 'function')
142
142
  return node_stream_1.Readable.isReadable(stream);
@@ -150,4 +150,3 @@ function isReadable(stream) {
150
150
  && typeof stream._read === 'function'
151
151
  && typeof stream._readableState === 'object';
152
152
  }
153
- exports.isReadable = isReadable;
@@ -1,4 +1,3 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
1
  import { LookupFunction } from 'node:net';
3
2
  import { Agent, Dispatcher, buildConnector } from 'undici';
4
3
  export type CheckAddressFunction = (ip: string, family: number | string) => boolean;
@@ -6,6 +5,7 @@ export type HttpAgentOptions = {
6
5
  lookup?: LookupFunction;
7
6
  checkAddress?: CheckAddressFunction;
8
7
  connect?: buildConnector.BuildOptions;
8
+ allowH2?: boolean;
9
9
  };
10
10
  export declare class HttpAgent extends Agent {
11
11
  #private;
@@ -48,7 +48,7 @@ export class HttpAgent extends Agent {
48
48
  });
49
49
  };
50
50
  super({
51
- connect: { ...options.connect, lookup },
51
+ connect: { ...options.connect, lookup, allowH2: options.allowH2 },
52
52
  });
53
53
  this.#checkAddress = options.checkAddress;
54
54
  }
@@ -1,6 +1,3 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
- /// <reference types="node" resolution-mode="require"/>
3
- /// <reference types="node" resolution-mode="require"/>
4
1
  import { EventEmitter } from 'node:events';
5
2
  import { LookupFunction } from 'node:net';
6
3
  import { Dispatcher } from 'undici';
@@ -9,6 +6,8 @@ import { RequestURL, RequestOptions, RequestMeta } from './Request.js';
9
6
  import { RawResponseWithMeta, HttpClientResponse } from './Response.js';
10
7
  export type ClientOptions = {
11
8
  defaultArgs?: RequestOptions;
9
+ /** Allow to use HTTP2 first. Default is `false` */
10
+ allowH2?: boolean;
12
11
  /**
13
12
  * Custom DNS lookup function, default is `dns.lookup`.
14
13
  */
@@ -62,7 +62,7 @@ class BlobFromStream {
62
62
  return 'Blob';
63
63
  }
64
64
  }
65
- export const HEADER_USER_AGENT = createUserAgent('node-urllib', '3.25.0');
65
+ export const HEADER_USER_AGENT = createUserAgent('node-urllib', '3.26.0');
66
66
  function getFileName(stream) {
67
67
  const filePath = stream.path;
68
68
  if (filePath) {
@@ -88,11 +88,19 @@ export class HttpClient extends EventEmitter {
88
88
  lookup: clientOptions.lookup,
89
89
  checkAddress: clientOptions.checkAddress,
90
90
  connect: clientOptions.connect,
91
+ allowH2: clientOptions.allowH2,
91
92
  });
92
93
  }
93
94
  else if (clientOptions?.connect) {
94
95
  this.#dispatcher = new Agent({
95
96
  connect: clientOptions.connect,
97
+ allowH2: clientOptions.allowH2,
98
+ });
99
+ }
100
+ else if (clientOptions?.allowH2) {
101
+ // Support HTTP2
102
+ this.#dispatcher = new Agent({
103
+ allowH2: clientOptions.allowH2,
96
104
  });
97
105
  }
98
106
  initDiagnosticsChannel();
@@ -1,4 +1,3 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
1
  import type { Except } from 'type-fest';
3
2
  import type { IncomingHttpHeaders as HTTPIncomingHttpHeaders } from 'node:http';
4
3
  export interface IncomingHttpHeaders extends Except<HTTPIncomingHttpHeaders, 'set-cookie'> {
@@ -1,7 +1,3 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
- /// <reference types="node" resolution-mode="require"/>
3
- /// <reference types="node" resolution-mode="require"/>
4
- /// <reference types="node" resolution-mode="require"/>
5
1
  import type { Readable, Writable } from 'node:stream';
6
2
  import type { EventEmitter } from 'node:events';
7
3
  import type { Dispatcher } from 'undici';
@@ -1,4 +1,3 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
1
  import type { Readable } from 'node:stream';
3
2
  import type { IncomingHttpHeaders } from './IncomingHttpHeaders.js';
4
3
  export type SocketInfo = {
@@ -173,8 +173,13 @@ export function initDiagnosticsChannel() {
173
173
  return;
174
174
  // get socket from opaque
175
175
  const socket = opaque[symbols.kRequestSocket];
176
- socket[symbols.kHandledResponses]++;
177
- debug('[%s] Request#%d get %s response headers on Socket#%d (handled %d responses, sock: %o)', name, opaque[symbols.kRequestId], response.statusCode, socket[symbols.kSocketId], socket[symbols.kHandledResponses], formatSocket(socket));
176
+ if (socket) {
177
+ socket[symbols.kHandledResponses]++;
178
+ debug('[%s] Request#%d get %s response headers on Socket#%d (handled %d responses, sock: %o)', name, opaque[symbols.kRequestId], response.statusCode, socket[symbols.kSocketId], socket[symbols.kHandledResponses], formatSocket(socket));
179
+ }
180
+ else {
181
+ debug('[%s] Request#%d get %s response headers on Unknown Socket', name, opaque[symbols.kRequestId], response.statusCode);
182
+ }
178
183
  if (!opaque[symbols.kEnableRequestTiming])
179
184
  return;
180
185
  opaque[symbols.kRequestTiming].waiting = performanceTime(opaque[symbols.kRequestStartTime]);
@@ -183,8 +188,9 @@ export function initDiagnosticsChannel() {
183
188
  subscribe('undici:request:trailers', (message, name) => {
184
189
  const { request } = message;
185
190
  const opaque = getRequestOpaque(request, kHandler);
186
- if (!opaque || !opaque[symbols.kRequestId])
191
+ if (!opaque || !opaque[symbols.kRequestId]) {
187
192
  return;
193
+ }
188
194
  debug('[%s] Request#%d get response body and trailers', name, opaque[symbols.kRequestId]);
189
195
  if (!opaque[symbols.kEnableRequestTiming])
190
196
  return;
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "urllib",
3
- "version": "3.25.0",
3
+ "version": "3.26.0",
4
4
  "publishConfig": {
5
- "tag": "latest"
5
+ "access": "public"
6
6
  },
7
- "description": "Help in opening URLs (mostly HTTP) in a complex world — basic and digest authentication, redirections, cookies and more. Base undici fetch API.",
7
+ "description": "Help in opening URLs (mostly HTTP) in a complex world — basic and digest authentication, redirections, timeout and more. Base undici API.",
8
8
  "keywords": [
9
9
  "urllib",
10
10
  "http",
@@ -40,7 +40,7 @@
40
40
  "test-keepalive": "cross-env TEST_KEEPALIVE_COUNT=50 vitest run --test-timeout 180000 keep-alive-header.test.ts",
41
41
  "cov": "vitest run --coverage",
42
42
  "preci": "node scripts/pre_test.js",
43
- "ci": "npm run lint && npm run cov && node scripts/build_test.js",
43
+ "ci": "npm run lint && npm run cov && node scripts/build_test.js && attw --pack --ignore-rules no-resolution",
44
44
  "contributor": "git-contributor",
45
45
  "clean": "rm -rf dist",
46
46
  "prepublishOnly": "npm run build"
@@ -59,6 +59,7 @@
59
59
  "ylru": "^1.3.2"
60
60
  },
61
61
  "devDependencies": {
62
+ "@arethetypeswrong/cli": "^0.15.3",
62
63
  "@tsconfig/node18": "^18.2.1",
63
64
  "@tsconfig/strictest": "^2.0.2",
64
65
  "@types/busboy": "^1.5.0",
@@ -98,10 +99,12 @@
98
99
  "exports": {
99
100
  ".": {
100
101
  "import": {
102
+ "source": "./src/index.ts",
101
103
  "types": "./dist/esm/index.d.ts",
102
104
  "default": "./dist/esm/index.js"
103
105
  },
104
106
  "require": {
107
+ "source": "./src/index.ts",
105
108
  "types": "./dist/commonjs/index.d.ts",
106
109
  "default": "./dist/commonjs/index.js"
107
110
  }
package/src/HttpAgent.ts CHANGED
@@ -12,6 +12,7 @@ export type HttpAgentOptions = {
12
12
  lookup?: LookupFunction;
13
13
  checkAddress?: CheckAddressFunction;
14
14
  connect?: buildConnector.BuildOptions,
15
+ allowH2?: boolean;
15
16
  };
16
17
 
17
18
  class IllegalAddressError extends Error {
@@ -62,7 +63,7 @@ export class HttpAgent extends Agent {
62
63
  });
63
64
  };
64
65
  super({
65
- connect: { ...options.connect, lookup },
66
+ connect: { ...options.connect, lookup, allowH2: options.allowH2 },
66
67
  });
67
68
  this.#checkAddress = options.checkAddress;
68
69
  }
package/src/HttpClient.ts CHANGED
@@ -70,6 +70,8 @@ const isNode14Or16 = /v1[46]\./.test(process.version);
70
70
 
71
71
  export type ClientOptions = {
72
72
  defaultArgs?: RequestOptions;
73
+ /** Allow to use HTTP2 first. Default is `false` */
74
+ allowH2?: boolean;
73
75
  /**
74
76
  * Custom DNS lookup function, default is `dns.lookup`.
75
77
  */
@@ -187,10 +189,17 @@ export class HttpClient extends EventEmitter {
187
189
  lookup: clientOptions.lookup,
188
190
  checkAddress: clientOptions.checkAddress,
189
191
  connect: clientOptions.connect,
192
+ allowH2: clientOptions.allowH2,
190
193
  });
191
194
  } else if (clientOptions?.connect) {
192
195
  this.#dispatcher = new Agent({
193
196
  connect: clientOptions.connect,
197
+ allowH2: clientOptions.allowH2,
198
+ });
199
+ } else if (clientOptions?.allowH2) {
200
+ // Support HTTP2
201
+ this.#dispatcher = new Agent({
202
+ allowH2: clientOptions.allowH2,
194
203
  });
195
204
  }
196
205
  initDiagnosticsChannel();
@@ -184,10 +184,15 @@ export function initDiagnosticsChannel() {
184
184
 
185
185
  // get socket from opaque
186
186
  const socket = opaque[symbols.kRequestSocket];
187
- socket[symbols.kHandledResponses]++;
188
- debug('[%s] Request#%d get %s response headers on Socket#%d (handled %d responses, sock: %o)',
189
- name, opaque[symbols.kRequestId], response.statusCode, socket[symbols.kSocketId], socket[symbols.kHandledResponses],
190
- formatSocket(socket));
187
+ if (socket) {
188
+ socket[symbols.kHandledResponses]++;
189
+ debug('[%s] Request#%d get %s response headers on Socket#%d (handled %d responses, sock: %o)',
190
+ name, opaque[symbols.kRequestId], response.statusCode, socket[symbols.kSocketId], socket[symbols.kHandledResponses],
191
+ formatSocket(socket));
192
+ } else {
193
+ debug('[%s] Request#%d get %s response headers on Unknown Socket',
194
+ name, opaque[symbols.kRequestId], response.statusCode);
195
+ }
191
196
 
192
197
  if (!opaque[symbols.kEnableRequestTiming]) return;
193
198
  opaque[symbols.kRequestTiming].waiting = performanceTime(opaque[symbols.kRequestStartTime]);
@@ -197,7 +202,9 @@ export function initDiagnosticsChannel() {
197
202
  subscribe('undici:request:trailers', (message, name) => {
198
203
  const { request } = message as DiagnosticsChannel.RequestTrailersMessage;
199
204
  const opaque = getRequestOpaque(request, kHandler);
200
- if (!opaque || !opaque[symbols.kRequestId]) return;
205
+ if (!opaque || !opaque[symbols.kRequestId]) {
206
+ return;
207
+ }
201
208
 
202
209
  debug('[%s] Request#%d get response body and trailers', name, opaque[symbols.kRequestId]);
203
210