urllib 3.20.0 → 3.21.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.
@@ -68,7 +68,7 @@ class HttpAgent extends undici_1.Agent {
68
68
  }
69
69
  const family = (0, node_net_1.isIP)(hostname);
70
70
  if (family === 4 || family === 6) {
71
- // if request hostname is ip, custom lookup won't excute
71
+ // if request hostname is ip, custom lookup won't execute
72
72
  if (!this.#checkAddress(hostname, family)) {
73
73
  throw new IllegalAddressError(hostname, hostname, family);
74
74
  }
@@ -29,6 +29,7 @@ const HttpAgent_js_1 = require("./HttpAgent.js");
29
29
  const utils_js_1 = require("./utils.js");
30
30
  const symbols_js_1 = __importDefault(require("./symbols.js"));
31
31
  const diagnosticsChannel_js_1 = require("./diagnosticsChannel.js");
32
+ const HttpClientError_js_1 = require("./HttpClientError.js");
32
33
  const PROTO_RE = /^https?:\/\//i;
33
34
  const FormData = undici_1.FormData ?? formdata_node_1.FormData;
34
35
  // impl promise pipeline on Node.js 14
@@ -65,15 +66,7 @@ class BlobFromStream {
65
66
  return 'Blob';
66
67
  }
67
68
  }
