diff --git a/src/http/mod.rs b/src/http/mod.rs index 4464f1f..741fea6 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -112,13 +112,26 @@ async fn execute_inner(cli: &Cli) -> Result { } else { None }; - let mut grpc_method = if let Some(schema) = &grpc_schema { + let grpc_method = if let Some(schema) = &grpc_schema { Some(proto::method_for_url(schema, &url)?) } else { None }; let session = load_session(cli)?; + let result = execute_request(cli, http_version, url, grpc_method, session.as_ref()).await; + if !cli.dry_run { + save_session(cli, session.as_ref()); + } + result +} +async fn execute_request( + cli: &Cli, + http_version: Option, + url: Url, + mut grpc_method: Option, + session: Option<&crate::session::Session>, +) -> Result { let request_start = Instant::now(); let request_timeout = cli .timeout @@ -138,7 +151,7 @@ async fn execute_inner(cli: &Cli) -> Result { request_timeout, connect_timeout, request_start, - session: session.as_ref(), + session, connect_timing: Some(&connect_timing), }; let mut initial_client = None; @@ -228,7 +241,7 @@ async fn execute_inner(cli: &Cli) -> Result { let total_attempts = total_attempts_for_retry(retry_count)?; let original_body_replayable = request_body_replayable(&body); let mut attempt = 0; - let result = loop { + loop { let mut request_method = method.clone(); let mut request_url = url.clone(); let mut request_body = body.clone(); @@ -452,9 +465,7 @@ async fn execute_inner(cli: &Cli) -> Result { break Err(FetchError::Runtime(message)); } } - }; - save_session(cli, session.as_ref()); - result + } } fn record_request_dns_timing( diff --git a/tests/http.rs b/tests/http.rs index 543b254..5d17499 100644 --- a/tests/http.rs +++ b/tests/http.rs @@ -801,6 +801,77 @@ fn redirects_range_status_and_timeouts() { assert_exit(&res, 0); } +#[test] +fn session_cookies_persist_after_invalid_redirect() { + let server = TestServer::start(|req| match req.path.as_str() { + "/start" => TestResponse::status(302, "Found", "") + .header("Set-Cookie", "sid=redirect; Path=/") + .header("Location", "http://[::1"), + "/check" if req.header("cookie").contains("sid=redirect") => TestResponse::ok("saved"), + "/check" => TestResponse::status(400, "Bad Request", "cookie missing"), + _ => TestResponse::status(404, "Not Found", ""), + }); + let dir = TempDir::new().unwrap(); + let sessions_dir = dir.path().join("sessions"); + let env = vec![( + "FETCH_INTERNAL_SESSIONS_DIR".to_string(), + sessions_dir.display().to_string(), + )]; + + let res = run_fetch_opts( + FetchOpts { + env: env.clone(), + ..Default::default() + }, + &[ + &format!("{}/start", server.url), + "--session", + "redirect-error", + ], + ); + assert_exit(&res, 1); + assert!(res.stderr.contains("invalid redirect location")); + + let res = run_fetch_opts( + FetchOpts { + env, + ..Default::default() + }, + &[ + &format!("{}/check", server.url), + "--session", + "redirect-error", + ], + ); + assert_exit(&res, 0); + assert_eq!(res.stdout, "saved"); +} + +#[test] +fn dry_run_leaves_corrupted_session_unchanged() { + let dir = TempDir::new().unwrap(); + let sessions_dir = dir.path().join("sessions"); + fs::create_dir_all(&sessions_dir).unwrap(); + let session_path = sessions_dir.join("dry-run.json"); + let original = b"not valid session json\n"; + fs::write(&session_path, original).unwrap(); + + let res = run_fetch_opts( + FetchOpts { + env: vec![( + "FETCH_INTERNAL_SESSIONS_DIR".to_string(), + sessions_dir.display().to_string(), + )], + ..Default::default() + }, + &["https://example.com", "--dry-run", "--session", "dry-run"], + ); + + assert_exit(&res, 0); + assert!(res.stderr.contains("session 'dry-run' is corrupted")); + assert_eq!(fs::read(session_path).unwrap(), original); +} + #[test] fn post_to_get_redirect_strips_entity_headers() { let server = TestServer::start(|req| match req.path.as_str() {