urllib 3.16.1 → 3.17.1

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.
@@ -67,11 +67,16 @@ export type RequestOptions = {
67
67
  headers?: IncomingHttpHeaders;
68
68
  /**
69
69
  * Request timeout in milliseconds for connecting phase and response receiving phase.
70
- * Defaults to exports.
71
- * TIMEOUT, both are 5s. You can use timeout: 5000 to tell urllib use same timeout on two phase or set them seperately such as
70
+ * Defaults is `5000`, both are 5 seconds. You can use timeout: 5000 to tell urllib use same timeout on two phase or set them separately such as
72
71
  * timeout: [3000, 5000], which will set connecting timeout to 3s and response 5s.
73
72
  */
74
73
  timeout?: number | number[];
74
+ /**
75
+ * Default is `4000`, 4 seconds - The timeout after which a socket without active requests will time out.
76
+ * Monitors time between activity on a connected socket.
77
+ * This value may be overridden by *keep-alive* hints from the server. See [MDN: HTTP - Headers - Keep-Alive directives](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive#directives) for more details.
78
+ */
79
+ keepAliveTimeout?: number;
75
80
  /**
76
81
  * username:password used in HTTP Basic Authorization.
77
82
  * Alias to `headers.authorization = xxx`
@@ -89,7 +94,7 @@ export type RequestOptions = {
89
94
  formatRedirectUrl?: (a: any, b: any) => void;
90
95
  /** Before request hook, you can change every thing here. */
91
96
  beforeRequest?: (...args: any[]) => void;
92
- /** Accept `gzip, br` response content and auto decode it, default is false. */
97
+ /** Accept `gzip, br` response content and auto decode it, default is `true`. */
93
98
  compressed?: boolean;
94
99
  /**
95
100
  * @deprecated
@@ -97,11 +102,11 @@ export type RequestOptions = {
97
102
  * */
98
103
  gzip?: boolean;
99
104
  /**
100
- * Enable timing or not, default is false.
105
+ * Enable timing or not, default is `true`.
101
106
  * */
102
107
  timing?: boolean;
103
108
  /**
104
- * Auto retry times on 5xx response, default is 0. Don't work on streaming request
109
+ * Auto retry times on 5xx response, default is `0`. Don't work on streaming request
105
110
  * It's not supported by using retry and writeStream, because the retry request can't stop the stream which is consuming.
106
111
  **/
107
112
  retry?: number;
@@ -112,6 +117,11 @@ export type RequestOptions = {
112
117
  * It will retry when status >= 500 by default. Request error is not included.
113
118
  */
114
119
  isRetry?: (response: HttpClientResponse) => boolean;
120
+ /**
121
+ * Auto retry times on socket error, default is `1`. Don't work on streaming request
122
+ * It's not supported by using retry and writeStream, because the retry request can't stop the stream which is consuming.
123
+ **/
124
+ socketErrorRetry?: number;
115
125
  /** Default: `null` */
116
126
  opaque?: unknown;
117
127
  /**
@@ -132,3 +142,10 @@ export type RequestOptions = {
132
142
  /** Default: `64 KiB` */
133
143
  highWaterMark?: number;
134
144
  };
145
+ export type RequestMeta = {
146
+ requestId: number;
147
+ url: string;
148
+ args: RequestOptions;
149
+ ctx?: unknown;
150
+ retries: number;
151
+ };
@@ -13,6 +13,8 @@ export type SocketInfo = {
13
13
  bytesRead: number;
14
14
  handledRequests: number;
15
15
  handledResponses: number;
16
+ connectedTime?: Date;
17
+ lastRequestEndTime?: Date;
16
18
  };
17
19
  /**
18
20
  * https://eggjs.org/en/core/httpclient.html#timing-boolean
@@ -37,6 +39,8 @@ export type RawResponseWithMeta = Readable & {
37
39
  rt: number;
38
40
  keepAliveSocket: boolean;
39
41
  requestUrls: string[];
42
+ retries: number;
43
+ socketErrorRetries: number;
40
44
  };
41
45
  export type HttpClientResponse<T = any> = {
42
46
  opaque: unknown;
@@ -15,6 +15,25 @@ let initedDiagnosticsChannel = false;
15
15
  // server --> client
16
16
  // undici:request:headers => { request, response }
17
17
  // -> undici:request:trailers => { request, trailers }
18
+ function subscribe(name, listener) {
19
+ if (typeof diagnosticsChannel.subscribe === 'function') {
20
+ diagnosticsChannel.subscribe(name, listener);
21
+ }
22
+ else {
23
+ // TODO: support Node.js 14, will be removed on the next major version
24
+ diagnosticsChannel.channel(name).subscribe(listener);
25
+ }
26
+ }
27
+ function formatSocket(socket) {
28
+ if (!socket)
29
+ return socket;
30
+ return {
31
+ localAddress: socket[symbols.kSocketLocalAddress],
32
+ localPort: socket[symbols.kSocketLocalPort],
33
+ remoteAddress: socket.remoteAddress,
34
+ remotePort: socket.remotePort,
35
+ };
36
+ }
18
37
  export function initDiagnosticsChannel() {
19
38
  // makre sure init global DiagnosticsChannel once
20
39
  if (initedDiagnosticsChannel)
@@ -23,7 +42,7 @@ export function initDiagnosticsChannel() {
23
42
  let kHandler;
24
43
  // This message is published when a new outgoing request is created.
25
44
  // Note: a request is only loosely completed to a given socket.
26
- diagnosticsChannel.channel('undici:request:create').subscribe((message, name) => {
45
+ subscribe('undici:request:create', (message, name) => {
27
46
  const { request } = message;
28
47
  if (!kHandler) {
29
48
  const symbols = Object.getOwnPropertySymbols(request);
@@ -46,16 +65,20 @@ export function initDiagnosticsChannel() {
46
65
  // diagnosticsChannel.channel('undici:client:beforeConnect')
47
66
  // diagnosticsChannel.channel('undici:client:connectError')
48
67
  // This message is published after a connection is established.
49
- diagnosticsChannel.channel('undici:client:connected').subscribe((message, name) => {
68
+ subscribe('undici:client:connected', (message, name) => {
50
69
  const { socket } = message;
51
70
  socket[symbols.kSocketId] = globalId('UndiciSocket');
52
71
  socket[symbols.kSocketStartTime] = performance.now();
72
+ socket[symbols.kSocketConnectedTime] = new Date();
53
73
  socket[symbols.kHandledRequests] = 0;
54
74
  socket[symbols.kHandledResponses] = 0;
55
- debug('[%s] Socket#%d connected', name, socket[symbols.kSocketId]);
75
+ // copy local address to symbol, avoid them be reset after request error throw
76
+ socket[symbols.kSocketLocalAddress] = socket.localAddress;
77
+ socket[symbols.kSocketLocalPort] = socket.localPort;
78
+ debug('[%s] Socket#%d connected (sock: %o)', name, socket[symbols.kSocketId], formatSocket(socket));
56
79
  });
57
80
  // This message is published right before the first byte of the request is written to the socket.
58
- diagnosticsChannel.channel('undici:client:sendHeaders').subscribe((message, name) => {
81
+ subscribe('undici:client:sendHeaders', (message, name) => {
59
82
  const { request, socket } = message;
60
83
  if (!kHandler)
61
84
  return;
@@ -65,7 +88,7 @@ export function initDiagnosticsChannel() {
65
88
  socket[symbols.kHandledRequests]++;
66
89
  // attach socket to opaque
67
90
  opaque[symbols.kRequestSocket] = socket;
68
- debug('[%s] Request#%d send headers on Socket#%d (handled %d requests)', name, opaque[symbols.kRequestId], socket[symbols.kSocketId], socket[symbols.kHandledRequests]);
91
+ debug('[%s] Request#%d send headers on Socket#%d (handled %d requests, sock: %o)', name, opaque[symbols.kRequestId], socket[symbols.kSocketId], socket[symbols.kHandledRequests], formatSocket(socket));
69
92
  if (!opaque[symbols.kEnableRequestTiming])
70
93
  return;
71
94
  opaque[symbols.kRequestTiming].requestHeadersSent = performanceTime(opaque[symbols.kRequestStartTime]);
@@ -76,7 +99,7 @@ export function initDiagnosticsChannel() {
76
99
  performanceTime(opaque[symbols.kRequestStartTime], socket[symbols.kSocketStartTime]);
77
100
  }
78
101
  });
79
- diagnosticsChannel.channel('undici:request:bodySent').subscribe((message, name) => {
102
+ subscribe('undici:request:bodySent', (message, name) => {
80
103
  const { request } = message;
81
104
  if (!kHandler)
82
105
  return;
@@ -89,7 +112,7 @@ export function initDiagnosticsChannel() {
89
112
  opaque[symbols.kRequestTiming].requestSent = performanceTime(opaque[symbols.kRequestStartTime]);
90
113
  });
91
114
  // This message is published after the response headers have been received, i.e. the response has been completed.
92
- diagnosticsChannel.channel('undici:request:headers').subscribe((message, name) => {
115
+ subscribe('undici:request:headers', (message, name) => {
93
116
  const { request, response } = message;
94
117
  if (!kHandler)
95
118
  return;
@@ -99,13 +122,13 @@ export function initDiagnosticsChannel() {
99
122
  // get socket from opaque
100
123
  const socket = opaque[symbols.kRequestSocket];
101
124
  socket[symbols.kHandledResponses]++;
102
- debug('[%s] Request#%d get %s response headers on Socket#%d (handled %d responses)', name, opaque[symbols.kRequestId], response.statusCode, socket[symbols.kSocketId], socket[symbols.kHandledResponses]);
125
+ 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));
103
126
  if (!opaque[symbols.kEnableRequestTiming])
104
127
  return;
105
128
  opaque[symbols.kRequestTiming].waiting = performanceTime(opaque[symbols.kRequestStartTime]);
106
129
  });
107
130
  // This message is published after the response body and trailers have been received, i.e. the response has been completed.
108
- diagnosticsChannel.channel('undici:request:trailers').subscribe((message, name) => {
131
+ subscribe('undici:request:trailers', (message, name) => {
109
132
  const { request } = message;
110
133
  if (!kHandler)
111
134
  return;
@@ -117,5 +140,14 @@ export function initDiagnosticsChannel() {
117
140
  return;
118
141
  opaque[symbols.kRequestTiming].contentDownload = performanceTime(opaque[symbols.kRequestStartTime]);
119
142
  });
120
- // diagnosticsChannel.channel('undici:request:error')
143
+ // This message is published if the request is going to error, but it has not errored yet.
144
+ // subscribe('undici:request:error', (message, name) => {
145
+ // const { request, error } = message as DiagnosticsChannel.RequestErrorMessage;
146
+ // const opaque = request[kHandler]?.opts?.opaque;
147
+ // if (!opaque || !opaque[symbols.kRequestId]) return;
148
+ // const socket = opaque[symbols.kRequestSocket];
149
+ // debug('[%s] Request#%d error on Socket#%d (handled %d responses, sock: %o), error: %o',
150
+ // name, opaque[symbols.kRequestId], socket[symbols.kSocketId], socket[symbols.kHandledResponses],
151
+ // formatSocket(socket), error);
152
+ // });
121
153
  }
@@ -2,7 +2,7 @@ import { RequestOptions, RequestURL } from './Request';
2
2
  export declare function request<T = any>(url: RequestURL, options?: RequestOptions): Promise<import("./Response").HttpClientResponse<T>>;
3
3
  export declare function curl<T = any>(url: RequestURL, options?: RequestOptions): Promise<import("./Response").HttpClientResponse<T>>;
4
4
  export { MockAgent, ProxyAgent, Agent, Dispatcher, setGlobalDispatcher, getGlobalDispatcher, } from 'undici';
5
- export { HttpClient, HttpClient as HttpClient2, HEADER_USER_AGENT as USER_AGENT } from './HttpClient';
5
+ export { HttpClient, HttpClient as HttpClient2, HEADER_USER_AGENT as USER_AGENT, RequestDiagnosticsMessage, ResponseDiagnosticsMessage, } from './HttpClient';
6
6
  export { RequestOptions, RequestOptions as RequestOptions2, RequestURL, HttpMethod, FixJSONCtlCharsHandler, FixJSONCtlChars, } from './Request';
7
7
  export { SocketInfo, Timing, RawResponseWithMeta, HttpClientResponse } from './Response';
8
8
  declare const _default: {
package/src/esm/index.js CHANGED
@@ -28,7 +28,7 @@ export async function curl(url, options) {
28
28
  }
29
29
  export { MockAgent, ProxyAgent, Agent, Dispatcher, setGlobalDispatcher, getGlobalDispatcher, } from 'undici';
30
30
  // HttpClient2 is keep compatible with urlib@2 HttpClient2
31
- export { HttpClient, HttpClient as HttpClient2, HEADER_USER_AGENT as USER_AGENT } from './HttpClient.js';
31
+ export { HttpClient, HttpClient as HttpClient2, HEADER_USER_AGENT as USER_AGENT, } from './HttpClient.js';
32
32
  export default {
33
33
  request,
34
34
  curl,
@@ -1,6 +1,10 @@
1
1
  declare const _default: {
2
2
  kSocketId: symbol;
3
3
  kSocketStartTime: symbol;
4
+ kSocketConnectedTime: symbol;
5
+ kSocketRequestEndTime: symbol;
6
+ kSocketLocalAddress: symbol;
7
+ kSocketLocalPort: symbol;
4
8
  kHandledRequests: symbol;
5
9
  kHandledResponses: symbol;
6
10
  kRequestSocket: symbol;
@@ -1,6 +1,10 @@
1
1
  export default {
2
2
  kSocketId: Symbol('socket id'),
3
3
  kSocketStartTime: Symbol('socket start time'),
4
+ kSocketConnectedTime: Symbol('socket connected time'),
5
+ kSocketRequestEndTime: Symbol('socket request end time'),
6
+ kSocketLocalAddress: Symbol('socket local address'),
7
+ kSocketLocalPort: Symbol('socket local port'),
4
8
  kHandledRequests: Symbol('handled requests per socket'),
5
9
  kHandledResponses: Symbol('handled responses per socket'),
6
10
  kRequestSocket: Symbol('request on the socket'),
package/src/index.ts CHANGED
@@ -36,7 +36,10 @@ export {
36
36
  setGlobalDispatcher, getGlobalDispatcher,
37
37
  } from 'undici';
38
38
  // HttpClient2 is keep compatible with urlib@2 HttpClient2
39
- export { HttpClient, HttpClient as HttpClient2, HEADER_USER_AGENT as USER_AGENT } from './HttpClient';
39
+ export {
40
+ HttpClient, HttpClient as HttpClient2, HEADER_USER_AGENT as USER_AGENT,
41
+ RequestDiagnosticsMessage, ResponseDiagnosticsMessage,
42
+ } from './HttpClient';
40
43
  // RequestOptions2 is keep compatible with urlib@2 RequestOptions2
41
44
  export {
42
45
  RequestOptions, RequestOptions as RequestOptions2, RequestURL, HttpMethod,
package/src/symbols.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  export default {
2
2
  kSocketId: Symbol('socket id'),
3
3
  kSocketStartTime: Symbol('socket start time'),
4
+ kSocketConnectedTime: Symbol('socket connected time'),
5
+ kSocketRequestEndTime: Symbol('socket request end time'),
6
+ kSocketLocalAddress: Symbol('socket local address'),
7
+ kSocketLocalPort: Symbol('socket local port'),
4
8
  kHandledRequests: Symbol('handled requests per socket'),
5
9
  kHandledResponses: Symbol('handled responses per socket'),
6
10
  kRequestSocket: Symbol('request on the socket'),