Skip to content

Commit

Permalink
feat: upgrade to undici v7
Browse files Browse the repository at this point in the history
fix server side close unexpected exit

closes #541
  • Loading branch information
fengmk2 committed Nov 28, 2024
1 parent 6d8311e commit a8c5aaf
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 12 deletions.
34 changes: 34 additions & 0 deletions examples/h2-other-side-closed-exit-0-fetch.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const { fetch, setGlobalDispatcher, Agent } = require('..');

setGlobalDispatcher(new Agent({
allowH2: true,
}));

async function main() {
for (let i = 0; i < 100; i++) {
try {
const r = await fetch('https://edgeupdates.microsoft.com/api/products');
console.log(r.status, r.headers, (await r.text()).length);
} catch (err) {
// console.error(err);
// throw err;
if (err.code === 'UND_ERR_SOCKET') {
continue;
} else {
throw err;
}
}
}
}

main().then(() => {
console.log('main end');
}).catch(err => {
console.error('main error throw: %s', err);
// console.error(err);
process.exit(1);
});

process.on('beforeExit', (...args) => {
console.error('beforeExit', args);
});
34 changes: 34 additions & 0 deletions examples/h2-other-side-closed-exit-0.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const { request, Agent, setGlobalDispatcher } = require('undici');

setGlobalDispatcher(new Agent({
allowH2: true,
}));

async function main() {
for (let i = 0; i < 100; i++) {
try {
const r = await request('https://edgeupdates.microsoft.com/api/products');
console.log(r.statusCode, r.headers, (await r.body.blob()).size);
} catch (err) {
// console.error(err);
// throw err;
if (err.code === 'UND_ERR_SOCKET') {
continue;
} else {
throw err;
}
}
}
}

main().then(() => {
console.log('main end');
}).catch(err => {
console.error('main error throw: %s', err);
// console.error(err);
process.exit(1);
});

process.on('beforeExit', (...args) => {
console.error('beforeExit', args);
});
49 changes: 49 additions & 0 deletions examples/longruning.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const { HttpClient } = require('..');

const httpClient = new HttpClient({
allowH2: true,
});

async function main() {
for (let i = 0; i < 1000000; i++) {
// await httpClient.request('https://registry.npmmirror.com/');
// console.log(r.status, r.headers, r.res.timing);
try {
const r = await httpClient.request('https://edgeupdates.microsoft.com/api/products');
// console.log(r.status, r.headers, r.data.length, r.res.timing);
if (i % 10 === 0) {
// console.log(r.status, r.headers, r.data.length, r.res.timing);
console.log(i, r.status, process.memoryUsage());
}
} catch (err) {
console.error('%s error: %s', i, err.message);
}
}
}

main().then(() => {
console.log('main end');
}).catch(err => {
console.error('main error throw: %s', err);
console.error(err);
process.exit(1);
});

// process.on('uncaughtException', (...args) => {
// console.error('uncaughtException', args);
// process.exit(1);
// });

// process.on('unhandledRejection', (...args) => {
// console.error('unhandledRejection', args);
// process.exit(2);
// });

// process.on('uncaughtExceptionMonitor', (...args) => {
// console.error('uncaughtExceptionMonitor', args);
// process.exit(2);
// });

