diff --git a/src/agent.rs b/src/agent.rs index 5bb9a736..60f6a132 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -408,14 +408,24 @@ impl AgentBuilder { /// /// If the redirect count hits this limit (and it's > 0), TooManyRedirects is returned. /// + /// WARNING: for 307 and 308 redirects, this value is ignored for methods that have a body. + /// You must handle 307 redirects yourself when sending a PUT, POST, PATCH, or DELETE request. + /// /// ``` /// # fn main() -> Result<(), ureq::Error> { /// # ureq::is_test(true); /// let result = ureq::builder() /// .redirects(1) /// .build() - /// .get("http://httpbin.org/redirect/3") - /// .call(); + /// .get("http://httpbin.org/status/301") + /// .error_on_non_2xx(false) + /// .call()?; + /// assert_ne!(result.status(), 301); + /// + /// let result = ureq::post("http://httpbin.org/status/307") + /// .error_on_non_2xx(false) + /// .send_bytes(b"some data")?; + /// assert_eq!(result.status(), 307); /// # Ok(()) /// # } /// ``` diff --git a/src/testserver.rs b/src/testserver.rs index a2eb4a99..8d7ea638 100644 --- a/src/testserver.rs +++ b/src/testserver.rs @@ -28,8 +28,12 @@ pub(crate) fn test_agent() -> Agent { stream.write_all(b"HTTP/1.1 200 OK\r\n")?; stream.write_all(b"\r\n")?; stream.write_all(br#"{"hello": "world"}"#)?; - } else if headers.path() == "/redirect/3" { - stream.write_all(b"HTTP/1.1 302 Found\r\n")?; + } else if headers.path() == "/status/301" { + stream.write_all(b"HTTP/1.1 301 Found\r\n")?; + stream.write_all(b"Location: /redirect/3\r\n")?; + stream.write_all(b"\r\n")?; + } else if headers.path() == "/status/307" { + stream.write_all(b"HTTP/1.1 307 Found\r\n")?; stream.write_all(b"Location: /redirect/3\r\n")?; stream.write_all(b"\r\n")?; } else { diff --git a/src/unit.rs b/src/unit.rs index 166fc125..4f794de3 100644 --- a/src/unit.rs +++ b/src/unit.rs @@ -263,9 +263,15 @@ pub(crate) fn connect( debug!("redirect {} {} -> {}", resp.status(), url, new_url); return connect(new_unit, use_pooled, redirect_count + 1, empty, true); } + // never change the method for 307/308 + // only resend the request if it cannot have a body + // NOTE: DELETE is intentionally excluded: https://stackoverflow.com/questions/299628 + 307 | 308 if ["GET", "HEAD", "OPTIONS", "TRACE"].contains(&method.as_str()) => { + let empty = Payload::Empty.into_read(); + debug!("redirect {} {} -> {}", resp.status(), url, new_url); + return connect(unit, use_pooled, redirect_count - 1, empty, true); + } _ => (), - // reinstate this with expect-100 - // 307 | 308 | _ => connect(unit, method, use_pooled, redirects - 1, body), }; } }