From aea4a1a35c031f72971afb2794083cb3ee450e82 Mon Sep 17 00:00:00 2001 From: Lucas Winther Date: Thu, 15 Oct 2026 00:43:47 +0200 Subject: [PATCH] fix(server): keep connections open 240 s; Bun's 10 s default cut slow Wayback lookups off Co-Authored-By: Claude Opus 5.5 --- src/server/app.ts | 2 +- src/server/bun.ts | 4 ++-- src/server/config.ts | 5 +++++ test/server-config.test.ts | 9 +++++++++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/server/app.ts b/src/server/app.ts index 1f571a3..06242ad 100644 --- a/src/server/app.ts +++ b/src/server/app.ts @@ -21,7 +21,7 @@ export interface ServerEnv { } export const MAX_REDIRECTS = 5; -const UPSTREAM_TIMEOUT_MS = 30_000; +export const UPSTREAM_TIMEOUT_MS = 30_000; /** Pages whose scripts may call our API besides our own (the userscript runs on mangabaka.org). */ const EXTRA_ORIGINS = new Set(["https://mangabaka.org"]); diff --git a/src/server/bun.ts b/src/server/bun.ts index faf2882..e4badce 100644 --- a/src/server/bun.ts +++ b/src/server/bun.ts @@ -1,7 +1,7 @@ // Self-hosted entry: Bun.serve behind our reverse proxy (spec §4.10). Keep it thin. import { join, resolve } from "node:path"; import { createApp } from "./app"; -import { clientIp, readConfig } from "./config"; +import { SERVER_IDLE_TIMEOUT_S, clientIp, readConfig } from "./config"; import { DirStorage } from "./storage-dir"; const config = readConfig(process.env); @@ -31,7 +31,7 @@ const app = createApp({ clientIp: (req) => clientIp(req, config.trustProxy, server.requestIP(req)?.address), }); -server = Bun.serve({ port: config.port, hostname: config.hostname, fetch: app }); +server = Bun.serve({ port: config.port, hostname: config.hostname, idleTimeout: SERVER_IDLE_TIMEOUT_S, fetch: app }); console.log( `mangabaka-seeder on http://${server.hostname}:${server.port} · storage: ${ storage ? `${config.storageDir} (${storage.usedBytes()} of ${config.storageMaxBytes} bytes)` : "off" diff --git a/src/server/config.ts b/src/server/config.ts index 37abe5b..496e5e9 100644 --- a/src/server/config.ts +++ b/src/server/config.ts @@ -15,6 +15,11 @@ export class ConfigError extends Error { } export const DEFAULT_STORAGE_MAX_BYTES = 1024 ** 3; +/** + * Seconds the Bun server keeps a quiet connection open. Its default (10 s) dropped slow Wayback lookups + * mid-request; this must outlast a proxied fetch plus a catbox upload. Bun's maximum is 255. + */ +export const SERVER_IDLE_TIMEOUT_S = 240; export function readConfig(env: Record): ServerConfig { const publicUrl = parsePublicUrl(env.MBSEED_PUBLIC_URL); diff --git a/test/server-config.test.ts b/test/server-config.test.ts index 74ee6c6..ca5a9c0 100644 --- a/test/server-config.test.ts +++ b/test/server-config.test.ts @@ -67,3 +67,12 @@ describe("resolveAssetPath", () => { expect(resolveAssetPath(path)).toBeNull(); }); }); + +test("the server keeps a connection open longer than its own slowest upstream work", async () => { + // Bun.serve's default idleTimeout (10 s) cut slow Wayback lookups off mid-request ("Failed to fetch"). + const { SERVER_IDLE_TIMEOUT_S } = await import("../src/server/config"); + const { UPSTREAM_TIMEOUT_MS } = await import("../src/server/app"); + const { UPLOAD_TIMEOUT_MS } = await import("../src/core/hosting/host"); + expect(SERVER_IDLE_TIMEOUT_S * 1000).toBeGreaterThan(UPSTREAM_TIMEOUT_MS + UPLOAD_TIMEOUT_MS); + expect(SERVER_IDLE_TIMEOUT_S).toBeLessThanOrEqual(255); // Bun's maximum +});