68
- class HttpClientRequestTimeoutError extends Error {
69
- constructor(timeout, options) {
70
- const message = `Request timeout for ${timeout} ms`;
71
- super(message, options);
72
- this.name = this.constructor.name;
73
- Error.captureStackTrace(this, this.constructor);
74
- }
75
- }
76
- exports.HEADER_USER_AGENT = (0, default_user_agent_1.default)('node-urllib', '3.20.0');
69
+ exports.HEADER_USER_AGENT = (0, default_user_agent_1.default)('node-urllib', '3.21.0');
77
70
  function getFileName(stream) {
78
71
  const filePath = stream.path;
79
72
  if (filePath) {
@@ -580,14 +573,17 @@ class HttpClient extends node_events_1.EventEmitter {
580
573
  }
581
574
  return clientResponse;
582
575
  }
583
- catch (e) {
584
- debug('Request#%d throw error: %s', requestId, e);
585
- let err = e;
576
+ catch (rawError) {
577
+ debug('Request#%d throw error: %s', requestId, rawError);
578
+ let err = rawError;
586
579
  if (err.name === 'HeadersTimeoutError') {
587
- err = new HttpClientRequestTimeoutError(headersTimeout, { cause: e });
580
+ err = new HttpClientError_js_1.HttpClientRequestTimeoutError(headersTimeout, { cause: err });
588
581
  }
589
582
  else if (err.name === 'BodyTimeoutError') {
590
- err = new HttpClientRequestTimeoutError(bodyTimeout, { cause: e });
583
+ err = new HttpClientError_js_1.HttpClientRequestTimeoutError(bodyTimeout, { cause: err });
584
+ }
585
+ else if (err.code === 'UND_ERR_CONNECT_TIMEOUT') {
586
+ err = new HttpClientError_js_1.HttpClientConnectTimeoutError(err.message, err.code, { cause: err });
591
587
  }
592
588
  else if (err.code === 'UND_ERR_SOCKET' || err.code === 'ECONNRESET') {
593
589
  // auto retry on socket error, https://github.com/node-modules/urllib/issues/454
@@ -610,7 +606,7 @@ class HttpClient extends node_events_1.EventEmitter {
610
606
  res.requestUrls.push(requestUrl.href);
611
607
  }
612
608
  res.rt = (0, utils_js_1.performanceTime)(requestStartTime);
613
- this.#updateSocketInfo(socketInfo, internalOpaque);
609
+ this.#updateSocketInfo(socketInfo, internalOpaque, rawError);
614
610
  channels.response.publish({
615
611
  request: reqMeta,
616
612
  response: res,
@@ -631,21 +627,38 @@ class HttpClient extends node_events_1.EventEmitter {
631
627
  throw err;
632
628
  }
633
629
  }
634
- #updateSocketInfo(socketInfo, internalOpaque) {
635
- const socket = internalOpaque[symbols_js_1.default.kRequestSocket];
630
+ #updateSocketInfo(socketInfo, internalOpaque, err) {
631
+ const socket = internalOpaque[symbols_js_1.default.kRequestSocket] ?? err?.[symbols_js_1.default.kErrorSocket];
636
632
  if (socket) {
637
633
  socketInfo.id = socket[symbols_js_1.default.kSocketId];
638
634
  socketInfo.handledRequests = socket[symbols_js_1.default.kHandledRequests];
639
635
  socketInfo.handledResponses = socket[symbols_js_1.default.kHandledResponses];
640
- socketInfo.localAddress = socket[symbols_js_1.default.kSocketLocalAddress];
641
- socketInfo.localPort = socket[symbols_js_1.default.kSocketLocalPort];
642
- socketInfo.remoteAddress = socket.remoteAddress;
643
- socketInfo.remotePort = socket.remotePort;
644
- socketInfo.remoteFamily = socket.remoteFamily;
636
+ if (socket[symbols_js_1.default.kSocketLocalAddress]) {
637
+ socketInfo.localAddress = socket[symbols_js_1.default.kSocketLocalAddress];
638
+ socketInfo.localPort = socket[symbols_js_1.default.kSocketLocalPort];
639
+ }
640
+ if (socket.remoteAddress) {
641
+ socketInfo.remoteAddress = socket.remoteAddress;
642
+ socketInfo.remotePort = socket.remotePort;
643
+ socketInfo.remoteFamily = socket.remoteFamily;
644
+ }
645
645
  socketInfo.bytesRead = socket.bytesRead;
646
646
  socketInfo.bytesWritten = socket.bytesWritten;
647
- socketInfo.connectedTime = socket[symbols_js_1.default.kSocketConnectedTime];
648
- socketInfo.lastRequestEndTime = socket[symbols_js_1.default.kSocketRequestEndTime];
647
+ if (socket[symbols_js_1.default.kSocketConnectErrorTime]) {
648
+ socketInfo.connectErrorTime = socket[symbols_js_1.default.kSocketConnectErrorTime];
649
+ if (Array.isArray(socket.autoSelectFamilyAttemptedAddresses)) {
650
+ socketInfo.attemptedRemoteAddresses = socket.autoSelectFamilyAttemptedAddresses;
651
+ }
652
+ socketInfo.connectProtocol = socket[symbols_js_1.default.kSocketConnectProtocol];
653
+ socketInfo.connectHost = socket[symbols_js_1.default.kSocketConnectHost];
654
+ socketInfo.connectPort = socket[symbols_js_1.default.kSocketConnectPort];
655
+ }
656
+ if (socket[symbols_js_1.default.kSocketConnectedTime]) {
657
+ socketInfo.connectedTime = socket[symbols_js_1.default.kSocketConnectedTime];
658
+ }
659
+ if (socket[symbols_js_1.default.kSocketRequestEndTime]) {
660
+ socketInfo.lastRequestEndTime = socket[symbols_js_1.default.kSocketRequestEndTime];
661
+ }
649
662
  socket[symbols_js_1.default.kSocketRequestEndTime] = new Date();
650
663
  }
651
664
  }
@@ -0,0 +1,19 @@
1
+ import type { RawResponseWithMeta, SocketInfo } from './Response.js';
2
+ import type { IncomingHttpHeaders } from './IncomingHttpHeaders.js';
3
+ interface ErrorOptions {
4
+ cause?: Error;
5
+ }
6
+ export declare class HttpClientRequestError extends Error {
7
+ status?: number;
8
+ headers?: IncomingHttpHeaders;
9
+ socket?: SocketInfo;
10
+ res?: RawResponseWithMeta;
11
+ }
12
+ export declare class HttpClientRequestTimeoutError extends HttpClientRequestError {
13
+ constructor(timeout: number, options: ErrorOptions);
14
+ }
15
+ export declare class HttpClientConnectTimeoutError extends HttpClientRequestError {
16
+ code: string;
17
+ constructor(message: string, code: string, options: ErrorOptions);
18
+ }
19
+ export {};
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpClientConnectTimeoutError = exports.HttpClientRequestTimeoutError = exports.HttpClientRequestError = void 0;
4
+ class HttpClientRequestError extends Error {
5
+ status;
6
+ headers;
7
+ socket;
8
+ res;
9
+ }
10
+ exports.HttpClientRequestError = HttpClientRequestError;
11
+ class HttpClientRequestTimeoutError extends HttpClientRequestError {
12
+ constructor(timeout, options) {
13
+ const message = `Request timeout for ${timeout} ms`;
14
+ super(message, options);
15
+ this.name = this.constructor.name;
16
+ Error.captureStackTrace(this, this.constructor);
17
+ }
18
+ }
19
+ exports.HttpClientRequestTimeoutError = HttpClientRequestTimeoutError;
20
+ class HttpClientConnectTimeoutError extends HttpClientRequestError {
21
+ code;
22
+ constructor(message, code, options) {
23
+ super(message, options);
24
+ this.name = this.constructor.name;
25
+ this.code = code;
26
+ Error.captureStackTrace(this, this.constructor);
27
+ }
28
+ }
29
+ exports.HttpClientConnectTimeoutError = HttpClientConnectTimeoutError;
@@ -13,7 +13,12 @@ export type SocketInfo = {
13
13
  handledRequests: number;
14
14
  handledResponses: number;
15
15
  connectedTime?: Date;
16
+ connectErrorTime?: Date;
16
17
  lastRequestEndTime?: Date;
18
+ attemptedRemoteAddresses?: string[];
19
+ connectProtocol?: string;
20
+ connectHost?: string;
21
+ connectPort?: string;
17
22
  };
18
23
  /**
19
24
  * https://eggjs.org/en/core/httpclient.html#timing-boolean
@@ -7,6 +7,7 @@ exports.initDiagnosticsChannel = void 0;
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");
10
+ const node_net_1 = require("node:net");
10
11
  const symbols_js_1 = __importDefault(require("./symbols.js"));
11
12
  const utils_js_1 = require("./utils.js");
12
13
  const debug = (0, node_util_1.debuglog)('urllib:DiagnosticsChannel');
@@ -38,8 +39,23 @@ function formatSocket(socket) {
38
39
  localPort: socket[symbols_js_1.default.kSocketLocalPort],
39
40
  remoteAddress: socket.remoteAddress,
40
41
  remotePort: socket.remotePort,
42
+ attemptedAddresses: socket.autoSelectFamilyAttemptedAddresses,
43
+ connecting: socket.connecting,
41
44
  };
42
45
  }
46
+ // make sure error contains socket info
47
+ const kDestroy = Symbol('kDestroy');
48
+ node_net_1.Socket.prototype[kDestroy] = node_net_1.Socket.prototype.destroy;
49
+ node_net_1.Socket.prototype.destroy = function (err) {
50
+ if (err) {
51
+ Object.defineProperty(err, symbols_js_1.default.kErrorSocket, {
52
+ // don't show on console log
53
+ enumerable: false,
54
+ value: this,
55
+ });
56
+ }
57
+ return this[kDestroy](err);
58
+ };
43
59
  function initDiagnosticsChannel() {
44
60
  // makre sure init global DiagnosticsChannel once
45
61
  if (initedDiagnosticsChannel)
@@ -69,10 +85,34 @@ function initDiagnosticsChannel() {
69
85
  opaque[symbols_js_1.default.kRequestTiming].queuing = (0, utils_js_1.performanceTime)(opaque[symbols_js_1.default.kRequestStartTime]);
70
86
  });
71
87
  // diagnosticsChannel.channel('undici:client:beforeConnect')
72
- // diagnosticsChannel.channel('undici:client:connectError')
88
+ subscribe('undici:client:connectError', (message, name) => {
89
+ const { error, connectParams } = message;
90
+ let { socket } = message;
91
+ if (!socket && error[symbols_js_1.default.kErrorSocket]) {
92
+ socket = error[symbols_js_1.default.kErrorSocket];
93
+ }
94
+ if (socket) {
95
+ socket[symbols_js_1.default.kSocketId] = (0, utils_js_1.globalId)('UndiciSocket');
96
+ socket[symbols_js_1.default.kSocketConnectErrorTime] = new Date();
97
+ socket[symbols_js_1.default.kHandledRequests] = 0;
98
+ socket[symbols_js_1.default.kHandledResponses] = 0;
99
+ // copy local address to symbol, avoid them be reset after request error throw
100
+ if (socket.localAddress) {
101
+ socket[symbols_js_1.default.kSocketLocalAddress] = socket.localAddress;
102
+ socket[symbols_js_1.default.kSocketLocalPort] = socket.localPort;
103
+ }
104
+ socket[symbols_js_1.default.kSocketConnectProtocol] = connectParams.protocol;
105
+ socket[symbols_js_1.default.kSocketConnectHost] = connectParams.host;
106
+ socket[symbols_js_1.default.kSocketConnectPort] = connectParams.port;
107
+ debug('[%s] Socket#%d connectError, connectParams: %o, error: %s, (sock: %o)', name, socket[symbols_js_1.default.kSocketId], connectParams, error.message, formatSocket(socket));
108
+ }
109
+ else {
110
+ debug('[%s] connectError, connectParams: %o, error: %o', name, connectParams, error);
111
+ }
112
+ });
73
113
  // This message is published after a connection is established.
74
114
  subscribe('undici:client:connected', (message, name) => {
75
- const { socket } = message;
115
+ const { socket, connectParams } = message;
76
116
  socket[symbols_js_1.default.kSocketId] = (0, utils_js_1.globalId)('UndiciSocket');
77
117
  socket[symbols_js_1.default.kSocketStartTime] = node_perf_hooks_1.performance.now();
78
118
  socket[symbols_js_1.default.kSocketConnectedTime] = new Date();
@@ -81,6 +121,9 @@ function initDiagnosticsChannel() {
81
121
  // copy local address to symbol, avoid them be reset after request error throw
82
122
  socket[symbols_js_1.default.kSocketLocalAddress] = socket.localAddress;
83
123
  socket[symbols_js_1.default.kSocketLocalPort] = socket.localPort;
124
+ socket[symbols_js_1.default.kSocketConnectProtocol] = connectParams.protocol;
125
+ socket[symbols_js_1.default.kSocketConnectHost] = connectParams.host;
126
+ socket[symbols_js_1.default.kSocketConnectPort] = connectParams.port;
84
127
  debug('[%s] Socket#%d connected (sock: %o)', name, socket[symbols_js_1.default.kSocketId], formatSocket(socket));
85
128
  });
86
129
  // This message is published right before the first byte of the request is written to the socket.
@@ -6,6 +6,7 @@ export { HttpClient, HttpClient as HttpClient2, HEADER_USER_AGENT as USER_AGENT,
6
6
  export { RequestOptions, RequestOptions as RequestOptions2, RequestURL, HttpMethod, FixJSONCtlCharsHandler, FixJSONCtlChars, } from './Request.js';
7
7
  export { SocketInfo, Timing, RawResponseWithMeta, HttpClientResponse, } from './Response.js';
8
8
  export { IncomingHttpHeaders, } from './IncomingHttpHeaders.js';
9
+ export * from './HttpClientError.js';
9
10
  declare const _default: {
10
11
  request: typeof request;
11
12
  curl: typeof curl;
@@ -1,4 +1,18 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
2
16
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
18
  };
@@ -46,6 +60,7 @@ var HttpClient_js_2 = require("./HttpClient.js");
46
60
  Object.defineProperty(exports, "HttpClient", { enumerable: true, get: function () { return HttpClient_js_2.HttpClient; } });
47
61
  Object.defineProperty(exports, "HttpClient2", { enumerable: true, get: function () { return HttpClient_js_2.HttpClient; } });
48
62
  Object.defineProperty(exports, "USER_AGENT", { enumerable: true, get: function () { return HttpClient_js_2.HEADER_USER_AGENT; } });
63
+ __exportStar(require("./HttpClientError.js"), exports);
49
64
  exports.default = {
50
65
  request,
51
66
  curl,
@@ -2,9 +2,13 @@ declare const _default: {
2
2
  kSocketId: symbol;
3
3
  kSocketStartTime: symbol;
4
4
  kSocketConnectedTime: symbol;
5
+ kSocketConnectErrorTime: symbol;
5
6
  kSocketRequestEndTime: symbol;
6
7
  kSocketLocalAddress: symbol;
7
8
  kSocketLocalPort: symbol;
9
+ kSocketConnectHost: symbol;
10
+ kSocketConnectPort: symbol;
11
+ kSocketConnectProtocol: symbol;
8
12
  kHandledRequests: symbol;
9
13
  kHandledResponses: symbol;
10
14
  kRequestSocket: symbol;
@@ -13,5 +17,6 @@ declare const _default: {
13
17
  kEnableRequestTiming: symbol;
14
18
  kRequestTiming: symbol;
15
19
  kRequestOriginalOpaque: symbol;
20
+ kErrorSocket: symbol;
16
21
  };
17
22
  export default _default;
@@ -4,9 +4,13 @@ exports.default = {
4
4
  kSocketId: Symbol('socket id'),
5
5
  kSocketStartTime: Symbol('socket start time'),
6
6
  kSocketConnectedTime: Symbol('socket connected time'),
7
+ kSocketConnectErrorTime: Symbol('socket connectError time'),
7
8
  kSocketRequestEndTime: Symbol('socket request end time'),
8
9
  kSocketLocalAddress: Symbol('socket local address'),
9
10
  kSocketLocalPort: Symbol('socket local port'),
11
+ kSocketConnectHost: Symbol('socket connect params: host'),
12
+ kSocketConnectPort: Symbol('socket connect params: port'),
13
+ kSocketConnectProtocol: Symbol('socket connect params: protocol'),
10
14
  kHandledRequests: Symbol('handled requests per socket'),
11
15
  kHandledResponses: Symbol('handled responses per socket'),
12
16
  kRequestSocket: Symbol('request on the socket'),
@@ -15,4 +19,5 @@ exports.default = {
15
19
  kEnableRequestTiming: Symbol('enable request timing or not'),
16
20
  kRequestTiming: Symbol('request timing'),
17
21
  kRequestOriginalOpaque: Symbol('request original opaque'),
22
+ kErrorSocket: Symbol('socket of error'),
18
23
  };
@@ -62,7 +62,7 @@ export class HttpAgent extends Agent {
62
62
  }
63
63
  const family = isIP(hostname);
64
64
  if (family === 4 || family === 6) {
65
- // if request hostname is ip, custom lookup won't excute
65
+ // if request hostname is ip, custom lookup won't execute
66
66
  if (!this.#checkAddress(hostname, family)) {
67
67
  throw new IllegalAddressError(hostname, hostname, family);
68
68
  }
@@ -23,6 +23,7 @@ import { HttpAgent } from './HttpAgent.js';
23
23
  import { parseJSON, sleep, digestAuthHeader, globalId, performanceTime, isReadable } from './utils.js';
24
24
  import symbols from './symbols.js';
25
25
  import { initDiagnosticsChannel } from './diagnosticsChannel.js';
26
+ import { HttpClientConnectTimeoutError, HttpClientRequestTimeoutError } from './HttpClientError.js';
26
27
  const PROTO_RE = /^https?:\/\//i;
27
28
  const FormData = FormDataNative ?? FormDataNode;
28
29
  // impl promise pipeline on Node.js 14
@@ -59,15 +60,7 @@ class BlobFromStream {
59
60
  return 'Blob';
60
61
  }
61
62
  }
62
- class HttpClientRequestTimeoutError extends Error {
63
- constructor(timeout, options) {
64
- const message = `Request timeout for ${timeout} ms`;
65
- super(message, options);
66
- this.name = this.constructor.name;
67
- Error.captureStackTrace(this, this.constructor);
68
- }
69
- }
70
- export const HEADER_USER_AGENT = createUserAgent('node-urllib', '3.20.0');
63
+ export const HEADER_USER_AGENT = createUserAgent('node-urllib', '3.21.0');
71
64
  function getFileName(stream) {
72
65
  const filePath = stream.path;
73
66
  if (filePath) {
@@ -574,14 +567,17 @@ export class HttpClient extends EventEmitter {
574
567
  }
575
568
  return clientResponse;
576
569
  }
577
- catch (e) {
578
- debug('Request#%d throw error: %s', requestId, e);
579
- let err = e;
570
+ catch (rawError) {
571
+ debug('Request#%d throw error: %s', requestId, rawError);
572
+ let err = rawError;
580
573
  if (err.name === 'HeadersTimeoutError') {
581
- err = new HttpClientRequestTimeoutError(headersTimeout, { cause: e });
574
+ err = new HttpClientRequestTimeoutError(headersTimeout, { cause: err });
582
575
  }
583
576
  else if (err.name === 'BodyTimeoutError') {
584
- err = new HttpClientRequestTimeoutError(bodyTimeout, { cause: e });
577
+ err = new HttpClientRequestTimeoutError(bodyTimeout, { cause: err });
578
+ }
579
+ else if (err.code === 'UND_ERR_CONNECT_TIMEOUT') {
580
+ err = new HttpClientConnectTimeoutError(err.message, err.code, { cause: err });
585
581
  }
586
582
  else if (err.code === 'UND_ERR_SOCKET' || err.code === 'ECONNRESET') {
587
583
  // auto retry on socket error, https://github.com/node-modules/urllib/issues/454
@@ -604,7 +600,7 @@ export class HttpClient extends EventEmitter {
604
600
  res.requestUrls.push(requestUrl.href);
605
601
  }
606
602
  res.rt = performanceTime(requestStartTime);
607
- this.#updateSocketInfo(socketInfo, internalOpaque);
603
+ this.#updateSocketInfo(socketInfo, internalOpaque, rawError);
608
604
  channels.response.publish({
609
605
  request: reqMeta,
610
606
  response: res,
@@ -625,21 +621,38 @@ export class HttpClient extends EventEmitter {
625
621
  throw err;
626
622
  }
627
623
  }
628
- #updateSocketInfo(socketInfo, internalOpaque) {
629
- const socket = internalOpaque[symbols.kRequestSocket];
624
+ #updateSocketInfo(socketInfo, internalOpaque, err) {
625
+ const socket = internalOpaque[symbols.kRequestSocket] ?? err?.[symbols.kErrorSocket];
630
626
  if (socket) {
631
627
  socketInfo.id = socket[symbols.kSocketId];
632
628
  socketInfo.handledRequests = socket[symbols.kHandledRequests];
633
629
  socketInfo.handledResponses = socket[symbols.kHandledResponses];
634
- socketInfo.localAddress = socket[symbols.kSocketLocalAddress];
635
- socketInfo.localPort = socket[symbols.kSocketLocalPort];
636
- socketInfo.remoteAddress = socket.remoteAddress;
637
- socketInfo.remotePort = socket.remotePort;
638
- socketInfo.remoteFamily = socket.remoteFamily;
630
+ if (socket[symbols.kSocketLocalAddress]) {
631
+ socketInfo.localAddress = socket[symbols.kSocketLocalAddress];
632
+ socketInfo.localPort = socket[symbols.kSocketLocalPort];
633
+ }
634
+ if (socket.remoteAddress) {
635
+ socketInfo.remoteAddress = socket.remoteAddress;
636
+ socketInfo.remotePort = socket.remotePort;
637
+ socketInfo.remoteFamily = socket.remoteFamily;
638
+ }
639
639
  socketInfo.bytesRead = socket.bytesRead;
640
640
  socketInfo.bytesWritten = socket.bytesWritten;
641
- socketInfo.connectedTime = socket[symbols.kSocketConnectedTime];
642
- socketInfo.lastRequestEndTime = socket[symbols.kSocketRequestEndTime];
641
+ if (socket[symbols.kSocketConnectErrorTime]) {
642
+ socketInfo.connectErrorTime = socket[symbols.kSocketConnectErrorTime];
643
+ if (Array.isArray(socket.autoSelectFamilyAttemptedAddresses)) {
644
+ socketInfo.attemptedRemoteAddresses = socket.autoSelectFamilyAttemptedAddresses;
645
+ }
646
+ socketInfo.connectProtocol = socket[symbols.kSocketConnectProtocol];
647
+ socketInfo.connectHost = socket[symbols.kSocketConnectHost];
648
+ socketInfo.connectPort = socket[symbols.kSocketConnectPort];
649
+ }
650
+ if (socket[symbols.kSocketConnectedTime]) {
651
+ socketInfo.connectedTime = socket[symbols.kSocketConnectedTime];
652
+ }
653
+ if (socket[symbols.kSocketRequestEndTime]) {
654
+ socketInfo.lastRequestEndTime = socket[symbols.kSocketRequestEndTime];
655
+ }
643
656
  socket[symbols.kSocketRequestEndTime] = new Date();
644
657
  }
645
658
  }
@@ -0,0 +1,19 @@
1
+ import type { RawResponseWithMeta, SocketInfo } from './Response.js';
2
+ import type { IncomingHttpHeaders } from './IncomingHttpHeaders.js';
3
+ interface ErrorOptions {
4
+ cause?: Error;
5
+ }
6
+ export declare class HttpClientRequestError extends Error {
7
+ status?: number;
8
+ headers?: IncomingHttpHeaders;
9
+ socket?: SocketInfo;
10
+ res?: RawResponseWithMeta;
11
+ }
12
+ export declare class HttpClientRequestTimeoutError extends HttpClientRequestError {
13
+ constructor(timeout: number, options: ErrorOptions);
14
+ }
15
+ export declare class HttpClientConnectTimeoutError extends HttpClientRequestError {
16
+ code: string;
17
+ constructor(message: string, code: string, options: ErrorOptions);
18
+ }
19
+ export {};
@@ -0,0 +1,23 @@
1
+ export class HttpClientRequestError extends Error {
2
+ status;
3
+ headers;
4
+ socket;
5
+ res;
6
+ }
7
+ export class HttpClientRequestTimeoutError extends HttpClientRequestError {
8
+ constructor(timeout, options) {
9
+ const message = `Request timeout for ${timeout} ms`;
10
+ super(message, options);
11
+ this.name = this.constructor.name;
12
+ Error.captureStackTrace(this, this.constructor);
13
+ }
14
+ }
15
+ export class HttpClientConnectTimeoutError extends HttpClientRequestError {
16
+ code;
17
+ constructor(message, code, options) {
18
+ super(message, options);
19
+ this.name = this.constructor.name;
20
+ this.code = code;
21
+ Error.captureStackTrace(this, this.constructor);
22
+ }
23
+ }
@@ -13,7 +13,12 @@ export type SocketInfo = {
13
13
  handledRequests: number;
14
14
  handledResponses: number;
15
15
  connectedTime?: Date;
16
+ connectErrorTime?: Date;
16
17
  lastRequestEndTime?: Date;
18
+ attemptedRemoteAddresses?: string[];
19
+ connectProtocol?: string;
20
+ connectHost?: string;
21
+ connectPort?: string;
17
22
  };
18
23
  /**
19
24
  * https://eggjs.org/en/core/httpclient.html#timing-boolean
@@ -1,6 +1,7 @@
1
1
  import diagnosticsChannel from 'node:diagnostics_channel';
2
2
  import { performance } from 'node:perf_hooks';
3
3
  import { debuglog } from 'node:util';
4
+ import { Socket } from 'node:net';
4
5
  import symbols from './symbols.js';
5
6
  import { globalId, performanceTime } from './utils.js';
6
7
  const debug = debuglog('urllib:DiagnosticsChannel');
@@ -32,8 +33,23 @@ function formatSocket(socket) {
32
33
  localPort: socket[symbols.kSocketLocalPort],
33
34
  remoteAddress: socket.remoteAddress,
34
35
  remotePort: socket.remotePort,
36
+ attemptedAddresses: socket.autoSelectFamilyAttemptedAddresses,
37
+ connecting: socket.connecting,
35
38
  };
36
39
  }
40
+ // make sure error contains socket info
41
+ const kDestroy = Symbol('kDestroy');
42
+ Socket.prototype[kDestroy] = Socket.prototype.destroy;
43
+ Socket.prototype.destroy = function (err) {
44
+ if (err) {
45
+ Object.defineProperty(err, symbols.kErrorSocket, {
46
+ // don't show on console log
47
+ enumerable: false,
48
+ value: this,
49
+ });
50
+ }
51
+ return this[kDestroy](err);
52
+ };
37
53
  export function initDiagnosticsChannel() {
38
54
  // makre sure init global DiagnosticsChannel once
39
55
  if (initedDiagnosticsChannel)
@@ -63,10 +79,34 @@ export function initDiagnosticsChannel() {
63
79
  opaque[symbols.kRequestTiming].queuing = performanceTime(opaque[symbols.kRequestStartTime]);
64
80
  });
65
81
  // diagnosticsChannel.channel('undici:client:beforeConnect')
66
- // diagnosticsChannel.channel('undici:client:connectError')
82
+ subscribe('undici:client:connectError', (message, name) => {
83
+ const { error, connectParams } = message;
84
+ let { socket } = message;
85
+ if (!socket && error[symbols.kErrorSocket]) {
86
+ socket = error[symbols.kErrorSocket];
87
+ }
88
+ if (socket) {
89
+ socket[symbols.kSocketId] = globalId('UndiciSocket');
90
+ socket[symbols.kSocketConnectErrorTime] = new Date();
91
+ socket[symbols.kHandledRequests] = 0;
92
+ socket[symbols.kHandledResponses] = 0;
93
+ // copy local address to symbol, avoid them be reset after request error throw
94
+ if (socket.localAddress) {
95
+ socket[symbols.kSocketLocalAddress] = socket.localAddress;
96
+ socket[symbols.kSocketLocalPort] = socket.localPort;
97
+ }
98
+ socket[symbols.kSocketConnectProtocol] = connectParams.protocol;
99
+ socket[symbols.kSocketConnectHost] = connectParams.host;
100
+ socket[symbols.kSocketConnectPort] = connectParams.port;
101
+ debug('[%s] Socket#%d connectError, connectParams: %o, error: %s, (sock: %o)', name, socket[symbols.kSocketId], connectParams, error.message, formatSocket(socket));
102
+ }
103
+ else {
104
+ debug('[%s] connectError, connectParams: %o, error: %o', name, connectParams, error);
105
+ }
106
+ });
67
107
  // This message is published after a connection is established.
68
108
  subscribe('undici:client:connected', (message, name) => {
69
- const { socket } = message;
109
+ const { socket, connectParams } = message;
70
110
  socket[symbols.kSocketId] = globalId('UndiciSocket');
71
111
  socket[symbols.kSocketStartTime] = performance.now();
72
112
  socket[symbols.kSocketConnectedTime] = new Date();
@@ -75,6 +115,9 @@ export function initDiagnosticsChannel() {
75
115
  // copy local address to symbol, avoid them be reset after request error throw
76
116
  socket[symbols.kSocketLocalAddress] = socket.localAddress;
77
117
  socket[symbols.kSocketLocalPort] = socket.localPort;
118
+ socket[symbols.kSocketConnectProtocol] = connectParams.protocol;
119
+ socket[symbols.kSocketConnectHost] = connectParams.host;
120
+ socket[symbols.kSocketConnectPort] = connectParams.port;
78
121
  debug('[%s] Socket#%d connected (sock: %o)', name, socket[symbols.kSocketId], formatSocket(socket));
79
122
  });
80
123
  // This message is published right before the first byte of the request is written to the socket.
@@ -6,6 +6,7 @@ export { HttpClient, HttpClient as HttpClient2, HEADER_USER_AGENT as USER_AGENT,
6
6
  export { RequestOptions, RequestOptions as RequestOptions2, RequestURL, HttpMethod, FixJSONCtlCharsHandler, FixJSONCtlChars, } from './Request.js';
7
7
  export { SocketInfo, Timing, RawResponseWithMeta, HttpClientResponse, } from './Response.js';
8
8
  export { IncomingHttpHeaders, } from './IncomingHttpHeaders.js';
9
+ export * from './HttpClientError.js';
9
10
  declare const _default: {
10
11
  request: typeof request;
11
12
  curl: typeof curl;
package/dist/esm/index.js CHANGED
@@ -29,6 +29,7 @@ export async function curl(url, options) {
29
29
  export { MockAgent, ProxyAgent, Agent, Dispatcher, setGlobalDispatcher, getGlobalDispatcher, } from 'undici';
30
30
  // HttpClient2 is keep compatible with urlib@2 HttpClient2
31
31
  export { HttpClient, HttpClient as HttpClient2, HEADER_USER_AGENT as USER_AGENT, } from './HttpClient.js';
32
+ export * from './HttpClientError.js';
32
33
  export default {
33
34
  request,
34
35
  curl,
@@ -2,9 +2,13 @@ declare const _default: {
2
2
  kSocketId: symbol;
3
3
  kSocketStartTime: symbol;
4
4
  kSocketConnectedTime: symbol;
5
+ kSocketConnectErrorTime: symbol;
5
6
  kSocketRequestEndTime: symbol;
6
7
  kSocketLocalAddress: symbol;
7
8
  kSocketLocalPort: symbol;
9
+ kSocketConnectHost: symbol;
10
+ kSocketConnectPort: symbol;
11
+ kSocketConnectProtocol: symbol;
8
12
  kHandledRequests: symbol;
9
13
  kHandledResponses: symbol;
10
14
  kRequestSocket: symbol;
@@ -13,5 +17,6 @@ declare const _default: {
13
17
  kEnableRequestTiming: symbol;
14
18
  kRequestTiming: symbol;
15
19
  kRequestOriginalOpaque: symbol;
20
+ kErrorSocket: symbol;
16
21
  };
17
22
  export default _default;
@@ -2,9 +2,13 @@ export default {
2
2
  kSocketId: Symbol('socket id'),
3
3
  kSocketStartTime: Symbol('socket start time'),
4
4
  kSocketConnectedTime: Symbol('socket connected time'),
5
+ kSocketConnectErrorTime: Symbol('socket connectError time'),
5
6
  kSocketRequestEndTime: Symbol('socket request end time'),
6
7
  kSocketLocalAddress: Symbol('socket local address'),
7
8
  kSocketLocalPort: Symbol('socket local port'),
9
+ kSocketConnectHost: Symbol('socket connect params: host'),
10
+ kSocketConnectPort: Symbol('socket connect params: port'),
11
+ kSocketConnectProtocol: Symbol('socket connect params: protocol'),
8
12
  kHandledRequests: Symbol('handled requests per socket'),
9
13
  kHandledResponses: Symbol('handled responses per socket'),
10
14
  kRequestSocket: Symbol('request on the socket'),
@@ -13,4 +17,5 @@ export default {
13
17
  kEnableRequestTiming: Symbol('enable request timing or not'),
14
18
  kRequestTiming: Symbol('request timing'),
15
19
  kRequestOriginalOpaque: Symbol('request original opaque'),
20
+ kErrorSocket: Symbol('socket of error'),
16
21
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "urllib",
3
- "version": "3.20.0",
3
+ "version": "3.21.0",
4
4
  "publishConfig": {
5
5
  "tag": "latest"
6
6
  },
@@ -32,8 +32,9 @@
32
32
  "build:esm:test": "cd test/esm && rm -rf node_modules && npm link ../.. && node index.js",
33
33
  "build:mts:test": "cd test/mts && rm -rf node_modules && npm link ../.. && tsc",
34
34
  "build:test": "npm run build && npm run build:cjs:test && npm run build:esm:test && npm run build:mts:test && npm run test-tsc",
35
- "test-tsc": "npm run test-tsc:cjs && npm run test-tsc:esm",
35
+ "test-tsc": "npm run test-tsc:cjs:es2021 && npm run test-tsc:cjs && npm run test-tsc:esm",
36
36
  "test-tsc:cjs": "cd test/fixtures/ts && rm -rf node_modules && npm link ../../.. && npm run build",
37
+ "test-tsc:cjs:es2021": "cd test/fixtures/ts-cjs-es2021 && rm -rf node_modules && npm link ../../.. && npm run build",
37
38
  "test-tsc:esm": "cd test/fixtures/ts-esm && rm -rf node_modules && npm link ../../.. && npm run build",
38
39
  "test": "npm run lint && vitest run",
39
40
  "test-keepalive": "cross-env TEST_KEEPALIVE_COUNT=50 vitest run --test-timeout 180000 keep-alive-header.test.ts",
package/src/HttpAgent.ts CHANGED
@@ -77,7 +77,7 @@ export class HttpAgent extends Agent {
77
77
  }
78
78
  const family = isIP(hostname);
79
79
  if (family === 4 || family === 6) {
80
- // if request hostname is ip, custom lookup won't excute
80
+ // if request hostname is ip, custom lookup won't execute
81
81
  if (!this.#checkAddress(hostname, family)) {
82
82
  throw new IllegalAddressError(hostname, hostname, family);
83
83
  }
package/src/HttpClient.ts CHANGED
@@ -38,6 +38,7 @@ import { RawResponseWithMeta, HttpClientResponse, SocketInfo } from './Response.
38
38
  import { parseJSON, sleep, digestAuthHeader, globalId, performanceTime, isReadable } from './utils.js';
39
39
  import symbols from './symbols.js';
40
40
  import { initDiagnosticsChannel } from './diagnosticsChannel.js';
41
+ import { HttpClientConnectTimeoutError, HttpClientRequestTimeoutError } from './HttpClientError.js';
41
42
 
42
43
  type Exists<T> = T extends undefined ? never : T;
43
44
  type UndiciRequestOption = Exists<Parameters<typeof undiciRequest>[1]>;
@@ -121,15 +122,6 @@ class BlobFromStream {
121
122
  }
122
123
  }
123
124
 
124
- class HttpClientRequestTimeoutError extends Error {
125
- constructor(timeout: number, options: ErrorOptions) {
126
- const message = `Request timeout for ${timeout} ms`;
127
- super(message, options);
128
- this.name = this.constructor.name;
129
- Error.captureStackTrace(this, this.constructor);
130
- }
131
- }
132
-
133
125
  export const HEADER_USER_AGENT = createUserAgent('node-urllib', 'VERSION');
134
126
 
135
127
  function getFileName(stream: Readable) {
@@ -653,13 +645,15 @@ export class HttpClient extends EventEmitter {
653
645
  }
654
646
 
655
647
  return clientResponse;
656
- } catch (e: any) {
657
- debug('Request#%d throw error: %s', requestId, e);
658
- let err = e;
648
+ } catch (rawError: any) {
649
+ debug('Request#%d throw error: %s', requestId, rawError);
650
+ let err = rawError;
659
651
  if (err.name === 'HeadersTimeoutError') {
660
- err = new HttpClientRequestTimeoutError(headersTimeout, { cause: e });
652
+ err = new HttpClientRequestTimeoutError(headersTimeout, { cause: err });
661
653
  } else if (err.name === 'BodyTimeoutError') {
662
- err = new HttpClientRequestTimeoutError(bodyTimeout, { cause: e });
654
+ err = new HttpClientRequestTimeoutError(bodyTimeout, { cause: err });
655
+ } else if (err.code === 'UND_ERR_CONNECT_TIMEOUT') {
656
+ err = new HttpClientConnectTimeoutError(err.message, err.code, { cause: err });
663
657
  } else if (err.code === 'UND_ERR_SOCKET' || err.code === 'ECONNRESET') {
664
658
  // auto retry on socket error, https://github.com/node-modules/urllib/issues/454
665
659
  if (args.socketErrorRetry > 0 && requestContext.socketErrorRetries < args.socketErrorRetry) {
@@ -681,7 +675,7 @@ export class HttpClient extends EventEmitter {
681
675
  res.requestUrls.push(requestUrl.href);
682
676
  }
683
677
  res.rt = performanceTime(requestStartTime);
684
- this.#updateSocketInfo(socketInfo, internalOpaque);
678
+ this.#updateSocketInfo(socketInfo, internalOpaque, rawError);
685
679
 
686
680
  channels.response.publish({
687
681
  request: reqMeta,
@@ -704,21 +698,38 @@ export class HttpClient extends EventEmitter {
704
698
  }
705
699
  }
706
700
 
707
- #updateSocketInfo(socketInfo: SocketInfo, internalOpaque: any) {
708
- const socket = internalOpaque[symbols.kRequestSocket];
701
+ #updateSocketInfo(socketInfo: SocketInfo, internalOpaque: any, err?: any) {
702
+ const socket = internalOpaque[symbols.kRequestSocket] ?? err?.[symbols.kErrorSocket];
709
703
  if (socket) {
710
704
  socketInfo.id = socket[symbols.kSocketId];
711
705
  socketInfo.handledRequests = socket[symbols.kHandledRequests];
712
706
  socketInfo.handledResponses = socket[symbols.kHandledResponses];
713
- socketInfo.localAddress = socket[symbols.kSocketLocalAddress];
714
- socketInfo.localPort = socket[symbols.kSocketLocalPort];
715
- socketInfo.remoteAddress = socket.remoteAddress;
716
- socketInfo.remotePort = socket.remotePort;
717
- socketInfo.remoteFamily = socket.remoteFamily;
707
+ if (socket[symbols.kSocketLocalAddress]) {
708
+ socketInfo.localAddress = socket[symbols.kSocketLocalAddress];
709
+ socketInfo.localPort = socket[symbols.kSocketLocalPort];
710
+ }
711
+ if (socket.remoteAddress) {
712
+ socketInfo.remoteAddress = socket.remoteAddress;
713
+ socketInfo.remotePort = socket.remotePort;
714
+ socketInfo.remoteFamily = socket.remoteFamily;
715
+ }
718
716
  socketInfo.bytesRead = socket.bytesRead;
719
717
  socketInfo.bytesWritten = socket.bytesWritten;
720
- socketInfo.connectedTime = socket[symbols.kSocketConnectedTime];
721
- socketInfo.lastRequestEndTime = socket[symbols.kSocketRequestEndTime];
718
+ if (socket[symbols.kSocketConnectErrorTime]) {
719
+ socketInfo.connectErrorTime = socket[symbols.kSocketConnectErrorTime];
720
+ if (Array.isArray(socket.autoSelectFamilyAttemptedAddresses)) {
721
+ socketInfo.attemptedRemoteAddresses = socket.autoSelectFamilyAttemptedAddresses;
722
+ }
723
+ socketInfo.connectProtocol = socket[symbols.kSocketConnectProtocol];
724
+ socketInfo.connectHost = socket[symbols.kSocketConnectHost];
725
+ socketInfo.connectPort = socket[symbols.kSocketConnectPort];
726
+ }
727
+ if (socket[symbols.kSocketConnectedTime]) {
728
+ socketInfo.connectedTime = socket[symbols.kSocketConnectedTime];
729
+ }
730
+ if (socket[symbols.kSocketRequestEndTime]) {
731
+ socketInfo.lastRequestEndTime = socket[symbols.kSocketRequestEndTime];
732
+ }
722
733
  socket[symbols.kSocketRequestEndTime] = new Date();
723
734
  }
724
735
  }
@@ -0,0 +1,34 @@
1
+ import type { RawResponseWithMeta, SocketInfo } from './Response.js';
2
+ import type { IncomingHttpHeaders } from './IncomingHttpHeaders.js';
3
+
4
+ // need to support ES2021
5
+ interface ErrorOptions {
6
+ cause?: Error;
7
+ }
8
+
9
+ export class HttpClientRequestError extends Error {
10
+ status?: number;
11
+ headers?: IncomingHttpHeaders;
12
+ socket?: SocketInfo;
13
+ res?: RawResponseWithMeta;
14
+ }
15
+
16
+ export class HttpClientRequestTimeoutError extends HttpClientRequestError {
17
+ constructor(timeout: number, options: ErrorOptions) {
18
+ const message = `Request timeout for ${timeout} ms`;
19
+ super(message, options);
20
+ this.name = this.constructor.name;
21
+ Error.captureStackTrace(this, this.constructor);
22
+ }
23
+ }
24
+
25
+ export class HttpClientConnectTimeoutError extends HttpClientRequestError {
26
+ code: string;
27
+
28
+ constructor(message: string, code: string, options: ErrorOptions) {
29
+ super(message, options);
30
+ this.name = this.constructor.name;
31
+ this.code = code;
32
+ Error.captureStackTrace(this, this.constructor);
33
+ }
34
+ }
package/src/Response.ts CHANGED
@@ -13,7 +13,12 @@ export type SocketInfo = {
13
13
  handledRequests: number;
14
14
  handledResponses: number;
15
15
  connectedTime?: Date;
16
+ connectErrorTime?: Date;
16
17
  lastRequestEndTime?: Date;
18
+ attemptedRemoteAddresses?: string[];
19
+ connectProtocol?: string;
20
+ connectHost?: string;
21
+ connectPort?: string;
17
22
  };
18
23
 
19
24
  /**
@@ -35,9 +35,25 @@ function formatSocket(socket: Socket) {
35
35
  localPort: socket[symbols.kSocketLocalPort],
36
36
  remoteAddress: socket.remoteAddress,
37
37
  remotePort: socket.remotePort,
38
+ attemptedAddresses: socket.autoSelectFamilyAttemptedAddresses,
39
+ connecting: socket.connecting,
38
40
  };
39
41
  }
40
42
 
43
+ // make sure error contains socket info
44
+ const kDestroy = Symbol('kDestroy');
45
+ Socket.prototype[kDestroy] = Socket.prototype.destroy;
46
+ Socket.prototype.destroy = function(err?: any) {
47
+ if (err) {
48
+ Object.defineProperty(err, symbols.kErrorSocket, {
49
+ // don't show on console log
50
+ enumerable: false,
51
+ value: this,
52
+ });
53
+ }
54
+ return this[kDestroy](err);
55
+ };
56
+
41
57
  export function initDiagnosticsChannel() {
42
58
  // makre sure init global DiagnosticsChannel once
43
59
  if (initedDiagnosticsChannel) return;
@@ -67,10 +83,37 @@ export function initDiagnosticsChannel() {
67
83
  });
68
84
 
69
85
  // diagnosticsChannel.channel('undici:client:beforeConnect')
70
- // diagnosticsChannel.channel('undici:client:connectError')
86
+
87
+ subscribe('undici:client:connectError', (message, name) => {
88
+ const { error, connectParams } = message as DiagnosticsChannel.ClientConnectErrorMessage & { error: any };
89
+ let { socket } = message as DiagnosticsChannel.ClientConnectErrorMessage;
90
+ if (!socket && error[symbols.kErrorSocket]) {
91
+ socket = error[symbols.kErrorSocket];
92
+ }
93
+ if (socket) {
94
+ socket[symbols.kSocketId] = globalId('UndiciSocket');
95
+ socket[symbols.kSocketConnectErrorTime] = new Date();
96
+ socket[symbols.kHandledRequests] = 0;
97
+ socket[symbols.kHandledResponses] = 0;
98
+ // copy local address to symbol, avoid them be reset after request error throw
99
+ if (socket.localAddress) {
100
+ socket[symbols.kSocketLocalAddress] = socket.localAddress;
101
+ socket[symbols.kSocketLocalPort] = socket.localPort;
102
+ }
103
+ socket[symbols.kSocketConnectProtocol] = connectParams.protocol;
104
+ socket[symbols.kSocketConnectHost] = connectParams.host;
105
+ socket[symbols.kSocketConnectPort] = connectParams.port;
106
+ debug('[%s] Socket#%d connectError, connectParams: %o, error: %s, (sock: %o)',
107
+ name, socket[symbols.kSocketId], connectParams, (error as Error).message, formatSocket(socket));
108
+ } else {
109
+ debug('[%s] connectError, connectParams: %o, error: %o',
110
+ name, connectParams, error);
111
+ }
112
+ });
113
+
71
114
  // This message is published after a connection is established.
72
115
  subscribe('undici:client:connected', (message, name) => {
73
- const { socket } = message as DiagnosticsChannel.ClientConnectedMessage;
116
+ const { socket, connectParams } = message as DiagnosticsChannel.ClientConnectedMessage;
74
117
  socket[symbols.kSocketId] = globalId('UndiciSocket');
75
118
  socket[symbols.kSocketStartTime] = performance.now();
76
119
  socket[symbols.kSocketConnectedTime] = new Date();
@@ -79,6 +122,9 @@ export function initDiagnosticsChannel() {
79
122
  // copy local address to symbol, avoid them be reset after request error throw
80
123
  socket[symbols.kSocketLocalAddress] = socket.localAddress;
81
124
  socket[symbols.kSocketLocalPort] = socket.localPort;
125
+ socket[symbols.kSocketConnectProtocol] = connectParams.protocol;
126
+ socket[symbols.kSocketConnectHost] = connectParams.host;
127
+ socket[symbols.kSocketConnectPort] = connectParams.port;
82
128
  debug('[%s] Socket#%d connected (sock: %o)', name, socket[symbols.kSocketId], formatSocket(socket));
83
129
  });
84
130
 
package/src/index.ts CHANGED
@@ -53,6 +53,7 @@ export {
53
53
  export {
54
54
  IncomingHttpHeaders,
55
55
  } from './IncomingHttpHeaders.js';
56
+ export * from './HttpClientError.js';
56
57
 
57
58
  export default {
58
59
  request,
package/src/symbols.ts CHANGED
@@ -2,9 +2,13 @@ export default {
2
2
  kSocketId: Symbol('socket id'),
3
3
  kSocketStartTime: Symbol('socket start time'),
4
4
  kSocketConnectedTime: Symbol('socket connected time'),
5
+ kSocketConnectErrorTime: Symbol('socket connectError time'),
5
6
  kSocketRequestEndTime: Symbol('socket request end time'),
6
7
  kSocketLocalAddress: Symbol('socket local address'),
7
8
  kSocketLocalPort: Symbol('socket local port'),
9
+ kSocketConnectHost: Symbol('socket connect params: host'),
10
+ kSocketConnectPort: Symbol('socket connect params: port'),
11
+ kSocketConnectProtocol: Symbol('socket connect params: protocol'),
8
12
  kHandledRequests: Symbol('handled requests per socket'),
9
13
  kHandledResponses: Symbol('handled responses per socket'),
10
14
  kRequestSocket: Symbol('request on the socket'),
@@ -13,4 +17,5 @@ export default {
13
17
  kEnableRequestTiming: Symbol('enable request timing or not'),
14
18
  kRequestTiming: Symbol('request timing'),
15
19
  kRequestOriginalOpaque: Symbol('request original opaque'),
20
+ kErrorSocket: Symbol('socket of error'),
16
21
  };