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::<Option<std::net::SocketAddr>>(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 rx.changed().await.unwrap();
166 let mid_addr = rx.borrow().unwrap();
167 assert_eq!(
168 req.headers()["referer"],
169 format!("http://{}/sensitive", mid_addr)
170 );
171 http::Response::default()
172 }
173 });
174
175 let end_addr = end_server.addr();
176
177 let mid_server = server::http(move |req| async move {
178 assert_eq!(req.headers()["cookie"], "foo=bar");
179 http::Response::builder()
180 .status(302)
181 .header("location", format!("http://{}/end", end_addr))
182 .body(Default::default())
183 .unwrap()
184 });
185
186 tx.send(Some(mid_server.addr())).unwrap();
187
188 reqwest::Client::builder()
189 .build()
190 .unwrap()
191 .get(&format!("http://{}/sensitive", mid_server.addr()))
192 .header(
193 reqwest::header::COOKIE,
194 reqwest::header::HeaderValue::from_static("foo=bar"),
195 )
196 .send()
197 .await
198 .unwrap();
199 }
200
201 #[tokio::test]
test_redirect_policy_can_return_errors()202 async fn test_redirect_policy_can_return_errors() {
203 let server = server::http(move |req| async move {
204 assert_eq!(req.uri(), "/loop");
205 http::Response::builder()
206 .status(302)
207 .header("location", "/loop")
208 .body(Default::default())
209 .unwrap()
210 });
211
212 let url = format!("http://{}/loop", server.addr());
213 let err = reqwest::get(&url).await.unwrap_err();
214 assert!(err.is_redirect());
215 }
216
217 #[tokio::test]
test_redirect_policy_can_stop_redirects_without_an_error()218 async fn test_redirect_policy_can_stop_redirects_without_an_error() {
219 let server = server::http(move |req| async move {
220 assert_eq!(req.uri(), "/no-redirect");
221 http::Response::builder()
222 .status(302)
223 .header("location", "/dont")
224 .body(Default::default())
225 .unwrap()
226 });
227
228 let url = format!("http://{}/no-redirect", server.addr());
229
230 let res = reqwest::Client::builder()
231 .redirect(reqwest::redirect::Policy::none())
232 .build()
233 .unwrap()
234 .get(&url)
235 .send()
236 .await
237 .unwrap();
238
239 assert_eq!(res.url().as_str(), url);
240 assert_eq!(res.status(), reqwest::StatusCode::FOUND);
241 }
242
243 #[tokio::test]
test_referer_is_not_set_if_disabled()244 async fn test_referer_is_not_set_if_disabled() {
245 let server = server::http(move |req| async move {
246 if req.uri() == "/no-refer" {
247 http::Response::builder()
248 .status(302)
249 .header("location", "/dst")
250 .body(Default::default())
251 .unwrap()
252 } else {
253 assert_eq!(req.uri(), "/dst");
254 assert_eq!(req.headers().get("referer"), None);
255
256 http::Response::default()
257 }
258 });
259
260 reqwest::Client::builder()
261 .referer(false)
262 .build()
263 .unwrap()
264 .get(&format!("http://{}/no-refer", server.addr()))
265 .send()
266 .await
267 .unwrap();
268 }
269
270 #[tokio::test]
test_invalid_location_stops_redirect_gh484()271 async fn test_invalid_location_stops_redirect_gh484() {
272 let server = server::http(move |_req| async move {
273 http::Response::builder()
274 .status(302)
275 .header("location", "http://www.yikes{KABOOM}")
276 .body(Default::default())
277 .unwrap()
278 });
279
280 let url = format!("http://{}/yikes", server.addr());
281
282 let res = reqwest::get(&url).await.unwrap();
283
284 assert_eq!(res.url().as_str(), url);
285 assert_eq!(res.status(), reqwest::StatusCode::FOUND);
286 }
287
288 #[cfg(feature = "cookies")]
289 #[tokio::test]
test_redirect_302_with_set_cookies()290 async fn test_redirect_302_with_set_cookies() {
291 let code = 302;
292 let server = server::http(move |req| async move {
293 if req.uri() == "/302" {
294 http::Response::builder()
295 .status(302)
296 .header("location", "/dst")
297 .header("set-cookie", "key=value")
298 .body(Default::default())
299 .unwrap()
300 } else {
301 assert_eq!(req.uri(), "/dst");
302 assert_eq!(req.headers()["cookie"], "key=value");
303 http::Response::default()
304 }
305 });
306
307 let url = format!("http://{}/{}", server.addr(), code);
308 let dst = format!("http://{}/{}", server.addr(), "dst");
309
310 let client = reqwest::ClientBuilder::new()
311 .cookie_store(true)
312 .build()
313 .unwrap();
314 let res = client.get(&url).send().await.unwrap();
315
316 assert_eq!(res.url().as_str(), dst);
317 assert_eq!(res.status(), reqwest::StatusCode::OK);
318 }
319