diff --git a/src/body.rs b/src/body.rs index 43fcfee..0016d0e 100644 --- a/src/body.rs +++ b/src/body.rs @@ -115,3 +115,10 @@ pub fn as_hyper_body<'a>(body: &'a mut Body) -> ::hyper::client::Body<'a> { } } } + +pub fn can_reset(body: &Body) -> bool { + match body.reader { + Kind::Bytes(_) => true, + Kind::Reader(..) => false, + } +} diff --git a/src/client.rs b/src/client.rs index 4c03c6a..a6a93f4 100644 --- a/src/client.rs +++ b/src/client.rs @@ -213,7 +213,13 @@ impl<'a> RequestBuilder<'a> { true }, StatusCode::TemporaryRedirect | - StatusCode::PermanentRedirect => true, + StatusCode::PermanentRedirect => { + if let Some(ref body) = body { + body::can_reset(body) + } else { + true + } + }, _ => false, }; diff --git a/tests/client.rs b/tests/client.rs index c6e8863..39b8080 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -33,45 +33,130 @@ fn test_get() { } #[test] -fn test_redirect_302_changes_post_to_get() { - - let redirect = server! { - request: b"\ - POST /302 HTTP/1.1\r\n\ - Host: $HOST\r\n\ - User-Agent: $USERAGENT\r\n\ - Content-Length: 0\r\n\ - \r\n\ - ", - response: b"\ - HTTP/1.1 302 Found\r\n\ - Server: test-redirect\r\n\ - Content-Length: 0\r\n\ - Location: /dst\r\n\ - Connection: close\r\n\ - \r\n\ - ", - - request: b"\ - GET /dst HTTP/1.1\r\n\ - Host: $HOST\r\n\ - User-Agent: $USERAGENT\r\n\ - Referer: http://$HOST/302\r\n\ - \r\n\ - ", - response: b"\ - HTTP/1.1 200 OK\r\n\ - Server: test-dst\r\n\ - Content-Length: 0\r\n\ - \r\n\ - " - }; - +fn test_redirect_301_and_302_and_303_changes_post_to_get() { let client = reqwest::Client::new().unwrap(); - let res = client.post(&format!("http://{}/302", redirect.addr())) - .send() - .unwrap(); - assert_eq!(res.status(), &reqwest::StatusCode::Ok); - assert_eq!(res.headers().get(), Some(&reqwest::header::Server("test-dst".to_string()))); + let codes = [301, 302, 303]; + for code in codes.iter() { + let redirect = server! { + request: format!("\ + POST /{} HTTP/1.1\r\n\ + Host: $HOST\r\n\ + User-Agent: $USERAGENT\r\n\ + Content-Length: 0\r\n\ + \r\n\ + ", code), + response: format!("\ + HTTP/1.1 {} reason\r\n\ + Server: test-redirect\r\n\ + Content-Length: 0\r\n\ + Location: /dst\r\n\ + Connection: close\r\n\ + \r\n\ + ", code), + + request: format!("\ + GET /dst HTTP/1.1\r\n\ + Host: $HOST\r\n\ + User-Agent: $USERAGENT\r\n\ + Referer: http://$HOST/{}\r\n\ + \r\n\ + ", code), + response: b"\ + HTTP/1.1 200 OK\r\n\ + Server: test-dst\r\n\ + Content-Length: 0\r\n\ + \r\n\ + " + }; + + let res = client.post(&format!("http://{}/{}", redirect.addr(), code)) + .send() + .unwrap(); + assert_eq!(res.status(), &reqwest::StatusCode::Ok); + assert_eq!(res.headers().get(), Some(&reqwest::header::Server("test-dst".to_string()))); + } +} + +#[test] +fn test_redirect_307_and_308_tries_to_post_again() { + let client = reqwest::Client::new().unwrap(); + let codes = [307, 308]; + for code in codes.iter() { + let redirect = server! { + request: format!("\ + POST /{} HTTP/1.1\r\n\ + Host: $HOST\r\n\ + User-Agent: $USERAGENT\r\n\ + Content-Length: 5\r\n\ + \r\n\ + Hello\ + ", code), + response: format!("\ + HTTP/1.1 {} reason\r\n\ + Server: test-redirect\r\n\ + Content-Length: 0\r\n\ + Location: /dst\r\n\ + Connection: close\r\n\ + \r\n\ + ", code), + + request: format!("\ + POST /dst HTTP/1.1\r\n\ + Host: $HOST\r\n\ + User-Agent: $USERAGENT\r\n\ + Referer: http://$HOST/{}\r\n\ + Content-Length: 5\r\n\ + \r\n\ + Hello\ + ", code), + response: b"\ + HTTP/1.1 200 OK\r\n\ + Server: test-dst\r\n\ + Content-Length: 0\r\n\ + \r\n\ + " + }; + + let res = client.post(&format!("http://{}/{}", redirect.addr(), code)) + .body("Hello") + .send() + .unwrap(); + assert_eq!(res.status(), &reqwest::StatusCode::Ok); + assert_eq!(res.headers().get(), Some(&reqwest::header::Server("test-dst".to_string()))); + } +} + +#[test] +fn test_redirect_307_does_not_try_if_reader_cannot_reset() { + let client = reqwest::Client::new().unwrap(); + let codes = [307, 308]; + for &code in codes.iter() { + let redirect = server! { + request: format!("\ + POST /{} HTTP/1.1\r\n\ + Host: $HOST\r\n\ + User-Agent: $USERAGENT\r\n\ + Transfer-Encoding: chunked\r\n\ + \r\n\ + 5\r\n\ + Hello\r\n\ + 0\r\n\r\n\ + ", code), + response: format!("\ + HTTP/1.1 {} reason\r\n\ + Server: test-redirect\r\n\ + Content-Length: 0\r\n\ + Location: /dst\r\n\ + Connection: close\r\n\ + \r\n\ + ", code) + }; + + let res = client.post(&format!("http://{}/{}", redirect.addr(), code)) + .body(reqwest::Body::new(&b"Hello"[..])) + .send() + .unwrap(); + assert_eq!(res.status(), &reqwest::StatusCode::from_u16(code)); + } }