mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 21:15:32 +09:00
fix(api): bound http server limits and shed on overload (#2170)
This commit is contained in:
1 parent
a2d7f5e8cc
commit
b26748a2a7
11 files changed
+230
-3
No files matched your search
@@ -4,10 +4,17 @@ import {applyFluxerVersionHeader} from '@fluxer/hono/src/middleware/VersionHeade
|
||||
import {type Http2Bindings, type HttpBindings, type ServerType, serve} from '@hono/node-server';
|
||||
import type {Env, Hono} from 'hono';
|
||||
|
||||
const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
|
||||
const DEFAULT_KEEP_ALIVE_TIMEOUT_MS = 125_000;
|
||||
const DEFAULT_MAX_REQUESTS_PER_SOCKET = 1_000;
|
||||
|
||||
interface ServerOptions {
|
||||
port: number;
|
||||
hostname?: string;
|
||||
onListen?: (info: {address: string; port: number}) => void;
|
||||
requestTimeoutMs?: number;
|
||||
keepAliveTimeoutMs?: number;
|
||||
maxRequestsPerSocket?: number;
|
||||
}
|
||||
|
||||
type NodeFetchCallback = (request: Request, env: HttpBindings | Http2Bindings) => Promise<unknown> | unknown;
|
||||
@@ -17,15 +24,30 @@ function createVersionedFetch<E extends Env>(app: Hono<E>): NodeFetchCallback {
|
||||
}
|
||||
|
||||
export function createServer<E extends Env = Env>(app: Hono<E>, options: ServerOptions): ServerType {
|
||||
const {port, hostname, onListen} = options;
|
||||
return serve(
|
||||
const {
|
||||
port,
|
||||
hostname,
|
||||
onListen,
|
||||
requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS,
|
||||
keepAliveTimeoutMs = DEFAULT_KEEP_ALIVE_TIMEOUT_MS,
|
||||
maxRequestsPerSocket = DEFAULT_MAX_REQUESTS_PER_SOCKET,
|
||||
} = options;
|
||||
const server = serve(
|
||||
{
|
||||
fetch: createVersionedFetch(app),
|
||||
port,
|
||||
...(hostname !== undefined && {hostname}),
|
||||
serverOptions: {
|
||||
keepAliveTimeout: keepAliveTimeoutMs,
|
||||
requestTimeout: requestTimeoutMs,
|
||||
},
|
||||
},
|
||||
onListen,
|
||||
);
|
||||
if ('maxRequestsPerSocket' in server) {
|
||||
server.maxRequestsPerSocket = maxRequestsPerSocket;
|
||||
}
|
||||
return server;
|
||||
}
|
||||
|
||||
type CleanupFunction = () => void | Promise<void>;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {Server} from 'node:http';
|
||||
import {createServer} from '@fluxer/hono/src/Server';
|
||||
import type {ServerType} from '@hono/node-server';
|
||||
import {Hono} from 'hono';
|
||||
import {afterEach, describe, expect, test} from 'vitest';
|
||||
|
||||
function closeServer(server: ServerType): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
server.close((error?: Error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function listen(options: Parameters<typeof createServer>[1]): Promise<{server: ServerType; port: number}> {
|
||||
return new Promise((resolve) => {
|
||||
const app = new Hono();
|
||||
app.get('/', (ctx) => ctx.text('OK'));
|
||||
const server = createServer(app, {
|
||||
...options,
|
||||
onListen: ({port}) => resolve({server, port}),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('Server limits', () => {
|
||||
let server: ServerType | null = null;
|
||||
afterEach(async () => {
|
||||
if (server) {
|
||||
await closeServer(server);
|
||||
server = null;
|
||||
}
|
||||
});
|
||||
|
||||
test('applies bounded defaults instead of stock node timeouts', async () => {
|
||||
const listening = await listen({port: 0});
|
||||
server = listening.server;
|
||||
const httpServer = server as Server;
|
||||
expect(httpServer.requestTimeout).toBe(30_000);
|
||||
expect(httpServer.keepAliveTimeout).toBe(125_000);
|
||||
expect(httpServer.maxRequestsPerSocket).toBe(1_000);
|
||||
expect(httpServer.headersTimeout).toBeLessThanOrEqual(httpServer.requestTimeout);
|
||||
});
|
||||
|
||||
test('honours explicit limit overrides', async () => {
|
||||
const listening = await listen({
|
||||
port: 0,
|
||||
requestTimeoutMs: 15_000,
|
||||
keepAliveTimeoutMs: 61_000,
|
||||
maxRequestsPerSocket: 250,
|
||||
});
|
||||
server = listening.server;
|
||||
const httpServer = server as Server;
|
||||
expect(httpServer.requestTimeout).toBe(15_000);
|
||||
expect(httpServer.keepAliveTimeout).toBe(61_000);
|
||||
expect(httpServer.maxRequestsPerSocket).toBe(250);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user