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