fix: honor configured navigation timeout

This commit is contained in:
Pradeep Elankumaran
2026-09-08 22:36:04 -07:00
parent d84a670c46
commit 9120ee49d5
4 changed files with 34 additions and 7 deletions
+2 -1
View File
@@ -116,7 +116,7 @@ function loadConfig({ configPath = CONFIG_PATH } = {}) {
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) || 25000,
navigateTimeoutMs: parseInt(process.env.NAVIGATE_TIMEOUT_MS) || 30000,
buildrefsTimeoutMs: parseInt(process.env.BUILDREFS_TIMEOUT_MS) || 12000,
browserIdleTimeoutMs: Number.isFinite(browserIdleTimeoutMs) ? browserIdleTimeoutMs : 300000,
nativeMemRestartThresholdMb: parseInt(process.env.NATIVE_MEM_RESTART_THRESHOLD_MB) || 300,
@@ -162,6 +162,7 @@ function loadConfig({ configPath = CONFIG_PATH } = {}) {
CAMOFOX_TRACES_TTL_HOURS: process.env.CAMOFOX_TRACES_TTL_HOURS,
CAMOFOX_DISABLE_DEFAULT_ADDONS: process.env.CAMOFOX_DISABLE_DEFAULT_ADDONS,
CAMOFOX_INTERACTIVE: process.env.CAMOFOX_INTERACTIVE,
NAVIGATE_TIMEOUT_MS: process.env.NAVIGATE_TIMEOUT_MS,
CAMOUFOX_EXECUTABLE: process.env.CAMOUFOX_EXECUTABLE,
CAMOUFOX_EXECUTABLE_PATH: process.env.CAMOUFOX_EXECUTABLE_PATH,
CAMOFOX_EXECUTABLE_PATH: process.env.CAMOFOX_EXECUTABLE_PATH,
+10 -6
View File
@@ -598,6 +598,10 @@ function requestTimeoutMs(baseMs = HANDLER_TIMEOUT_MS) {
return proxyPool?.canRotateSessions ? Math.max(baseMs, 180000) : baseMs;
}
function navigationRequestTimeoutMs() {
return Math.max(requestTimeoutMs(), NAVIGATE_TIMEOUT_MS + 5000);
}
const userConcurrency = new Map();
async function withUserLimit(userId, operation) {
@@ -1958,10 +1962,10 @@ async function rotateGoogleTab(userId, sessionKey, tabId, previousTabState, reas
proxySession: session.proxySessionId || null,
});
await withPageLoadDuration('navigate', () => navigatePage(page, 'https://www.google.com/'));
await withPageLoadDuration('navigate', () => navigatePage(page, 'https://www.google.com/', { timeout: NAVIGATE_TIMEOUT_MS }));
tabState.visitedUrls.add('https://www.google.com/');
await page.waitForTimeout(1200);
await withPageLoadDuration('navigate', () => navigatePage(page, tabState.lastRequestedUrl));
await withPageLoadDuration('navigate', () => navigatePage(page, tabState.lastRequestedUrl, { timeout: NAVIGATE_TIMEOUT_MS }));
tabState.visitedUrls.add(tabState.lastRequestedUrl);
return { session, tabState };
}
@@ -3044,7 +3048,7 @@ app.post('/tabs/:tabId/navigate', async (req, res) => {
const navigateCurrentPage = async () => {
tabState.lastRequestedUrl = targetUrl;
const ac = tabState.navigateAbort = new AbortController();
const gotoP = withPageLoadDuration('navigate', () => navigatePage(tabState.page, targetUrl));
const gotoP = withPageLoadDuration('navigate', () => navigatePage(tabState.page, targetUrl, { timeout: NAVIGATE_TIMEOUT_MS }));
try {
const response = await Promise.race([
gotoP,
@@ -3114,7 +3118,7 @@ app.post('/tabs/:tabId/navigate', async (req, res) => {
const prewarm = await createLeasedPage(session);
const prewarmPage = prewarm.page;
try {
await withPageLoadDuration('navigate', () => navigatePage(prewarmPage, 'https://www.google.com/'));
await withPageLoadDuration('navigate', () => navigatePage(prewarmPage, 'https://www.google.com/', { timeout: NAVIGATE_TIMEOUT_MS }));
tabState.visitedUrls.add('https://www.google.com/');
await prewarmPage.waitForTimeout(1200);
} finally {
@@ -3284,8 +3288,8 @@ app.post('/tabs/:tabId/navigate', async (req, res) => {
refsAvailable: tabState.refs.size > 0,
...searchFallback,
};
}, requestTimeoutMs());
})(), requestTimeoutMs(), 'navigate'));
}, navigationRequestTimeoutMs());
})(), navigationRequestTimeoutMs(), 'navigate'));
log('info', 'navigated', { reqId: req.reqId, tabId, url: result.url });
pluginEvents.emit('tab:navigated', { userId: req.body.userId, tabId, url: result.url, prevUrl: null });
+10
View File
@@ -69,6 +69,16 @@ describe('loadConfig', () => {
expect(loadConfig().browserRssRestartThresholdMb).toBe(2048);
});
test('configures and forwards navigation timeout', () => {
delete process.env.NAVIGATE_TIMEOUT_MS;
expect(loadConfig().navigateTimeoutMs).toBe(30000);
process.env.NAVIGATE_TIMEOUT_MS = '60000';
const config = loadConfig();
expect(config.navigateTimeoutMs).toBe(60000);
expect(config.serverEnv.NAVIGATE_TIMEOUT_MS).toBe('60000');
});
test('reads newPageTimeoutMs from camofox.config.json with a 10s fallback', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'camofox-config-'));
const configPath = path.join(dir, 'camofox.config.json');
+12
View File
@@ -68,4 +68,16 @@ describe('launch compatibility source contract', () => {
expect(healthProbeOptions).toContain('viewport: null');
});
test('uses the configured navigation timeout without racing its request deadline', () => {
const navigateRoute = sourceBetween(
"app.post('/tabs/:tabId/navigate'",
'// Snapshot'
);
expect(serverSource).toContain('function navigationRequestTimeoutMs()');
expect(serverSource).toContain('NAVIGATE_TIMEOUT_MS + 5000');
expect(navigateRoute).toContain('timeout: NAVIGATE_TIMEOUT_MS');
expect(navigateRoute).toContain("})(), navigationRequestTimeoutMs(), 'navigate'))");
});
});