Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion config.default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ rate_limit_rps = 10 # Max requests/second (global). 0 = unlimited.
mode = "auto" # auto | lightpanda | playwright | chrome | camoufox | none
page_timeout_ms = 30000
pool_size = 4
# camoufox_timeout_ms = 60000 # camoufox per-request REST budget override
# camoufox_challenge_wait_ms = 20000
# # ceiling for polling out a Cloudflare-style JS
# # challenge on the camoufox tier (0 disables)
# render_js_default = true # alias: force_js = true
# Forces JS rendering when a request omits `renderJs`.
# Per-request `renderJs` always wins over this default.
Expand Down Expand Up @@ -65,7 +69,10 @@ ws_url = "ws://127.0.0.1:9222/"
# base_url = "http://127.0.0.1:9377"
# api_key = "" # optional bearer token if the sidecar is protected
# include_in_auto = false # default: stay out of the auto ladder
# camoufox_timeout_ms = 60000 # optional per-request REST budget override
#
# The two camoufox budgets (camoufox_timeout_ms, camoufox_challenge_wait_ms)
# are top-level [renderer] keys, shown in that section above. Placed under
# [renderer.camoufox] they are silently ignored.

[crawler]
max_concurrency = 10
Expand Down
45 changes: 45 additions & 0 deletions crates/crw-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,15 @@ pub const MAX_WAIT_FOR_MS: u64 = 60_000;
/// and must never be charged CDP overhead.
pub const CAMOUFOX_DEFAULT_TIMEOUT_MS: u64 = 60_000;

/// Default ceiling (ms) for polling a Camoufox tab while a Cloudflare-style JS
/// challenge clears itself, before giving up and reporting the wall. The CDP
/// tiers already run this loop ([`crw-renderer`'s `CHALLENGE_MAX_RETRIES`] ×
/// `CHALLENGE_POLL_INTERVAL_MS` = 9s); Camoufox is a slower engine reached
/// after the CDP tiers have already failed, so it gets a wider ceiling. 20s
/// covers the commonly observed 5-25s clear without eating most of a 60s
/// request budget. Used by [`RendererConfig::camoufox_challenge_wait`].
pub const CAMOUFOX_DEFAULT_CHALLENGE_WAIT_MS: u64 = 20_000;

/// Default cloak (Turnstile-solver sidecar) per-request budget (ms) — the
/// per-attempt solve budget bounding one sidecar mirror call. A cold interactive
/// Turnstile solve is ~21s; 35s leaves margin for the browser solve + curl_cffi
Expand Down Expand Up @@ -803,6 +812,14 @@ pub struct RendererConfig {
/// [`CAMOUFOX_DEFAULT_TIMEOUT_MS`] when unset.
#[serde(default)]
pub camoufox_timeout_ms: Option<u64>,
/// Ceiling (ms) for polling a Camoufox tab while a bot-challenge
/// interstitial clears, before giving up and reporting the wall. Falls back
/// to [`CAMOUFOX_DEFAULT_CHALLENGE_WAIT_MS`] when unset; `Some(0)` disables
/// the poll and restores the single-shot behaviour. The Camoufox analogue
/// of `chrome_challenge_max_retries`.
/// Env: `CRW_RENDERER__CAMOUFOX_CHALLENGE_WAIT_MS`.
#[serde(default)]
pub camoufox_challenge_wait_ms: Option<u64>,
/// Opt-in cloak Turnstile-solver sidecar endpoint. See [`CloakEndpoint`].
/// `None` = not configured (default) → the tier is never constructed and the
/// engine is byte-identical to a build without it. Fired only as a
Expand Down Expand Up @@ -1080,6 +1097,7 @@ impl Default for RendererConfig {
chrome_proxy_timeout_ms: None,
camoufox: None,
camoufox_timeout_ms: None,
camoufox_challenge_wait_ms: None,
cloak: None,
cloak_timeout_ms: None,
cloak_proxy_host: None,
Expand Down Expand Up @@ -1133,6 +1151,12 @@ impl RendererConfig {
self.camoufox_timeout_ms
.unwrap_or(CAMOUFOX_DEFAULT_TIMEOUT_MS)
}
/// Camoufox bot-challenge poll ceiling (ms). Unconditional (no `#[cfg]`),
/// like [`Self::camoufox_timeout`].
pub fn camoufox_challenge_wait(&self) -> u64 {
self.camoufox_challenge_wait_ms
.unwrap_or(CAMOUFOX_DEFAULT_CHALLENGE_WAIT_MS)
}
/// Per-attempt cloak solve budget (ms). Unconditional (no `#[cfg]`) so
/// `tier_timeouts_from` can reference it in every build, like
/// [`Self::camoufox_timeout`].
Expand Down Expand Up @@ -3171,6 +3195,7 @@ search_backend_url = "http://from-file:8080"
assert_eq!(r.chrome_proxy_timeout_ms, None);
assert!(r.camoufox.is_none());
assert_eq!(r.camoufox_timeout_ms, None);
assert_eq!(r.camoufox_challenge_wait_ms, None);
assert!(r.cloak.is_none());
assert_eq!(r.cloak_timeout_ms, None);
assert_eq!(r.cloak_proxy_host, None);
Expand Down Expand Up @@ -3355,6 +3380,26 @@ search_backend_url = "http://from-file:8080"
assert_eq!(r2.camoufox_timeout(), 1_234);
}

#[test]
fn camoufox_challenge_wait_default_and_override() {
let r = RendererConfig::default();
assert_eq!(
r.camoufox_challenge_wait(),
CAMOUFOX_DEFAULT_CHALLENGE_WAIT_MS
);
let r2 = RendererConfig {
camoufox_challenge_wait_ms: Some(4_321),
..Default::default()
};
assert_eq!(r2.camoufox_challenge_wait(), 4_321);
// Explicit 0 must disable the poll, not fall back to the default.
let r3 = RendererConfig {
camoufox_challenge_wait_ms: Some(0),
..Default::default()
};
assert_eq!(r3.camoufox_challenge_wait(), 0);
}

#[test]
fn cloak_timeout_default_and_override() {
let r = RendererConfig::default();
Expand Down
Loading
Loading