process.on('beforeExit', (...args) => {
console.error('beforeExit', args);
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"mime-types": "^2.1.35",
"qs": "^6.12.1",
"type-fest": "^4.20.1",
"undici": "^6.19.2",
"undici": "^7.0.0",
"ylru": "^2.0.0"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion src/FetchOpaqueInterceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export interface OpaqueInterceptorOptions {
export function fetchOpaqueInterceptor(opts: OpaqueInterceptorOptions) {
const opaqueLocalStorage = opts?.opaqueLocalStorage;
return (dispatch: Dispatcher['dispatch']): Dispatcher['dispatch'] => {
return function redirectInterceptor(opts: Dispatcher.DispatchOptions, handler: Dispatcher.DispatchHandlers) {
return function redirectInterceptor(opts: Dispatcher.DispatchOptions, handler: Dispatcher.DispatchHandler) {
const opaque = opaqueLocalStorage?.getStore();
(handler as any).opaque = opaque;
return dispatch(opts, handler);
Expand Down
2 changes: 1 addition & 1 deletion src/HttpAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export class HttpAgent extends Agent {
this.#checkAddress = options.checkAddress;
}

dispatch(options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandlers): boolean {
dispatch(options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandler): boolean {
if (this.#checkAddress && options.origin) {
const originUrl = typeof options.origin === 'string' ? new URL(options.origin) : options.origin;
let hostname = originUrl.hostname;
Expand Down
10 changes: 8 additions & 2 deletions src/HttpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type IUndiciRequestOption = PropertyShouldBe<UndiciRequestOption, 'headers', Inc

export const PROTO_RE = /^https?:\/\//i;

export interface UnidiciTimingInfo {
export interface UndiciTimingInfo {
startTime: number;
redirectStartTime: number;
redirectEndTime: number;
Expand All @@ -70,6 +70,9 @@ export interface UnidiciTimingInfo {
};
}

// keep typo compatibility
export interface UnidiciTimingInfo extends UndiciTimingInfo {}

function noop() {
// noop
}
Expand Down Expand Up @@ -701,7 +704,8 @@ export class HttpClient extends EventEmitter {

return clientResponse;
} catch (rawError: any) {
debug('Request#%d throw error: %s', requestId, rawError);
debug('Request#%d throw error: %s, socketErrorRetry: %s, socketErrorRetries: %s',
requestId, rawError, args.socketErrorRetry, requestContext.socketErrorRetries);
let err = rawError;
if (err.name === 'HeadersTimeoutError') {
err = new HttpClientRequestTimeoutError(headersTimeout, { cause: err });
Expand All @@ -713,6 +717,8 @@ export class HttpClient extends EventEmitter {
// auto retry on socket error, https://github.com/node-modules/urllib/issues/454
if (args.socketErrorRetry > 0 && requestContext.socketErrorRetries < args.socketErrorRetry) {
requestContext.socketErrorRetries++;
debug('Request#%d retry on socket error, socketErrorRetries: %d',
requestId, requestContext.socketErrorRetries);
return await this.#requestInternal(url, options, requestContext);
}
}
Expand Down
13 changes: 6 additions & 7 deletions src/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,20 @@ import {
Agent,
getGlobalDispatcher,
Pool,
Dispatcher,
} from 'undici';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import undiciSymbols from 'undici/lib/core/symbols.js';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import undiciFetchSymbols from 'undici/lib/web/fetch/symbols.js';
import { getResponseState } from 'undici/lib/web/fetch/response.js';
import {
channels,
ClientOptions,
PoolStat,
RequestDiagnosticsMessage,
ResponseDiagnosticsMessage,
UnidiciTimingInfo,
UndiciTimingInfo,
} from './HttpClient.js';
import {
HttpAgent,
Expand Down Expand Up @@ -51,7 +50,7 @@ export type FetchDiagnosticsMessage = {

export type FetchResponseDiagnosticsMessage = {
fetch: FetchMeta;
timingInfo?: UnidiciTimingInfo;
timingInfo?: UndiciTimingInfo;
response?: Response;
error?: Error;
};
Expand Down Expand Up @@ -236,8 +235,8 @@ export class FetchFactory {
throw e;
}

// get unidici internal response
const state = Reflect.get(res!, undiciFetchSymbols.kState) as Dispatcher.ResponseData;
// get undici internal response
const state = getResponseState(res!);
updateSocketInfo(socketInfo, internalOpaque /* , rawError */);

urllibResponse.headers = convertHeader(res!.headers);
Expand All @@ -250,7 +249,7 @@ export class FetchFactory {

channels.fetchResponse.publish({
fetch: fetchMeta,
timingInfo: (state as any).timingInfo,
timingInfo: state.timingInfo,
response: res!,
} as FetchResponseDiagnosticsMessage);
channels.response.publish({
Expand Down

0 comments on commit a8c5aaf

Please sign in to comment.