Skip to content

Commit 34ca36a

Browse files
nodejs-github-botaduh95
authored andcommitted
deps: update undici to 6.20.0
PR-URL: #55329 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Michael Dawson <midawson@redhat.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: Matthew Aitken <maitken033380023@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
1 parent ff3cc3b commit 34ca36a

27 files changed

+1528
-608
lines changed

deps/undici/src/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,6 @@ undici-fetch.js
8484
.npmrc
8585

8686
.tap
87+
88+
# File generated by /test/request-timeout.js
89+
test/request-timeout.10mb.bin

deps/undici/src/.npmignore

+3
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@ lib/llhttp/llhttp.wasm
1111
!index.d.ts
1212
!docs/docs/**/*
1313
!scripts/strip-comments.js
14+
15+
# File generated by /test/request-timeout.js
16+
test/request-timeout.10mb.bin

deps/undici/src/docs/docs/api/Dispatcher.md

+197
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,203 @@ client.dispatch(
984984
);
985985
```
986986

987+
##### `Response Error Interceptor`
988+
989+
**Introduction**
990+
991+
The Response Error Interceptor is designed to handle HTTP response errors efficiently. It intercepts responses and throws detailed errors for responses with status codes indicating failure (4xx, 5xx). This interceptor enhances error handling by providing structured error information, including response headers, data, and status codes.
992+
993+
**ResponseError Class**
994+
995+
The `ResponseError` class extends the `UndiciError` class and encapsulates detailed error information. It captures the response status code, headers, and data, providing a structured way to handle errors.
996+
997+
**Definition**
998+
999+
```js
1000+
class ResponseError extends UndiciError {
1001+
constructor (message, code, { headers, data }) {
1002+
super(message);
1003+
this.name = 'ResponseError';
1004+
this.message = message || 'Response error';
1005+
this.code = 'UND_ERR_RESPONSE';
1006+
this.statusCode = code;
1007+
this.data = data;
1008+
this.headers = headers;
1009+
}
1010+
}
1011+
```
1012+
1013+
**Interceptor Handler**
1014+
1015+
The interceptor's handler class extends `DecoratorHandler` and overrides methods to capture response details and handle errors based on the response status code.
1016+
1017+
**Methods**
1018+
1019+
- **onConnect**: Initializes response properties.
1020+
- **onHeaders**: Captures headers and status code. Decodes body if content type is `application/json` or `text/plain`.
1021+
- **onData**: Appends chunks to the body if status code indicates an error.
1022+
- **onComplete**: Finalizes error handling, constructs a `ResponseError`, and invokes the `onError` method.
1023+
- **onError**: Propagates errors to the handler.
1024+
1025+
**Definition**
1026+
1027+
```js
1028+
class Handler extends DecoratorHandler {
1029+
// Private properties
1030+
#handler;
1031+
#statusCode;
1032+
#contentType;
1033+
#decoder;
1034+
#headers;
1035+
#body;
1036+
1037+
constructor (opts, { handler }) {
1038+
super(handler);
1039+
this.#handler = handler;
1040+
}
1041+
1042+
onConnect (abort) {
1043+
this.#statusCode = 0;
1044+
this.#contentType = null;
1045+
this.#decoder = null;
1046+
this.#headers = null;
1047+
this.#body = '';
1048+
return this.#handler.onConnect(abort);
1049+
}
1050+
1051+
onHeaders (statusCode, rawHeaders, resume, statusMessage, headers = parseHeaders(rawHeaders)) {
1052+
this.#statusCode = statusCode;
1053+
this.#headers = headers;
1054+
this.#contentType = headers['content-type'];
1055+
1056+
if (this.#statusCode < 400) {
1057+
return this.#handler.onHeaders(statusCode, rawHeaders, resume, statusMessage, headers);
1058+
}
1059+
1060+
if (this.#contentType === 'application/json' || this.#contentType === 'text/plain') {
1061+
this.#decoder = new TextDecoder('utf-8');
1062+
}
1063+
}
1064+
1065+
onData (chunk) {
1066+
if (this.#statusCode < 400) {
1067+
return this.#handler.onData(chunk);
1068+
}
1069+
this.#body += this.#decoder?.decode(chunk, { stream: true }) ?? '';
1070+
}
1071+
1072+
onComplete (rawTrailers) {
1073+
if (this.#statusCode >= 400) {
1074+
this.#body += this.#decoder?.decode(undefined, { stream: false }) ?? '';
1075+
if (this.#contentType === 'application/json') {
1076+
try {
1077+
this.#body = JSON.parse(this.#body);
1078+
} catch {
1079+
// Do nothing...
1080+
}
1081+
}
1082+
1083+
let err;
1084+
const stackTraceLimit = Error.stackTraceLimit;
1085+
Error.stackTraceLimit = 0;
1086+
try {
1087+
err = new ResponseError('Response Error', this.#statusCode, this.#headers, this.#body);
1088+
} finally {
1089+
Error.stackTraceLimit = stackTraceLimit;
1090+
}
1091+
1092+
this.#handler.onError(err);
1093+
} else {
1094+
this.#handler.onComplete(rawTrailers);
1095+
}
1096+
}
1097+
1098+
onError (err) {
1099+
this.#handler.onError(err);
1100+
}
1101+
}
1102+
1103+
module.exports = (dispatch) => (opts, handler) => opts.throwOnError
1104+
? dispatch(opts, new Handler(opts, { handler }))
1105+
: dispatch(opts, handler);
1106+
```
1107+
1108+
**Tests**
1109+
1110+
Unit tests ensure the interceptor functions correctly, handling both error and non-error responses appropriately.
1111+
1112+
**Example Tests**
1113+
1114+
- **No Error if `throwOnError` is False**:
1115+
1116+
```js
1117+
test('should not error if request is not meant to throw error', async (t) => {
1118+
const opts = { throwOnError: false };
1119+
const handler = { onError: () => {}, onData: () => {}, onComplete: () => {} };
1120+
const interceptor = createResponseErrorInterceptor((opts, handler) => handler.onComplete());
1121+
assert.doesNotThrow(() => interceptor(opts, handler));
1122+
});
1123+
```
1124+
1125+
- **Error if Status Code is in Specified Error Codes**:
1126+
1127+
```js
1128+
test('should error if request status code is in the specified error codes', async (t) => {
1129+
const opts = { throwOnError: true, statusCodes: [500] };
1130+
const response = { statusCode: 500 };
1131+
let capturedError;
1132+
const handler = {
1133+
onError: (err) => { capturedError = err; },
1134+
onData: () => {},
1135+
onComplete: () => {}
1136+
};
1137+
1138+
const interceptor = createResponseErrorInterceptor((opts, handler) => {
1139+
if (opts.throwOnError && opts.statusCodes.includes(response.statusCode)) {
1140+
handler.onError(new Error('Response Error'));
1141+
} else {
1142+
handler.onComplete();
1143+
}
1144+
});
1145+
1146+
interceptor({ ...opts, response }, handler);
1147+
1148+
await new Promise(resolve => setImmediate(resolve));
1149+
1150+
assert(capturedError, 'Expected error to be captured but it was not.');
1151+
assert.strictEqual(capturedError.message, 'Response Error');
1152+
assert.strictEqual(response.statusCode, 500);
1153+
});
1154+
```
1155+
1156+
- **No Error if Status Code is Not in Specified Error Codes**:
1157+
1158+
```js
1159+
test('should not error if request status code is not in the specified error codes', async (t) => {
1160+
const opts = { throwOnError: true, statusCodes: [500] };
1161+
const response = { statusCode: 404 };
1162+
const handler = {
1163+
onError: () => {},
1164+
onData: () => {},
1165+
onComplete: () => {}
1166+
};
1167+
1168+
const interceptor = createResponseErrorInterceptor((opts, handler) => {
1169+
if (opts.throwOnError && opts.statusCodes.includes(response.statusCode)) {
1170+
handler.onError(new Error('Response Error'));
1171+
} else {
1172+
handler.onComplete();
1173+
}
1174+
});
1175+
1176+
assert.doesNotThrow(() => interceptor({ ...opts, response }, handler));
1177+
});
1178+
```
1179+
1180+
**Conclusion**
1181+
1182+
The Response Error Interceptor provides a robust mechanism for handling HTTP response errors by capturing detailed error information and propagating it through a structured `ResponseError` class. This enhancement improves error handling and debugging capabilities in applications using the interceptor.
1183+
9871184
## Instance Events
9881185
9891186
### Event: `'connect'`

deps/undici/src/docs/docs/api/RetryHandler.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Extends: [`Dispatch.DispatchOptions`](Dispatcher.md#parameter-dispatchoptions).
1919

2020
#### `RetryOptions`
2121

22-
- **retry** `(err: Error, context: RetryContext, callback: (err?: Error | null) => void) => void` (optional) - Function to be called after every retry. It should pass error if no more retries should be performed.
22+
- **retry** `(err: Error, context: RetryContext, callback: (err?: Error | null) => void) => number | null` (optional) - Function to be called after every retry. It should pass error if no more retries should be performed.
2323
- **maxRetries** `number` (optional) - Maximum number of retries. Default: `5`
2424
- **maxTimeout** `number` (optional) - Maximum number of milliseconds to wait before retrying. Default: `30000` (30 seconds)
2525
- **minTimeout** `number` (optional) - Minimum number of milliseconds to wait before retrying. Default: `500` (half a second)

deps/undici/src/lib/api/api-upgrade.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ class UpgradeHandler extends AsyncResource {
5050
}
5151

5252
onUpgrade (statusCode, rawHeaders, socket) {
53-
const { callback, opaque, context } = this
53+
assert(statusCode === 101)
5454

55-
assert.strictEqual(statusCode, 101)
55+
const { callback, opaque, context } = this
5656

5757
removeSignal(this)
5858

0 commit comments

Comments
 (0)