mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 21:15:32 +09:00
fix(api): make the http header and request timeouts tunable (#2259)
This commit is contained in:
1 parent
995f5118b2
commit
45f694310a
10 files changed
+128
-2
No files matched your search
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {createAPIApp} from '@app/api/App';
|
||||
import {initializeConfig} from '@app/api/Config';
|
||||
import {buildAPIServerOptions, initializeConfig} from '@app/api/Config';
|
||||
import {initializeLogger} from '@app/api/Logger';
|
||||
import {Config} from '@app/Config';
|
||||
import {shutdownInstrumentation} from '@app/Instrument';
|
||||
@@ -34,7 +34,7 @@ async function main(): Promise<void> {
|
||||
process.on('unhandledRejection', (reason) => {
|
||||
Logger.error({reason}, 'Unhandled rejection (suppressed)');
|
||||
});
|
||||
const server = createServer(app, {port: Config.port});
|
||||
const server = createServer(app, buildAPIServerOptions(Config));
|
||||
Logger.info({port: Config.port}, `Starting Fluxer API on port ${Config.port}`);
|
||||
setupGracefulShutdown(
|
||||
async () => {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {loadConfig, resetConfig} from '@fluxer/config/src/ConfigLoader';
|
||||
import {createServer} from '@fluxer/hono/src/Server';
|
||||
import {Hono} from 'hono';
|
||||
import {afterAll, afterEach, describe, expect, test, vi} from 'vitest';
|
||||
import {buildAPIConfigFromMaster, buildAPIServerOptions} from './Config';
|
||||
|
||||
interface ListeningServer {
|
||||
close: (callback: () => void) => void;
|
||||
headersTimeout: number;
|
||||
requestTimeout: number;
|
||||
}
|
||||
|
||||
const servers: Array<ListeningServer> = [];
|
||||
|
||||
async function listenWithEnv(env: Record<string, string> = {}): Promise<ListeningServer> {
|
||||
for (const [key, value] of Object.entries({FLUXER_API_PORT: '0', ...env})) {
|
||||
vi.stubEnv(key, value);
|
||||
}
|
||||
resetConfig();
|
||||
const config = buildAPIConfigFromMaster(await loadConfig());
|
||||
const server = createServer(new Hono(), buildAPIServerOptions(config)) as unknown as ListeningServer;
|
||||
servers.push(server);
|
||||
return server;
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(servers.splice(0).map((server) => new Promise<void>((resolve) => server.close(() => resolve()))));
|
||||
vi.unstubAllEnvs();
|
||||
resetConfig();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await loadConfig();
|
||||
});
|
||||
|
||||
describe('buildAPIServerOptions', () => {
|
||||
test('starts the api on the shipped header and request timeouts', async () => {
|
||||
const server = await listenWithEnv();
|
||||
expect(server.headersTimeout).toBe(30_000);
|
||||
expect(server.requestTimeout).toBe(120_000);
|
||||
});
|
||||
|
||||
test('carries the operator header timeout from the environment into the server', async () => {
|
||||
const server = await listenWithEnv({FLUXER_API_HEADERS_TIMEOUT_MS: '45000'});
|
||||
expect(server.headersTimeout).toBe(45_000);
|
||||
expect(server.requestTimeout).toBe(120_000);
|
||||
});
|
||||
|
||||
test('carries the operator request timeout from the environment into the server', async () => {
|
||||
const server = await listenWithEnv({FLUXER_API_REQUEST_TIMEOUT_MS: '600000'});
|
||||
expect(server.headersTimeout).toBe(30_000);
|
||||
expect(server.requestTimeout).toBe(600_000);
|
||||
});
|
||||
|
||||
test('clamps a header timeout set above the request timeout', async () => {
|
||||
const server = await listenWithEnv({
|
||||
FLUXER_API_HEADERS_TIMEOUT_MS: '90000',
|
||||
FLUXER_API_REQUEST_TIMEOUT_MS: '45000',
|
||||
});
|
||||
expect(server.requestTimeout).toBe(45_000);
|
||||
expect(server.headersTimeout).toBe(45_000);
|
||||
});
|
||||
});
|
||||
@@ -160,6 +160,8 @@ export function buildAPIConfigFromMaster(master: MasterConfig): APIConfig {
|
||||
return {
|
||||
nodeEnv: master.env === 'test' ? 'development' : master.env,
|
||||
port: master.services.api.port,
|
||||
headersTimeoutMs: master.services.api.headers_timeout_ms,
|
||||
requestTimeoutMs: master.services.api.request_timeout_ms,
|
||||
maxInflightRequests: master.services.api.max_inflight_requests,
|
||||
ipBanExemptIps: normalizeIpBanExemptIps(master.services.api.ip_ban_exempt_ips),
|
||||
desktopGitHubRedirectCountries: normalizeCountryCodes(
|
||||
@@ -530,6 +532,20 @@ export function buildAPIConfigFromMaster(master: MasterConfig): APIConfig {
|
||||
};
|
||||
}
|
||||
|
||||
interface APIServerOptions {
|
||||
port: number;
|
||||
headersTimeoutMs: number;
|
||||
requestTimeoutMs: number;
|
||||
}
|
||||
|
||||
export function buildAPIServerOptions(config: APIConfig): APIServerOptions {
|
||||
return {
|
||||
port: config.port,
|
||||
headersTimeoutMs: config.headersTimeoutMs,
|
||||
requestTimeoutMs: config.requestTimeoutMs,
|
||||
};
|
||||
}
|
||||
|
||||
let _config: APIConfig | null = null;
|
||||
|
||||
export function initializeConfig(config: APIConfig): void {
|
||||
|
||||
@@ -34,6 +34,8 @@ export type APIGeoipConfig = APIGeoipFilesystemConfig | APIGeoipS3Config;
|
||||
export interface APIConfig {
|
||||
nodeEnv: 'development' | 'production';
|
||||
port: number;
|
||||
headersTimeoutMs: number;
|
||||
requestTimeoutMs: number;
|
||||
maxInflightRequests: number;
|
||||
ipBanExemptIps: Array<string>;
|
||||
desktopGitHubRedirectCountries: ReadonlySet<string>;
|
||||
|
||||
Reference in new issue
Block a user