feat(gateway): harden failover and payload handling

Retry pre-response transport failures across candidates with an explicit stop policy, and propagate end-to-end timing into usage records and UI diagnostics.

Remove legacy body, import, cookie, PII, and tunnel replay caps while preserving optional operator-configured gateway limits.
This commit is contained in:
elky
2026-07-30 01:03:27 +08:00
parent a97acc07fc
commit a04673a90d
80 changed files with 3640 additions and 1236 deletions
@@ -35,7 +35,6 @@ pub const CLAUDE_CODE_OAUTH_SCOPES: &[&str] = &[
pub const CLAUDE_CODE_COOKIE_SCOPE: &str =
"user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";
const MAX_CLAUDE_SESSION_KEY_BYTES: usize = 16 * 1024;
const CLAUDE_CODE_BROWSER_USER_AGENT: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36";
#[derive(Debug, Clone)]
@@ -85,7 +84,6 @@ impl ClaudeCodeProviderOAuthAdapter {
fn session_cookie(session_key: &str) -> Result<String, OAuthError> {
let session_key = session_key.trim();
if session_key.is_empty()
|| session_key.len() > MAX_CLAUDE_SESSION_KEY_BYTES
|| session_key.contains(['\r', '\n', ';'])
|| http::HeaderValue::from_str(session_key).is_err()
{
@@ -669,6 +667,14 @@ mod tests {
assert!(token_body.get("code_verifier").is_some());
}
#[test]
fn session_cookie_accepts_values_above_previous_length_cap() {
let session_key = "x".repeat(20 * 1024);
let cookie = ClaudeCodeProviderOAuthAdapter::session_cookie(&session_key)
.expect("long sessionKey should remain valid");
assert_eq!(cookie.len(), "sessionKey=".len() + session_key.len());
}
#[tokio::test]
async fn rejects_wrong_state_and_hostile_authorize_redirects() {
for redirect_mode in [RedirectMode::WrongState, RedirectMode::HostileHost] {