fix(stockbit): end a browser sign-in on a CDN block

A block (UPSTREAMBLOCKED) was treated as a passing failure, so idx's browser
re-checked the same cookie every 5 s until the 15-minute timeout, warning
"does not retry a block ... retrying". A block holds for every session from
this network: the wait now ends with it at once.

Claude-Session: https://claude.ai/code/session_01RqPPVeVDrnLoP7NUZmLRxY
This commit is contained in:
Rasyidan Akbar F. 2026-11-04 20:37:54 +07:00
commit 9590219977
2 changed files with 41 additions and 5 deletions

View file

@ -271,6 +271,12 @@ fn wait_for_session(
));
retry_at = Some(Instant::now() + VERIFY_RETRY);
}
// A block holds for every session from this network, so
// the wait ends with it rather than at the timeout.
Err(err @ IdxError::UpstreamBlocked(_)) => {
devtools.close();
return Err(err);
}
Err(err) => {
crate::runtime::warn(format!(
"Stockbit refused the session in idx's browser ({err}); sign in \
@ -1041,6 +1047,35 @@ mod tests {
let _ = std::fs::remove_dir_all(&profile);
}
/// A CDN block is never retried: the wait ends with it at once, not at
/// the timeout.
#[test]
fn a_blocked_check_ends_the_wait() {
let profile = temp_dir("blocked");
let browser = FakeBrowser::start(Fake::default());
write_endpoint(&profile, browser.port);
let mut devtools = connect(&profile, &|| true).unwrap();
let checks = std::cell::Cell::new(0);
let started = Instant::now();
let err = wait_for_session(
&mut devtools,
Duration::from_secs(30),
&|_| {
checks.set(checks.get() + 1);
Err(IdxError::UpstreamBlocked(
"HTTP 403 from its CDN".to_string(),
))
},
&|| true,
)
.unwrap_err();
assert!(matches!(err, IdxError::UpstreamBlocked(_)), "{err}");
assert_eq!(checks.get(), 1);
assert!(started.elapsed() < Duration::from_secs(5));
browser.finish();
let _ = std::fs::remove_dir_all(&profile);
}
/// Closing the window ends the wait at once, not at the timeout.
#[test]
fn closing_the_window_ends_the_wait() {

View file

@ -295,12 +295,10 @@ fn keep_after_failed_check(err: &IdxError, rotated: bool) -> bool {
}
/// Whether a session check failed for a reason that says nothing about the
/// session itself, so the same session may be checked again later.
/// session itself, so the same session may be checked again later. A CDN
/// block (`UpstreamBlocked`) is not: idx never retries a block.
pub(crate) fn check_is_transient(err: &IdxError) -> bool {
matches!(
err,
IdxError::Http(_) | IdxError::RateLimited | IdxError::UpstreamBlocked(_)
)
matches!(err, IdxError::Http(_) | IdxError::RateLimited)
}
fn restore(store: &SessionStore, previous: Option<Vec<u8>>) {
@ -343,6 +341,9 @@ mod tests {
assert!(check_is_transient(&IdxError::RateLimited));
assert!(check_is_transient(&network));
assert!(!check_is_transient(&refused));
assert!(!check_is_transient(&IdxError::UpstreamBlocked(
"403".to_string()
)));
assert!(!check_is_transient(&IdxError::InvalidInput(
"x".to_string()
)));