1 #![cfg(not(target_arch = "wasm32"))]
2 mod support;
3 use futures_util::stream::StreamExt;
4 use support::*;
5 
6 #[tokio::test]
test_redirect_301_and_302_and_303_changes_post_to_get()7 async fn test_redirect_301_and_302_and_303_changes_post_to_get() {
8     let client = reqwest::Client::new();
9     let codes = [301u16, 302, 303];
10 
11     for &code in codes.iter() {
12         let redirect = server::http(move |req| async move {
13             if req.method() == "POST" {
14                 assert_eq!(req.uri(), &*format!("/{}", code));
15                 http::Response::builder()
16                     .status(code)
17                     .header("location", "/dst")
18                     .header("server", "test-redirect")
19                     .body(Default::default())
20                     .unwrap()
21             } else {
22                 assert_eq!(req.method(), "GET");
23 
24                 http::Response::builder()
25                     .header("server", "test-dst")
26                     .body(Default::default())
27                     .unwrap()
28             }
29         });
30 
31         let url = format!("http://{}/{}", redirect.addr(), code);
32         let dst = format!("http://{}/{}", redirect.addr(), "dst");
33         let res = client.post(&url).send().await.unwrap();
34         assert_eq!(res.url().as_str(), dst);
35         assert_eq!(res.status(), reqwest::StatusCode::OK);
36         assert_eq!(
37             res.headers().get(reqwest::header::SERVER).unwrap(),
38             &"test-dst"
39         );
40     }
41 }
42 
43 #[tokio::test]
test_redirect_307_and_308_tries_to_get_again()44 async fn test_redirect_307_and_308_tries_to_get_again() {
45     let client = reqwest::Client::new();
46     let codes = [307u16, 308];
47     for &code in codes.iter() {
48         let redirect = server::http(move |req| async move {
49             assert_eq!(req.method(), "GET");
50             if req.uri() == &*format!("/{}", code) {
51                 http::Response::builder()
52                     .status(code)
53                     .header("location", "/dst")
54                     .header("server", "test-redirect")
55                     .body(Default::default())
56                     .unwrap()
57             } else {
58                 assert_eq!(req.uri(), "/dst");
59 
60                 http::Response::builder()
61                     .header("server", "test-dst")
62                     .body(Default::default())
63                     .unwrap()
64             }
65         });
66 
67         let url = format!("http://{}/{}", redirect.addr(), code);
68         let dst = format!("http://{}/{}", redirect.addr(), "dst");
69         let res = client.get(&url).send().await.unwrap();
70         assert_eq!(res.url().as_str(), dst);
71         assert_eq!(res.status(), reqwest::StatusCode::OK);
72         assert_eq!(
73             res.headers().get(reqwest::header::SERVER).unwrap(),
74             &"test-dst"
75         );
76     }
77 }
78 
79 #[tokio::test]
test_redirect_307_and_308_tries_to_post_again()80 async fn test_redirect_307_and_308_tries_to_post_again() {
81     let _ = env_logger::try_init();
82     let client = reqwest::Client::new();
83     let codes = [307u16, 308];
84     for &code in codes.iter() {
85         let redirect = server::http(move |mut req| async move {
86             assert_eq!(req.method(), "POST");
87             assert_eq!(req.headers()["content-length"], "5");
88 
89             let data = req.body_mut().next().await.unwrap().unwrap();
90             assert_eq!(&*data, b"Hello");
91 
92             if req.uri() == &*format!("/{}", code) {
93                 http::Response::builder()
94                     .status(code)
95                     .header("location", "/dst")
96                     .header("server", "test-redirect")
97                     .body(Default::default())
98                     .unwrap()
99             } else {
100                 assert_eq!(req.uri(), "/dst");
101 
102                 http::Response::builder()
103                     .header("server", "test-dst")
104                     .body(Default::default())
105                     .unwrap()
106             }
107         });
108 
109         let url = format!("http://{}/{}", redirect.addr(), code);
110         let dst = format!("http://{}/{}", redirect.addr(), "dst");
111         let res = client.post(&url).body("Hello").send().await.unwrap();
112         assert_eq!(res.url().as_str(), dst);
113         assert_eq!(res.status(), reqwest::StatusCode::OK);
114         assert_eq!(
115             res.headers().get(reqwest::header::SERVER).unwrap(),
116             &"test-dst"
117         );
118     }
119 }
120 
121 #[cfg(feature = "blocking")]
122 #[test]
test_redirect_307_does_not_try_if_reader_cannot_reset()123 fn test_redirect_307_does_not_try_if_reader_cannot_reset() {
124     let client = reqwest::blocking::Client::new();
125     let codes = [307u16, 308];
126     for &code in codes.iter() {
127         let redirect = server::http(move |mut req| async move {
128             assert_eq!(req.method(), "POST");
129             assert_eq!(req.uri(), &*format!("/{}", code));
130             assert_eq!(req.headers()["transfer-encoding"], "chunked");
131 
132             let data = req.body_mut().next().await.unwrap().unwrap();
133             assert_eq!(&*data, b"Hello");
134 
135             http::Response::builder()
136                 .status(code)
137                 .header("location", "/dst")
138                 .header("server", "test-redirect")
139                 .body(Default::default())
140                 .unwrap()
141         });
142 
143         let url = format!("http://{}/{}", redirect.addr(), code);
144         let res = client
145             .post(&url)
146             .body(reqwest::blocking::Body::new(&b"Hello"[..]))
147             .send()
148             .unwrap();
149         assert_eq!(res.url().as_str(), url);
150         assert_eq!(res.status(), code);
151     }
152 }
153 
154 #[tokio::test]
test_redirect_removes_sensitive_headers()155 async fn test_redirect_removes_sensitive_headers() {
156     use tokio::sync::watch;
157 
158     let (tx, rx) = watch::channel(None);
159 
160     let end_server = server::http(move |req| {
161         let mut rx = rx.clone();
162         async move {
163             assert_eq!(req.headers().get("cookie"), None);
164 
165             let mid_addr = rx.recv().await.unwrap().unwrap();
166             assert_eq!(
167                 req.headers()["referer"],
168                 format!("http://{}/sensitive", mid_addr)
169             );
170             http::Response::default()
171         }
172     });
173 
174     let end_addr = end_server.addr();
175 
176     let mid_server = server::http(move |req| async move {
177         assert_eq!(req.headers()["cookie"], "foo=bar");
178         http::Response::builder()
179             .status(302)
180             .header("location", format!("http://{}/end", end_addr))
181             .body(Default::default())
182             .unwrap()
183     });
184 
185     tx.broadcast(Some(mid_server.addr())).unwrap();
186 
187     reqwest::Client::builder()
188         .build()
189         .unwrap()
190         .get(&format!("http://{}/sensitive", mid_server.addr()))
191         .header(
192             reqwest::header::COOKIE,
193             reqwest::header::HeaderValue::from_static("foo=bar"),
194         )
195         .send()
196         .await
197         .unwrap();
198 }
199 
200 #[tokio::test]
test_redirect_policy_can_return_errors()201 async fn test_redirect_policy_can_return_errors() {
202     let server = server::http(move |req| async move {
203         assert_eq!(req.uri(), "/loop");
204         http::Response::builder()
205             .status(302)
206             .header("location", "/loop")
207             .body(Default::default())
208             .unwrap()
209     });
210 
211     let url = format!("http://{}/loop", server.addr());
212     let err = reqwest::get(&url).await.unwrap_err();
213     assert!(err.is_redirect());
214 }
215 
216 #[tokio::test]
test_redirect_policy_can_stop_redirects_without_an_error()217 async fn test_redirect_policy_can_stop_redirects_without_an_error() {
218     let server = server::http(move |req| async move {
219         assert_eq!(req.uri(), "/no-redirect");
220         http::Response::builder()
221             .status(302)
222             .header("location", "/dont")
223             .body(Default::default())
224             .unwrap()
225     });
226 
227     let url = format!("http://{}/no-redirect", server.addr());
228 
229     let res = reqwest::Client::builder()
230         .redirect(reqwest::redirect::Policy::none())
231         .build()
232         .unwrap()
233         .get(&url)
234         .send()
235         .await
236         .unwrap();
237 
238     assert_eq!(res.url().as_str(), url);
239     assert_eq!(res.status(), reqwest::StatusCode::FOUND);
240 }
241 
242 #[tokio::test]
test_referer_is_not_set_if_disabled()243 async fn test_referer_is_not_set_if_disabled() {
244     let server = server::http(move |req| async move {
245         if req.uri() == "/no-refer" {
246             http::Response::builder()
247                 .status(302)
248                 .header("location", "/dst")
249                 .body(Default::default())
250                 .unwrap()
251         } else {
252             assert_eq!(req.uri(), "/dst");
253             assert_eq!(req.headers().get("referer"), None);
254 
255             http::Response::default()
256         }
257     });
258 
259     reqwest::Client::builder()
260         .referer(false)
261         .build()
262         .unwrap()
263         .get(&format!("http://{}/no-refer", server.addr()))
264         .send()
265         .await
266         .unwrap();
267 }
268 
269 #[tokio::test]
test_invalid_location_stops_redirect_gh484()270 async fn test_invalid_location_stops_redirect_gh484() {
271     let server = server::http(move |_req| async move {
272         http::Response::builder()
273             .status(302)
274             .header("location", "http://www.yikes{KABOOM}")
275             .body(Default::default())
276             .unwrap()
277     });
278 
279     let url = format!("http://{}/yikes", server.addr());
280 
281     let res = reqwest::get(&url).await.unwrap();
282 
283     assert_eq!(res.url().as_str(), url);
284     assert_eq!(res.status(), reqwest::StatusCode::FOUND);
285 }
286 
287 #[cfg(feature = "cookies")]
288 #[tokio::test]
test_redirect_302_with_set_cookies()289 async fn test_redirect_302_with_set_cookies() {
290     let code = 302;
291     let server = server::http(move |req| async move {
292         if req.uri() == "/302" {
293             http::Response::builder()
294                 .status(302)
295                 .header("location", "/dst")
296                 .header("set-cookie", "key=value")
297                 .body(Default::default())
298                 .unwrap()
299         } else {
300             assert_eq!(req.uri(), "/dst");
301             assert_eq!(req.headers()["cookie"], "key=value");
302             http::Response::default()
303         }
304     });
305 
306     let url = format!("http://{}/{}", server.addr(), code);
307     let dst = format!("http://{}/{}", server.addr(), "dst");
308 
309     let client = reqwest::ClientBuilder::new()
310         .cookie_store(true)
311         .build()
312         .unwrap();
313     let res = client.get(&url).send().await.unwrap();
314 
315     assert_eq!(res.url().as_str(), dst);
316     assert_eq!(res.status(), reqwest::StatusCode::OK);
317 }
318