Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libs/backend-api7/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export class BackendAPI7 implements ADCSDK.Backend {
...(opts.timeout ? { timeout: opts.timeout } : {}),
});
this.gatewayGroupName = opts.gatewayGroup!;

ADCSDK.utils.registerTimeoutInterceptor(this.client);
}

public metadata() {
Expand Down
9 changes: 5 additions & 4 deletions libs/backend-api7/src/operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,7 @@ export class Operator extends ADCSDK.backend.BackendEventSource {
return throwError(
() =>
new Error(
error.response?.data?.error_msg ??
JSON.stringify(error.response?.data),
ADCSDK.utils.formatAxiosErrorMessage(error),
),
);
return throwError(() => error);
Expand All @@ -107,8 +106,10 @@ export class Operator extends ADCSDK.backend.BackendEventSource {
error,
...(axios.isAxiosError(error) && {
axiosResponse: error.response,
...(error.response?.data?.error_msg && {
error: new Error(error.response.data.error_msg),
...(error.response && {
error: new Error(
ADCSDK.utils.formatAxiosErrorMessage(error),
),
}),
}),
} satisfies ADCSDK.BackendSyncResult);
Expand Down
117 changes: 117 additions & 0 deletions libs/backend-api7/test/timeout.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import * as ADCSDK from '@api7/adc-sdk';
import { Agent as httpAgent } from 'http';
import { Agent as httpsAgent } from 'https';
import { createServer } from 'node:http';
import { lastValueFrom, toArray } from 'rxjs';
import { AddressInfo } from 'node:net';

import { BackendAPI7 } from '../src';

describe('BackendAPI7 timeout error message', () => {
// Create a local HTTP server that delays responses to trigger timeouts
let server: ReturnType<typeof createServer>;
let serverUrl: string;

beforeAll(async () => {
server = createServer((_, res) => {
// Delay response by 5 seconds to guarantee timeout
setTimeout(() => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ value: {} }));
}, 5000);
});
await new Promise<void>((resolve) => {
server.listen(0, '127.0.0.1', () => resolve());
});
const addr = server.address() as AddressInfo;
serverUrl = `http://127.0.0.1:${addr.port}`;
});

afterAll(async () => {
await new Promise<void>((resolve) => server.close(() => resolve()));
});

const createBackend = (timeout: number) =>
new BackendAPI7({
server: serverUrl,
token: 'test-token',
gatewayGroup: 'default',
timeout,
cacheKey: 'test',
httpAgent: new httpAgent({ keepAlive: false }),
httpsAgent: new httpsAgent({ keepAlive: false }),
});

it('ping timeout should include request URL and timeout hint', async () => {
const backend = createBackend(10);
try {
await backend.ping();
throw new Error('Expected timeout error');
} catch (err) {
const error = err as Error;
expect(error.message).toContain(
`${serverUrl}/api/gateway_groups`,
);
expect(error.message).toContain('timed out after 10ms');
expect(error.message).toContain('--timeout');
}
});

it('version timeout should include request URL and timeout hint', async () => {
const backend = createBackend(10);
try {
await backend.version();
throw new Error('Expected timeout error');
} catch (err) {
const error = err as Error;
expect(error.message).toContain(`${serverUrl}/api/version`);
expect(error.message).toContain('timed out after 10ms');
expect(error.message).toContain('--timeout');
}
});

it('dump timeout should include request URL and timeout hint', async () => {
const backend = createBackend(10);
try {
await lastValueFrom(backend.dump().pipe(toArray()));
throw new Error('Expected timeout error');
} catch (err) {
const error = err as Error;
expect(error.message).toContain(`${serverUrl}/api/`);
expect(error.message).toContain('timed out after 10ms');
expect(error.message).toContain('--timeout');
}
});

it('sync timeout should include request URL and timeout hint', async () => {
const backend = createBackend(10);
try {
await lastValueFrom(
backend
.sync(
[
{
type: ADCSDK.EventType.CREATE,
resourceType: ADCSDK.ResourceType.CONSUMER,
resourceId: 'test-consumer',
resourceName: 'test-consumer',
newValue: {
username: 'test',
plugins: {},
},
},
],
{ exitOnFailure: true },
)
.pipe(toArray()),
);
throw new Error('Expected timeout error');
} catch (err) {
const error = err as Error;
// sync first calls version() and getGatewayGroupId(), which will timeout
expect(error.message).toContain(`${serverUrl}/api/`);
expect(error.message).toContain('timed out after 10ms');
expect(error.message).toContain('--timeout');
}
});
});
2 changes: 2 additions & 0 deletions libs/backend-apisix-standalone/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export class BackendAPISIXStandalone implements ADCSDK.Backend {
httpsAgent: opts.httpsAgent,
...(opts.timeout ? { timeout: opts.timeout } : {}),
});

ADCSDK.utils.registerTimeoutInterceptor(this.client);
}

public metadata() {
Expand Down
9 changes: 5 additions & 4 deletions libs/backend-apisix-standalone/src/operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,7 @@ export class Operator extends ADCSDK.backend.BackendEventSource {
return throwError(
() =>
new Error(
error.response?.data?.error_msg ??
JSON.stringify(error.response?.data),
ADCSDK.utils.formatAxiosErrorMessage(error),
),
);
return throwError(() => error);
Expand All @@ -165,8 +164,10 @@ export class Operator extends ADCSDK.backend.BackendEventSource {
error,
...(axios.isAxiosError(error) && {
axiosResponse: error.response,
...(error.response?.data?.error_msg && {
error: new Error(error.response.data.error_msg),
...(error.response && {
error: new Error(
ADCSDK.utils.formatAxiosErrorMessage(error),
),
}),
}),
server,
Expand Down
2 changes: 2 additions & 0 deletions libs/backend-apisix/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export class BackendAPISIX implements ADCSDK.Backend {
httpsAgent: opts.httpsAgent,
...(opts.timeout ? { timeout: opts.timeout } : {}),
});

ADCSDK.utils.registerTimeoutInterceptor(this.client);
}

public metadata() {
Expand Down
9 changes: 5 additions & 4 deletions libs/backend-apisix/src/operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,7 @@ export class Operator extends ADCSDK.backend.BackendEventSource {
return throwError(
() =>
new Error(
error.response?.data?.error_msg ??
JSON.stringify(error.response?.data),
ADCSDK.utils.formatAxiosErrorMessage(error),
),
);
return throwError(() => error);
Expand All @@ -174,8 +173,10 @@ export class Operator extends ADCSDK.backend.BackendEventSource {
error,
...(axios.isAxiosError(error) && {
axiosResponse: error.response,
...(error.response?.data?.error_msg && {
error: new Error(error.response.data.error_msg),
...(error.response && {
error: new Error(
ADCSDK.utils.formatAxiosErrorMessage(error),
),
}),
}),
} satisfies ADCSDK.BackendSyncResult);
Expand Down
Loading
Loading