Fix SESSION_TIMEOUT_MS=0 and BROWSER_IDLE_TIMEOUT_MS=0 (0 = never)

Previously:
- BROWSER_IDLE_TIMEOUT_MS=0 scheduled setTimeout(..., 0), shutting the
  browser down immediately instead of 'never' (as documented in README).
- SESSION_TIMEOUT_MS=0 collapsed to the 600000 default via '|| 600000',
  so sessions still expired instead of never.

Now 0 explicitly means 'disabled / never' for both timeouts, while unset
env vars still fall back to their defaults. The session-cleanup and
idle-shutdown code paths guard against <= 0 so a disabled timeout is a
no-op.
This commit is contained in:
audinue
2026-09-09 00:14:49 -07:00
committed by Pradeep Elankumaran
parent 17fad1987d
commit cb68c6e633
3 changed files with 11 additions and 6 deletions
+1 -1
View File
@@ -642,7 +642,7 @@ Browser behavior can be tuned in `camofox.config.json`:
| `CAMOFOX_TRACES_TTL_HOURS` | Traces older than this are swept on startup | `24` |
| `MAX_SESSIONS` | Max concurrent browser sessions | `50` |
| `MAX_TABS_PER_SESSION` | Max tabs per session | `10` |
| `SESSION_TIMEOUT_MS` | Session inactivity timeout | `1800000` (30min) |
| `SESSION_TIMEOUT_MS` | Session inactivity timeout (0 = never) | `1800000` (30min) |
| `BROWSER_IDLE_TIMEOUT_MS` | Kill browser when idle (0 = never) | `300000` (5min) |
| `CAMOFOX_INTERACTIVE` | Interactive browser mode: `desktop` opens a real local Camoufox window; `off` keeps normal headless behavior | `off` |
| `HANDLER_TIMEOUT_MS` | Max time for any handler | `30000` (30s) |
+8 -3
View File
@@ -83,7 +83,12 @@ function normalizeInteractiveMode(value) {
function loadConfig({ configPath = CONFIG_PATH } = {}) {
const externalCamoufoxExecutable = camoufoxExecutablePath();
const browserIdleTimeoutMs = parseInt(process.env.BROWSER_IDLE_TIMEOUT_MS, 10);
const configuredSessionTimeoutMs = parseInt(process.env.SESSION_TIMEOUT_MS, 10);
// 0 disables session expiry (sessions never expire); unset falls back to the default.
const sessionTimeoutMs = Number.isNaN(configuredSessionTimeoutMs) ? 600000 : configuredSessionTimeoutMs;
const configuredBrowserIdleTimeoutMs = parseInt(process.env.BROWSER_IDLE_TIMEOUT_MS, 10);
// 0 disables idle browser shutdown; unset falls back to the default.
const browserIdleTimeoutMs = Number.isNaN(configuredBrowserIdleTimeoutMs) ? 300000 : configuredBrowserIdleTimeoutMs;
const fileConfig = readCamofoxConfig(configPath);
const configuredNewPageTimeoutMs = Number(fileConfig.newPageTimeoutMs);
const newPageTimeoutMs = Number.isFinite(configuredNewPageTimeoutMs) && configuredNewPageTimeoutMs > 0
@@ -111,14 +116,14 @@ function loadConfig({ configPath = CONFIG_PATH } = {}) {
handlerTimeoutMs: parseInt(process.env.HANDLER_TIMEOUT_MS) || 30000,
newPageTimeoutMs,
maxConcurrentPerUser: parseInt(process.env.MAX_CONCURRENT_PER_USER) || 3,
sessionTimeoutMs: parseInt(process.env.SESSION_TIMEOUT_MS) || 600000,
sessionTimeoutMs,
tabInactivityMs: parseInt(process.env.TAB_INACTIVITY_MS) || 300000,
maxSessions: parseInt(process.env.MAX_SESSIONS) || 50,
maxTabsPerSession: parseInt(process.env.MAX_TABS_PER_SESSION) || 10,
maxTabsGlobal: parseInt(process.env.MAX_TABS_GLOBAL) || 50,
navigateTimeoutMs: parseInt(process.env.NAVIGATE_TIMEOUT_MS) || 30000,
buildrefsTimeoutMs: parseInt(process.env.BUILDREFS_TIMEOUT_MS) || 12000,
browserIdleTimeoutMs: Number.isFinite(browserIdleTimeoutMs) ? browserIdleTimeoutMs : 300000,
browserIdleTimeoutMs,
nativeMemRestartThresholdMb: parseInt(process.env.NATIVE_MEM_RESTART_THRESHOLD_MB) || 300,
browserRssRestartThresholdMb: parseInt(process.env.BROWSER_RSS_RESTART_THRESHOLD_MB) || 1500,
camoufoxExecutablePath: externalCamoufoxExecutable,
+2 -2
View File
@@ -690,7 +690,7 @@ let _lastBrowserStopReason = null;
const INTENTIONAL_STOP_REASONS = new Set(['idle_shutdown', 'admin_stop']);
function scheduleBrowserIdleShutdown() {
if (browserIdleTimer || sessions.size > 0 || !browser) return;
if (browserIdleTimer || sessions.size > 0 || !browser || BROWSER_IDLE_TIMEOUT_MS <= 0) return;
browserIdleTimer = setTimeout(async () => {
browserIdleTimer = null;
if (sessions.size === 0 && browser) {
@@ -5896,7 +5896,7 @@ app.delete('/sessions/:userId', async (req, res) => {
setInterval(() => {
const now = Date.now();
for (const [userId, session] of Array.from(sessions.entries())) {
if (now - session.lastAccess > SESSION_TIMEOUT_MS) {
if (SESSION_TIMEOUT_MS > 0 && now - session.lastAccess > SESSION_TIMEOUT_MS) {
session._closing = true;
const idleMs = now - session.lastAccess;
sessionsExpiredTotal.inc();