Rewrite tests with a hyper server instead of raw TCP
This makes the tests much less brittle, by not depending on the exact order of the HTTP headers, nor always requiring to check for every single header.
This commit is contained in:
		| @@ -1,35 +1,15 @@ | |||||||
| #[macro_use] |  | ||||||
| mod support; | mod support; | ||||||
|  | use support::*; | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_response_text() { | fn test_response_text() { | ||||||
|     let server = server! { |     let server = server::http(move |_req| async { http::Response::new("Hello".into()) }); | ||||||
|         request: b"\ |  | ||||||
|             GET /text HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: test\r\n\ |  | ||||||
|             Content-Length: 5\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             Hello\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/text", server.addr()); |     let url = format!("http://{}/text", server.addr()); | ||||||
|     let res = reqwest::blocking::get(&url).unwrap(); |     let res = reqwest::blocking::get(&url).unwrap(); | ||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |     assert_eq!(res.content_length(), Some(5)); | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), |  | ||||||
|         &"5" |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     let body = res.text().unwrap(); |     let body = res.text().unwrap(); | ||||||
|     assert_eq!(b"Hello", body.as_bytes()); |     assert_eq!(b"Hello", body.as_bytes()); | ||||||
| @@ -37,34 +17,20 @@ fn test_response_text() { | |||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_response_non_utf_8_text() { | fn test_response_non_utf_8_text() { | ||||||
|     let server = server! { |     let server = server::http(move |_req| { | ||||||
|         request: b"\ |         async { | ||||||
|             GET /text HTTP/1.1\r\n\ |             http::Response::builder() | ||||||
|             user-agent: $USERAGENT\r\n\ |                 .header("content-type", "text/plain; charset=gbk") | ||||||
|             accept: */*\r\n\ |                 .body(b"\xc4\xe3\xba\xc3"[..].into()) | ||||||
|             accept-encoding: gzip\r\n\ |                 .unwrap() | ||||||
|             host: $HOST\r\n\ |         } | ||||||
|             \r\n\ |     }); | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: test\r\n\ |  | ||||||
|             Content-Length: 4\r\n\ |  | ||||||
|             Content-Type: text/plain; charset=gbk\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             \xc4\xe3\xba\xc3\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/text", server.addr()); |     let url = format!("http://{}/text", server.addr()); | ||||||
|     let res = reqwest::blocking::get(&url).unwrap(); |     let res = reqwest::blocking::get(&url).unwrap(); | ||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |     assert_eq!(res.content_length(), Some(4)); | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), |  | ||||||
|         &"4" |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     let body = res.text().unwrap(); |     let body = res.text().unwrap(); | ||||||
|     assert_eq!("你好", &body); |     assert_eq!("你好", &body); | ||||||
| @@ -73,33 +39,13 @@ fn test_response_non_utf_8_text() { | |||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_response_json() { | fn test_response_json() { | ||||||
|     let server = server! { |     let server = server::http(move |_req| async { http::Response::new("\"Hello\"".into()) }); | ||||||
|         request: b"\ |  | ||||||
|             GET /json HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: test\r\n\ |  | ||||||
|             Content-Length: 7\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             \"Hello\"\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/json", server.addr()); |     let url = format!("http://{}/json", server.addr()); | ||||||
|     let res = reqwest::blocking::get(&url).unwrap(); |     let res = reqwest::blocking::get(&url).unwrap(); | ||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |     assert_eq!(res.content_length(), Some(7)); | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), |  | ||||||
|         &"7" |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     let body = res.json::<String>().unwrap(); |     let body = res.json::<String>().unwrap(); | ||||||
|     assert_eq!("Hello", body); |     assert_eq!("Hello", body); | ||||||
| @@ -107,66 +53,27 @@ fn test_response_json() { | |||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_response_copy_to() { | fn test_response_copy_to() { | ||||||
|     let server = server! { |     let server = server::http(move |_req| async { http::Response::new("Hello".into()) }); | ||||||
|         request: b"\ |  | ||||||
|             GET /1 HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: test\r\n\ |  | ||||||
|             Content-Length: 5\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             Hello\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/1", server.addr()); |     let url = format!("http://{}/1", server.addr()); | ||||||
|     let res = reqwest::blocking::get(&url).unwrap(); |     let mut res = reqwest::blocking::get(&url).unwrap(); | ||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |  | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), |  | ||||||
|         &"5" |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     assert_eq!("Hello".to_owned(), res.text().unwrap()); |     let mut dst = Vec::new(); | ||||||
|  |     res.copy_to(&mut dst).unwrap(); | ||||||
|  |     assert_eq!(dst, b"Hello"); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_get() { | fn test_get() { | ||||||
|     let server = server! { |     let server = server::http(move |_req| async { http::Response::default() }); | ||||||
|         request: b"\ |  | ||||||
|             GET /1 HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: test\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/1", server.addr()); |     let url = format!("http://{}/1", server.addr()); | ||||||
|     let res = reqwest::blocking::get(&url).unwrap(); |     let res = reqwest::blocking::get(&url).unwrap(); | ||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |  | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), |  | ||||||
|         &"0" |  | ||||||
|     ); |  | ||||||
|     assert_eq!(res.remote_addr(), Some(server.addr())); |     assert_eq!(res.remote_addr(), Some(server.addr())); | ||||||
|  |  | ||||||
|     assert_eq!(res.text().unwrap().len(), 0) |     assert_eq!(res.text().unwrap().len(), 0) | ||||||
| @@ -174,24 +81,17 @@ fn test_get() { | |||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_post() { | fn test_post() { | ||||||
|     let server = server! { |     let server = server::http(move |mut req| { | ||||||
|         request: b"\ |         async move { | ||||||
|             POST /2 HTTP/1.1\r\n\ |             assert_eq!(req.method(), "POST"); | ||||||
|             content-length: 5\r\n\ |             assert_eq!(req.headers()["content-length"], "5"); | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |             let data = req.body_mut().next().await.unwrap().unwrap(); | ||||||
|             accept-encoding: gzip\r\n\ |             assert_eq!(&*data, b"Hello"); | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |             http::Response::default() | ||||||
|             Hello\ |         } | ||||||
|             ", |     }); | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: post\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/2", server.addr()); |     let url = format!("http://{}/2", server.addr()); | ||||||
|     let res = reqwest::blocking::Client::new() |     let res = reqwest::blocking::Client::new() | ||||||
| @@ -202,36 +102,25 @@ fn test_post() { | |||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"post"); |  | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), |  | ||||||
|         &"0" |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     assert_eq!(res.text().unwrap().len(), 0) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_post_form() { | fn test_post_form() { | ||||||
|     let server = server! { |     let server = server::http(move |mut req| { | ||||||
|         request: b"\ |         async move { | ||||||
|             POST /form HTTP/1.1\r\n\ |             assert_eq!(req.method(), "POST"); | ||||||
|             content-type: application/x-www-form-urlencoded\r\n\ |             assert_eq!(req.headers()["content-length"], "24"); | ||||||
|             content-length: 24\r\n\ |             assert_eq!( | ||||||
|             user-agent: $USERAGENT\r\n\ |                 req.headers()["content-type"], | ||||||
|             accept: */*\r\n\ |                 "application/x-www-form-urlencoded" | ||||||
|             accept-encoding: gzip\r\n\ |             ); | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |             let data = req.body_mut().next().await.unwrap().unwrap(); | ||||||
|             hello=world&sean=monstar\ |             assert_eq!(&*data, b"hello=world&sean=monstar"); | ||||||
|             ", |  | ||||||
|         response: b"\ |             http::Response::default() | ||||||
|             HTTP/1.1 200 OK\r\n\ |         } | ||||||
|             Server: post-form\r\n\ |     }); | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let form = &[("hello", "world"), ("sean", "monstar")]; |     let form = &[("hello", "world"), ("sean", "monstar")]; | ||||||
|  |  | ||||||
| @@ -250,22 +139,14 @@ fn test_post_form() { | |||||||
| /// returns a error. | /// returns a error. | ||||||
| #[test] | #[test] | ||||||
| fn test_error_for_status_4xx() { | fn test_error_for_status_4xx() { | ||||||
|     let server = server! { |     let server = server::http(move |_req| { | ||||||
|         request: b"\ |         async { | ||||||
|             GET /1 HTTP/1.1\r\n\ |             http::Response::builder() | ||||||
|             user-agent: $USERAGENT\r\n\ |                 .status(400) | ||||||
|             accept: */*\r\n\ |                 .body(Default::default()) | ||||||
|             accept-encoding: gzip\r\n\ |                 .unwrap() | ||||||
|             host: $HOST\r\n\ |         } | ||||||
|             \r\n\ |     }); | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 400 OK\r\n\ |  | ||||||
|             Server: test\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/1", server.addr()); |     let url = format!("http://{}/1", server.addr()); | ||||||
|     let res = reqwest::blocking::get(&url).unwrap(); |     let res = reqwest::blocking::get(&url).unwrap(); | ||||||
| @@ -279,22 +160,14 @@ fn test_error_for_status_4xx() { | |||||||
| /// returns a error. | /// returns a error. | ||||||
| #[test] | #[test] | ||||||
| fn test_error_for_status_5xx() { | fn test_error_for_status_5xx() { | ||||||
|     let server = server! { |     let server = server::http(move |_req| { | ||||||
|         request: b"\ |         async { | ||||||
|             GET /1 HTTP/1.1\r\n\ |             http::Response::builder() | ||||||
|             user-agent: $USERAGENT\r\n\ |                 .status(500) | ||||||
|             accept: */*\r\n\ |                 .body(Default::default()) | ||||||
|             accept-encoding: gzip\r\n\ |                 .unwrap() | ||||||
|             host: $HOST\r\n\ |         } | ||||||
|             \r\n\ |     }); | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 500 OK\r\n\ |  | ||||||
|             Server: test\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/1", server.addr()); |     let url = format!("http://{}/1", server.addr()); | ||||||
|     let res = reqwest::blocking::get(&url).unwrap(); |     let res = reqwest::blocking::get(&url).unwrap(); | ||||||
| @@ -309,144 +182,75 @@ fn test_error_for_status_5xx() { | |||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_default_headers() { | fn test_default_headers() { | ||||||
|     use reqwest::header; |     let server = server::http(move |req| { | ||||||
|     let mut headers = header::HeaderMap::with_capacity(1); |         async move { | ||||||
|     headers.insert(header::COOKIE, header::HeaderValue::from_static("a=b;c=d")); |             assert_eq!(req.headers()["reqwest-test"], "orly"); | ||||||
|  |             http::Response::default() | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     let mut headers = http::HeaderMap::with_capacity(1); | ||||||
|  |     headers.insert("reqwest-test", "orly".parse().unwrap()); | ||||||
|     let client = reqwest::blocking::Client::builder() |     let client = reqwest::blocking::Client::builder() | ||||||
|         .default_headers(headers) |         .default_headers(headers) | ||||||
|         .build() |         .build() | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET /1 HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             cookie: a=b;c=d\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: test\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/1", server.addr()); |     let url = format!("http://{}/1", server.addr()); | ||||||
|     let res = client.get(&url).send().unwrap(); |     let res = client.get(&url).send().unwrap(); | ||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |  | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), |  | ||||||
|         &"0" |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET /2 HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             cookie: a=b;c=d\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: test\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/2", server.addr()); |  | ||||||
|     let res = client.get(&url).send().unwrap(); |  | ||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), &url); |  | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |  | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |  | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), |  | ||||||
|         &"0" |  | ||||||
|     ); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_override_default_headers() { | fn test_override_default_headers() { | ||||||
|     use reqwest::header; |     let server = server::http(move |req| { | ||||||
|     let mut headers = header::HeaderMap::with_capacity(1); |         async move { | ||||||
|  |             // not 'iamatoken' | ||||||
|  |             assert_eq!(req.headers()[&http::header::AUTHORIZATION], "secret"); | ||||||
|  |             http::Response::default() | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     let mut headers = http::HeaderMap::with_capacity(1); | ||||||
|     headers.insert( |     headers.insert( | ||||||
|         header::AUTHORIZATION, |         http::header::AUTHORIZATION, | ||||||
|         header::HeaderValue::from_static("iamatoken"), |         http::header::HeaderValue::from_static("iamatoken"), | ||||||
|     ); |     ); | ||||||
|     let client = reqwest::blocking::Client::builder() |     let client = reqwest::blocking::Client::builder() | ||||||
|         .default_headers(headers) |         .default_headers(headers) | ||||||
|         .build() |         .build() | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET /3 HTTP/1.1\r\n\ |  | ||||||
|             authorization: secret\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: test\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/3", server.addr()); |     let url = format!("http://{}/3", server.addr()); | ||||||
|     let res = client |     let res = client | ||||||
|         .get(&url) |         .get(&url) | ||||||
|         .header( |         .header( | ||||||
|             header::AUTHORIZATION, |             http::header::AUTHORIZATION, | ||||||
|             header::HeaderValue::from_static("secret"), |             http::header::HeaderValue::from_static("secret"), | ||||||
|         ) |         ) | ||||||
|         .send() |         .send() | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |  | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), |  | ||||||
|         &"0" |  | ||||||
|     ); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_appended_headers_not_overwritten() { | fn test_appended_headers_not_overwritten() { | ||||||
|     let client = reqwest::blocking::Client::new(); |     let server = server::http(move |req| { | ||||||
|  |         async move { | ||||||
|  |             let mut accepts = req.headers().get_all("accept").into_iter(); | ||||||
|  |             assert_eq!(accepts.next().unwrap(), "application/json"); | ||||||
|  |             assert_eq!(accepts.next().unwrap(), "application/json+hal"); | ||||||
|  |             assert_eq!(accepts.next(), None); | ||||||
|  |  | ||||||
|     let server = server! { |             http::Response::default() | ||||||
|         request: b"\ |         } | ||||||
|             GET /4 HTTP/1.1\r\n\ |     }); | ||||||
|             accept: application/json\r\n\ |  | ||||||
|             accept: application/json+hal\r\n\ |     let client = reqwest::blocking::Client::new(); | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: test\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/4", server.addr()); |     let url = format!("http://{}/4", server.addr()); | ||||||
|     let res = client |     let res = client | ||||||
| @@ -458,11 +262,6 @@ fn test_appended_headers_not_overwritten() { | |||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |  | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), |  | ||||||
|         &"0" |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     // make sure this also works with default headers |     // make sure this also works with default headers | ||||||
|     use reqwest::header; |     use reqwest::header; | ||||||
| @@ -476,24 +275,6 @@ fn test_appended_headers_not_overwritten() { | |||||||
|         .build() |         .build() | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET /4 HTTP/1.1\r\n\ |  | ||||||
|             accept: application/json\r\n\ |  | ||||||
|             accept: application/json+hal\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: test\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/4", server.addr()); |     let url = format!("http://{}/4", server.addr()); | ||||||
|     let res = client |     let res = client | ||||||
|         .get(&url) |         .get(&url) | ||||||
| @@ -504,9 +285,4 @@ fn test_appended_headers_not_overwritten() { | |||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |  | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), |  | ||||||
|         &"0" |  | ||||||
|     ); |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										360
									
								
								tests/client.rs
									
									
									
									
									
								
							
							
						
						
									
										360
									
								
								tests/client.rs
									
									
									
									
									
								
							| @@ -1,44 +1,35 @@ | |||||||
| #[macro_use] |  | ||||||
| mod support; | mod support; | ||||||
|  | use support::*; | ||||||
|  |  | ||||||
| use std::io::Write; | use reqwest::Client; | ||||||
| use std::time::Duration; |  | ||||||
|  |  | ||||||
| use reqwest::multipart::{Form, Part}; |  | ||||||
| use reqwest::{Body, Client}; |  | ||||||
|  |  | ||||||
| use bytes::Bytes; |  | ||||||
|  |  | ||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn gzip_response() { | async fn auto_headers() { | ||||||
|     gzip_case(10_000, 4096).await; |     let server = server::http(move |req| { | ||||||
|  |         async move { | ||||||
|  |             assert_eq!(req.method(), "GET"); | ||||||
|  |  | ||||||
|  |             assert_eq!(req.headers()["accept"], "*/*"); | ||||||
|  |             assert_eq!(req.headers()["user-agent"], DEFAULT_USER_AGENT); | ||||||
|  |             assert_eq!(req.headers()["accept-encoding"], "gzip"); | ||||||
|  |  | ||||||
|  |             http::Response::default() | ||||||
|         } |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
| #[tokio::test] |     let url = format!("http://{}/1", server.addr()); | ||||||
| async fn gzip_single_byte_chunks() { |     let res = reqwest::get(&url).await.unwrap(); | ||||||
|     gzip_case(10, 1).await; |  | ||||||
|  |     assert_eq!(res.url().as_str(), &url); | ||||||
|  |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|  |     assert_eq!(res.remote_addr(), Some(server.addr())); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn response_text() { | async fn response_text() { | ||||||
|     let _ = env_logger::try_init(); |     let _ = env_logger::try_init(); | ||||||
|  |  | ||||||
|     let server = server! { |     let server = server::http(move |_req| async { http::Response::new("Hello".into()) }); | ||||||
|         request: b"\ |  | ||||||
|             GET /text HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Content-Length: 5\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             Hello\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let client = Client::new(); |     let client = Client::new(); | ||||||
|  |  | ||||||
| @@ -55,22 +46,7 @@ async fn response_text() { | |||||||
| async fn response_json() { | async fn response_json() { | ||||||
|     let _ = env_logger::try_init(); |     let _ = env_logger::try_init(); | ||||||
|  |  | ||||||
|     let server = server! { |     let server = server::http(move |_req| async { http::Response::new("\"Hello\"".into()) }); | ||||||
|         request: b"\ |  | ||||||
|             GET /json HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Content-Length: 7\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             \"Hello\"\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let client = Client::new(); |     let client = Client::new(); | ||||||
|  |  | ||||||
| @@ -83,287 +59,29 @@ async fn response_json() { | |||||||
|     assert_eq!("Hello", text); |     assert_eq!("Hello", text); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[tokio::test] |  | ||||||
| async fn multipart() { |  | ||||||
|     use futures_util::{future, stream}; |  | ||||||
|  |  | ||||||
|     let _ = env_logger::try_init(); |  | ||||||
|  |  | ||||||
|     let stream = reqwest::Body::wrap_stream(stream::once(future::ready(Ok::<_, reqwest::Error>( |  | ||||||
|         "part1 part2".to_owned(), |  | ||||||
|     )))); |  | ||||||
|     let part = Part::stream(stream); |  | ||||||
|  |  | ||||||
|     let form = Form::new().text("foo", "bar").part("part_stream", part); |  | ||||||
|  |  | ||||||
|     let expected_body = format!( |  | ||||||
|         "\ |  | ||||||
|          24\r\n\ |  | ||||||
|          --{0}\r\n\r\n\ |  | ||||||
|          2E\r\n\ |  | ||||||
|          Content-Disposition: form-data; name=\"foo\"\r\n\r\n\r\n\ |  | ||||||
|          3\r\n\ |  | ||||||
|          bar\r\n\ |  | ||||||
|          2\r\n\ |  | ||||||
|          \r\n\r\n\ |  | ||||||
|          24\r\n\ |  | ||||||
|          --{0}\r\n\r\n\ |  | ||||||
|          36\r\n\ |  | ||||||
|          Content-Disposition: form-data; name=\"part_stream\"\r\n\r\n\r\n\ |  | ||||||
|          B\r\n\ |  | ||||||
|          part1 part2\r\n\ |  | ||||||
|          2\r\n\ |  | ||||||
|          \r\n\r\n\ |  | ||||||
|          26\r\n\ |  | ||||||
|          --{0}--\r\n\r\n\ |  | ||||||
|          0\r\n\r\n\ |  | ||||||
|          ", |  | ||||||
|         form.boundary() |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     let server = server! { |  | ||||||
|         request: format!("\ |  | ||||||
|             POST /multipart/1 HTTP/1.1\r\n\ |  | ||||||
|             content-type: multipart/form-data; boundary={}\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             transfer-encoding: chunked\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             {}\ |  | ||||||
|             ", form.boundary(), expected_body), |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: multipart\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/multipart/1", server.addr()); |  | ||||||
|  |  | ||||||
|     let client = Client::new(); |  | ||||||
|  |  | ||||||
|     let res = client |  | ||||||
|         .post(&url) |  | ||||||
|         .multipart(form) |  | ||||||
|         .send() |  | ||||||
|         .await |  | ||||||
|         .expect("Failed to post multipart"); |  | ||||||
|     assert_eq!(res.url().as_str(), &url); |  | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[tokio::test] |  | ||||||
| async fn request_timeout() { |  | ||||||
|     let _ = env_logger::try_init(); |  | ||||||
|  |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET /slow HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Content-Length: 5\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             Hello\ |  | ||||||
|             ", |  | ||||||
|         read_timeout: Duration::from_secs(2) |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let client = Client::builder() |  | ||||||
|         .timeout(Duration::from_millis(500)) |  | ||||||
|         .build() |  | ||||||
|         .unwrap(); |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/slow", server.addr()); |  | ||||||
|  |  | ||||||
|     let res = client.get(&url).send().await; |  | ||||||
|  |  | ||||||
|     let err = res.unwrap_err(); |  | ||||||
|  |  | ||||||
|     assert!(err.is_timeout()); |  | ||||||
|     assert_eq!(err.url().map(|u| u.as_str()), Some(url.as_str())); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[tokio::test] |  | ||||||
| async fn response_timeout() { |  | ||||||
|     let _ = env_logger::try_init(); |  | ||||||
|  |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET /slow HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Content-Length: 5\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             Hello\ |  | ||||||
|             ", |  | ||||||
|         write_timeout: Duration::from_secs(2) |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let client = Client::builder() |  | ||||||
|         .timeout(Duration::from_millis(500)) |  | ||||||
|         .build() |  | ||||||
|         .unwrap(); |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/slow", server.addr()); |  | ||||||
|     let res = client.get(&url).send().await.expect("Failed to get"); |  | ||||||
|     let body = res.text().await; |  | ||||||
|  |  | ||||||
|     let err = body.unwrap_err(); |  | ||||||
|  |  | ||||||
|     assert!(err.is_timeout()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async fn gzip_case(response_size: usize, chunk_size: usize) { |  | ||||||
|     let content: String = (0..response_size) |  | ||||||
|         .into_iter() |  | ||||||
|         .map(|i| format!("test {}", i)) |  | ||||||
|         .collect(); |  | ||||||
|     let mut encoder = libflate::gzip::Encoder::new(Vec::new()).unwrap(); |  | ||||||
|     match encoder.write(content.as_bytes()) { |  | ||||||
|         Ok(n) => assert!(n > 0, "Failed to write to encoder."), |  | ||||||
|         _ => panic!("Failed to gzip encode string."), |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let gzipped_content = encoder.finish().into_result().unwrap(); |  | ||||||
|  |  | ||||||
|     let mut response = format!( |  | ||||||
|         "\ |  | ||||||
|          HTTP/1.1 200 OK\r\n\ |  | ||||||
|          Server: test-accept\r\n\ |  | ||||||
|          Content-Encoding: gzip\r\n\ |  | ||||||
|          Content-Length: {}\r\n\ |  | ||||||
|          \r\n", |  | ||||||
|         &gzipped_content.len() |  | ||||||
|     ) |  | ||||||
|     .into_bytes(); |  | ||||||
|     response.extend(&gzipped_content); |  | ||||||
|  |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET /gzip HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         chunk_size: chunk_size, |  | ||||||
|         write_timeout: Duration::from_millis(10), |  | ||||||
|         response: response |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let client = Client::new(); |  | ||||||
|  |  | ||||||
|     let res = client |  | ||||||
|         .get(&format!("http://{}/gzip", server.addr())) |  | ||||||
|         .send() |  | ||||||
|         .await |  | ||||||
|         .expect("response"); |  | ||||||
|  |  | ||||||
|     let body = res.text().await.expect("text"); |  | ||||||
|     assert_eq!(body, content); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[tokio::test] |  | ||||||
| async fn body_stream() { |  | ||||||
|     let _ = env_logger::try_init(); |  | ||||||
|  |  | ||||||
|     let source = futures_util::stream::iter::<Vec<Result<Bytes, std::io::Error>>>(vec![ |  | ||||||
|         Ok(Bytes::from_static(b"123")), |  | ||||||
|         Ok(Bytes::from_static(b"4567")), |  | ||||||
|     ]); |  | ||||||
|  |  | ||||||
|     let expected_body = "3\r\n123\r\n4\r\n4567\r\n0\r\n\r\n"; |  | ||||||
|  |  | ||||||
|     let server = server! { |  | ||||||
|         request: format!("\ |  | ||||||
|             POST /post HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             transfer-encoding: chunked\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             {}\ |  | ||||||
|             ", expected_body), |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: post\r\n\ |  | ||||||
|             Content-Length: 7\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/post", server.addr()); |  | ||||||
|  |  | ||||||
|     let client = Client::new(); |  | ||||||
|  |  | ||||||
|     let res = client |  | ||||||
|         .post(&url) |  | ||||||
|         .body(Body::wrap_stream(source)) |  | ||||||
|         .send() |  | ||||||
|         .await |  | ||||||
|         .expect("Failed to post"); |  | ||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), &url); |  | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn body_pipe_response() { | async fn body_pipe_response() { | ||||||
|     let _ = env_logger::try_init(); |     let _ = env_logger::try_init(); | ||||||
|  |  | ||||||
|     let server = server! { |     let server = server::http(move |mut req| { | ||||||
|         request: b"\ |         async move { | ||||||
|             GET /get HTTP/1.1\r\n\ |             if req.uri() == "/get" { | ||||||
|             user-agent: $USERAGENT\r\n\ |                 http::Response::new("pipe me".into()) | ||||||
|             accept: */*\r\n\ |             } else { | ||||||
|             accept-encoding: gzip\r\n\ |                 assert_eq!(req.uri(), "/pipe"); | ||||||
|             host: $HOST\r\n\ |                 assert_eq!(req.headers()["transfer-encoding"], "chunked"); | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: pipe\r\n\ |  | ||||||
|             Content-Length: 7\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             pipe me\ |  | ||||||
|             "; |  | ||||||
|  |  | ||||||
|         request: b"\ |                 let mut full: Vec<u8> = Vec::new(); | ||||||
|             POST /pipe HTTP/1.1\r\n\ |                 while let Some(item) = req.body_mut().next().await { | ||||||
|             user-agent: $USERAGENT\r\n\ |                     full.extend(&*item.unwrap()); | ||||||
|             accept: */*\r\n\ |                 } | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |                 assert_eq!(full, b"pipe me"); | ||||||
|             transfer-encoding: chunked\r\n\ |  | ||||||
|             \r\n\ |                 http::Response::default() | ||||||
|             7\r\n\ |             } | ||||||
|             pipe me\r\n\ |         } | ||||||
|             0\r\n\r\n\ |     }); | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: pipe\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let client = Client::new(); |     let client = Client::new(); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										384
									
								
								tests/cookie.rs
									
									
									
									
									
								
							
							
						
						
									
										384
									
								
								tests/cookie.rs
									
									
									
									
									
								
							| @@ -1,38 +1,32 @@ | |||||||
| #[macro_use] |  | ||||||
| mod support; | mod support; | ||||||
|  | use support::*; | ||||||
|  |  | ||||||
| #[test] | #[tokio::test] | ||||||
| fn cookie_response_accessor() { | async fn cookie_response_accessor() { | ||||||
|     let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); |     let server = server::http(move |_req| { | ||||||
|     let client = reqwest::r#async::Client::new(); |         async move { | ||||||
|  |             http::Response::builder() | ||||||
|  |                 .header("Set-Cookie", "key=val") | ||||||
|  |                 .header( | ||||||
|  |                     "Set-Cookie", | ||||||
|  |                     "expires=1; Expires=Wed, 21 Oct 2015 07:28:00 GMT", | ||||||
|  |                 ) | ||||||
|  |                 .header("Set-Cookie", "path=1; Path=/the-path") | ||||||
|  |                 .header("Set-Cookie", "maxage=1; Max-Age=100") | ||||||
|  |                 .header("Set-Cookie", "domain=1; Domain=mydomain") | ||||||
|  |                 .header("Set-Cookie", "secure=1; Secure") | ||||||
|  |                 .header("Set-Cookie", "httponly=1; HttpOnly") | ||||||
|  |                 .header("Set-Cookie", "samesitelax=1; SameSite=Lax") | ||||||
|  |                 .header("Set-Cookie", "samesitestrict=1; SameSite=Strict") | ||||||
|  |                 .body(Default::default()) | ||||||
|  |                 .unwrap() | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     let server = server! { |     let client = reqwest::Client::new(); | ||||||
|         request: b"\ |  | ||||||
|             GET / HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Set-Cookie: key=val\r\n\ |  | ||||||
|             Set-Cookie: expires=1; Expires=Wed, 21 Oct 2015 07:28:00 GMT\r\n\ |  | ||||||
|             Set-Cookie: path=1; Path=/the-path\r\n\ |  | ||||||
|             Set-Cookie: maxage=1; Max-Age=100\r\n\ |  | ||||||
|             Set-Cookie: domain=1; Domain=mydomain\r\n\ |  | ||||||
|             Set-Cookie: secure=1; Secure\r\n\ |  | ||||||
|             Set-Cookie: httponly=1; HttpOnly\r\n\ |  | ||||||
|             Set-Cookie: samesitelax=1; SameSite=Lax\r\n\ |  | ||||||
|             Set-Cookie: samesitestrict=1; SameSite=Strict\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/", server.addr()); |     let url = format!("http://{}/", server.addr()); | ||||||
|     let res = rt.block_on(client.get(&url).send()).unwrap(); |     let res = client.get(&url).send().await.unwrap(); | ||||||
|  |  | ||||||
|     let cookies = res.cookies().collect::<Vec<_>>(); |     let cookies = res.cookies().collect::<Vec<_>>(); | ||||||
|  |  | ||||||
| @@ -79,273 +73,143 @@ fn cookie_response_accessor() { | |||||||
|     assert!(cookies[8].same_site_strict()); |     assert!(cookies[8].same_site_strict()); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[tokio::test] | ||||||
| fn cookie_store_simple() { | async fn cookie_store_simple() { | ||||||
|     let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); |     let server = server::http(move |req| { | ||||||
|     let client = reqwest::r#async::Client::builder() |         async move { | ||||||
|  |             if req.uri() == "/2" { | ||||||
|  |                 assert_eq!(req.headers()["cookie"], "key=val"); | ||||||
|  |             } | ||||||
|  |             http::Response::builder() | ||||||
|  |                 .header("Set-Cookie", "key=val; HttpOnly") | ||||||
|  |                 .body(Default::default()) | ||||||
|  |                 .unwrap() | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     let client = reqwest::Client::builder() | ||||||
|         .cookie_store(true) |         .cookie_store(true) | ||||||
|         .build() |         .build() | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET / HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Set-Cookie: key=val; HttpOnly\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|     let url = format!("http://{}/", server.addr()); |     let url = format!("http://{}/", server.addr()); | ||||||
|     rt.block_on(client.get(&url).send()).unwrap(); |     client.get(&url).send().await.unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |     let url = format!("http://{}/2", server.addr()); | ||||||
|         request: b"\ |     client.get(&url).send().await.unwrap(); | ||||||
|             GET / HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             cookie: key=val\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|     let url = format!("http://{}/", server.addr()); |  | ||||||
|     rt.block_on(client.get(&url).send()).unwrap(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[tokio::test] | ||||||
| fn cookie_store_overwrite_existing() { | async fn cookie_store_overwrite_existing() { | ||||||
|     let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); |     let server = server::http(move |req| { | ||||||
|     let client = reqwest::r#async::Client::builder() |         async move { | ||||||
|  |             if req.uri() == "/" { | ||||||
|  |                 http::Response::builder() | ||||||
|  |                     .header("Set-Cookie", "key=val") | ||||||
|  |                     .body(Default::default()) | ||||||
|  |                     .unwrap() | ||||||
|  |             } else if req.uri() == "/2" { | ||||||
|  |                 assert_eq!(req.headers()["cookie"], "key=val"); | ||||||
|  |                 http::Response::builder() | ||||||
|  |                     .header("Set-Cookie", "key=val2") | ||||||
|  |                     .body(Default::default()) | ||||||
|  |                     .unwrap() | ||||||
|  |             } else { | ||||||
|  |                 assert_eq!(req.uri(), "/3"); | ||||||
|  |                 assert_eq!(req.headers()["cookie"], "key=val2"); | ||||||
|  |                 http::Response::default() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     let client = reqwest::Client::builder() | ||||||
|         .cookie_store(true) |         .cookie_store(true) | ||||||
|         .build() |         .build() | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET / HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Set-Cookie: key=val\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|     let url = format!("http://{}/", server.addr()); |     let url = format!("http://{}/", server.addr()); | ||||||
|     rt.block_on(client.get(&url).send()).unwrap(); |     client.get(&url).send().await.unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |     let url = format!("http://{}/2", server.addr()); | ||||||
|         request: b"\ |     client.get(&url).send().await.unwrap(); | ||||||
|             GET / HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             cookie: key=val\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Set-Cookie: key=val2\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|     let url = format!("http://{}/", server.addr()); |  | ||||||
|     rt.block_on(client.get(&url).send()).unwrap(); |  | ||||||
|  |  | ||||||
|     let server = server! { |     let url = format!("http://{}/3", server.addr()); | ||||||
|         request: b"\ |     client.get(&url).send().await.unwrap(); | ||||||
|             GET / HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             cookie: key=val2\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|     let url = format!("http://{}/", server.addr()); |  | ||||||
|     rt.block_on(client.get(&url).send()).unwrap(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[tokio::test] | ||||||
| fn cookie_store_max_age() { | async fn cookie_store_max_age() { | ||||||
|     let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); |     let server = server::http(move |req| { | ||||||
|     let client = reqwest::r#async::Client::builder() |         async move { | ||||||
|  |             assert_eq!(req.headers().get("cookie"), None); | ||||||
|  |             http::Response::builder() | ||||||
|  |                 .header("Set-Cookie", "key=val; Max-Age=0") | ||||||
|  |                 .body(Default::default()) | ||||||
|  |                 .unwrap() | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     let client = reqwest::Client::builder() | ||||||
|         .cookie_store(true) |         .cookie_store(true) | ||||||
|         .build() |         .build() | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET / HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Set-Cookie: key=val; Max-Age=0\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|     let url = format!("http://{}/", server.addr()); |     let url = format!("http://{}/", server.addr()); | ||||||
|     rt.block_on(client.get(&url).send()).unwrap(); |     client.get(&url).send().await.unwrap(); | ||||||
|  |     client.get(&url).send().await.unwrap(); | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET / HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|     let url = format!("http://{}/", server.addr()); |  | ||||||
|     rt.block_on(client.get(&url).send()).unwrap(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[tokio::test] | ||||||
| fn cookie_store_expires() { | async fn cookie_store_expires() { | ||||||
|     let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); |     let server = server::http(move |req| { | ||||||
|  |         async move { | ||||||
|  |             assert_eq!(req.headers().get("cookie"), None); | ||||||
|  |             http::Response::builder() | ||||||
|  |                 .header( | ||||||
|  |                     "Set-Cookie", | ||||||
|  |                     "key=val; Expires=Wed, 21 Oct 2015 07:28:00 GMT", | ||||||
|  |                 ) | ||||||
|  |                 .body(Default::default()) | ||||||
|  |                 .unwrap() | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     let client = reqwest::r#async::Client::builder() |     let client = reqwest::r#async::Client::builder() | ||||||
|         .cookie_store(true) |         .cookie_store(true) | ||||||
|         .build() |         .build() | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET / HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Set-Cookie: key=val; Expires=Wed, 21 Oct 2015 07:28:00 GMT\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|     let url = format!("http://{}/", server.addr()); |     let url = format!("http://{}/", server.addr()); | ||||||
|     rt.block_on(client.get(&url).send()).unwrap(); |     client.get(&url).send().await.unwrap(); | ||||||
|  |     client.get(&url).send().await.unwrap(); | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET / HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|     let url = format!("http://{}/", server.addr()); |  | ||||||
|     rt.block_on(client.get(&url).send()).unwrap(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[tokio::test] | ||||||
| fn cookie_store_path() { | async fn cookie_store_path() { | ||||||
|     let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); |     let server = server::http(move |req| { | ||||||
|  |         async move { | ||||||
|  |             if req.uri() == "/" { | ||||||
|  |                 assert_eq!(req.headers().get("cookie"), None); | ||||||
|  |                 http::Response::builder() | ||||||
|  |                     .header("Set-Cookie", "key=val; Path=/subpath") | ||||||
|  |                     .body(Default::default()) | ||||||
|  |                     .unwrap() | ||||||
|  |             } else { | ||||||
|  |                 assert_eq!(req.uri(), "/subpath"); | ||||||
|  |                 assert_eq!(req.headers()["cookie"], "key=val"); | ||||||
|  |                 http::Response::default() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     let client = reqwest::r#async::Client::builder() |     let client = reqwest::r#async::Client::builder() | ||||||
|         .cookie_store(true) |         .cookie_store(true) | ||||||
|         .build() |         .build() | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET / HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Set-Cookie: key=val; Path=/subpath\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|     let url = format!("http://{}/", server.addr()); |     let url = format!("http://{}/", server.addr()); | ||||||
|     rt.block_on(client.get(&url).send()).unwrap(); |     client.get(&url).send().await.unwrap(); | ||||||
|  |     client.get(&url).send().await.unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET / HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|     let url = format!("http://{}/", server.addr()); |  | ||||||
|     rt.block_on(client.get(&url).send()).unwrap(); |  | ||||||
|  |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET /subpath HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             cookie: key=val\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|     let url = format!("http://{}/subpath", server.addr()); |     let url = format!("http://{}/subpath", server.addr()); | ||||||
|     rt.block_on(client.get(&url).send()).unwrap(); |     client.get(&url).send().await.unwrap(); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										269
									
								
								tests/gzip.rs
									
									
									
									
									
								
							
							
						
						
									
										269
									
								
								tests/gzip.rs
									
									
									
									
									
								
							| @@ -1,13 +1,101 @@ | |||||||
| #[macro_use] |  | ||||||
| mod support; | mod support; | ||||||
|  | use support::*; | ||||||
|  |  | ||||||
| use std::io::Write; | use std::io::Write; | ||||||
| use std::time::Duration; |  | ||||||
|  |  | ||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn test_gzip_response() { | async fn gzip_response() { | ||||||
|     let content: String = (0..50).into_iter().map(|i| format!("test {}", i)).collect(); |     gzip_case(10_000, 4096).await; | ||||||
|     let chunk_size = content.len() / 3; | } | ||||||
|  |  | ||||||
|  | #[tokio::test] | ||||||
|  | async fn gzip_single_byte_chunks() { | ||||||
|  |     gzip_case(10, 1).await; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[tokio::test] | ||||||
|  | async fn test_gzip_empty_body() { | ||||||
|  |     let server = server::http(move |req| { | ||||||
|  |         async move { | ||||||
|  |             assert_eq!(req.method(), "HEAD"); | ||||||
|  |  | ||||||
|  |             http::Response::builder() | ||||||
|  |                 .header("content-encoding", "gzip") | ||||||
|  |                 .header("content-length", 100) | ||||||
|  |                 .body(Default::default()) | ||||||
|  |                 .unwrap() | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     let client = reqwest::Client::new(); | ||||||
|  |     let res = client | ||||||
|  |         .head(&format!("http://{}/gzip", server.addr())) | ||||||
|  |         .send() | ||||||
|  |         .await | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|  |     let body = res.text().await.unwrap(); | ||||||
|  |  | ||||||
|  |     assert_eq!(body, ""); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[tokio::test] | ||||||
|  | async fn test_accept_header_is_not_changed_if_set() { | ||||||
|  |     let server = server::http(move |req| { | ||||||
|  |         async move { | ||||||
|  |             assert_eq!(req.headers()["accept"], "application/json"); | ||||||
|  |             assert_eq!(req.headers()["accept-encoding"], "gzip"); | ||||||
|  |             http::Response::default() | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     let client = reqwest::Client::new(); | ||||||
|  |  | ||||||
|  |     let res = client | ||||||
|  |         .get(&format!("http://{}/accept", server.addr())) | ||||||
|  |         .header( | ||||||
|  |             reqwest::header::ACCEPT, | ||||||
|  |             reqwest::header::HeaderValue::from_static("application/json"), | ||||||
|  |         ) | ||||||
|  |         .send() | ||||||
|  |         .await | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|  |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[tokio::test] | ||||||
|  | async fn test_accept_encoding_header_is_not_changed_if_set() { | ||||||
|  |     let server = server::http(move |req| { | ||||||
|  |         async move { | ||||||
|  |             assert_eq!(req.headers()["accept"], "*/*"); | ||||||
|  |             assert_eq!(req.headers()["accept-encoding"], "identity"); | ||||||
|  |             http::Response::default() | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     let client = reqwest::Client::new(); | ||||||
|  |  | ||||||
|  |     let res = client | ||||||
|  |         .get(&format!("http://{}/accept-encoding", server.addr())) | ||||||
|  |         .header( | ||||||
|  |             reqwest::header::ACCEPT_ENCODING, | ||||||
|  |             reqwest::header::HeaderValue::from_static("identity"), | ||||||
|  |         ) | ||||||
|  |         .send() | ||||||
|  |         .await | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|  |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async fn gzip_case(response_size: usize, chunk_size: usize) { | ||||||
|  |     use futures_util::stream::StreamExt; | ||||||
|  |  | ||||||
|  |     let content: String = (0..response_size) | ||||||
|  |         .into_iter() | ||||||
|  |         .map(|i| format!("test {}", i)) | ||||||
|  |         .collect(); | ||||||
|     let mut encoder = libflate::gzip::Encoder::new(Vec::new()).unwrap(); |     let mut encoder = libflate::gzip::Encoder::new(Vec::new()).unwrap(); | ||||||
|     match encoder.write(content.as_bytes()) { |     match encoder.write(content.as_bytes()) { | ||||||
|         Ok(n) => assert!(n > 0, "Failed to write to encoder."), |         Ok(n) => assert!(n > 0, "Failed to write to encoder."), | ||||||
| @@ -28,147 +116,38 @@ async fn test_gzip_response() { | |||||||
|     .into_bytes(); |     .into_bytes(); | ||||||
|     response.extend(&gzipped_content); |     response.extend(&gzipped_content); | ||||||
|  |  | ||||||
|     let server = server! { |     let server = server::http(move |req| { | ||||||
|         request: b"\ |         assert_eq!(req.headers()["accept-encoding"], "gzip"); | ||||||
|             GET /gzip HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         chunk_size: chunk_size, |  | ||||||
|         write_timeout: Duration::from_millis(10), |  | ||||||
|         response: response |  | ||||||
|     }; |  | ||||||
|     let url = format!("http://{}/gzip", server.addr()); |  | ||||||
|     let res = reqwest::get(&url).await.unwrap(); |  | ||||||
|  |  | ||||||
|     let body = res.text().await.unwrap(); |         let gzipped = gzipped_content.clone(); | ||||||
|  |         async move { | ||||||
|  |             let len = gzipped.len(); | ||||||
|  |             let stream = futures_util::stream::unfold((gzipped, 0), move |(gzipped, pos)| { | ||||||
|  |                 async move { | ||||||
|  |                     let chunk = gzipped.chunks(chunk_size).nth(pos)?.to_vec(); | ||||||
|  |  | ||||||
|  |                     Some((chunk, (gzipped, pos + 1))) | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             let body = hyper::Body::wrap_stream(stream.map(Ok::<_, std::convert::Infallible>)); | ||||||
|  |  | ||||||
|  |             http::Response::builder() | ||||||
|  |                 .header("content-encoding", "gzip") | ||||||
|  |                 .header("content-length", len) | ||||||
|  |                 .body(body) | ||||||
|  |                 .unwrap() | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     let client = reqwest::Client::new(); | ||||||
|  |  | ||||||
|  |     let res = client | ||||||
|  |         .get(&format!("http://{}/gzip", server.addr())) | ||||||
|  |         .send() | ||||||
|  |         .await | ||||||
|  |         .expect("response"); | ||||||
|  |  | ||||||
|  |     let body = res.text().await.expect("text"); | ||||||
|     assert_eq!(body, content); |     assert_eq!(body, content); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[tokio::test] |  | ||||||
| async fn test_gzip_empty_body() { |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             HEAD /gzip HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: test-accept\r\n\ |  | ||||||
|             Content-Encoding: gzip\r\n\ |  | ||||||
|             Content-Length: 100\r\n\ |  | ||||||
|             \r\n" |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let client = reqwest::Client::new(); |  | ||||||
|     let res = client |  | ||||||
|         .head(&format!("http://{}/gzip", server.addr())) |  | ||||||
|         .send() |  | ||||||
|         .await |  | ||||||
|         .unwrap(); |  | ||||||
|  |  | ||||||
|     let body = res.text().await.unwrap(); |  | ||||||
|  |  | ||||||
|     assert_eq!(body, ""); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[tokio::test] |  | ||||||
| async fn test_gzip_invalid_body() { |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET /gzip HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: test-accept\r\n\ |  | ||||||
|             Content-Encoding: gzip\r\n\ |  | ||||||
|             Content-Length: 100\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             0" |  | ||||||
|     }; |  | ||||||
|     let url = format!("http://{}/gzip", server.addr()); |  | ||||||
|     let res = reqwest::get(&url).await.unwrap(); |  | ||||||
|     // this tests that the request.send() didn't error, but that the error |  | ||||||
|     // is in reading the body |  | ||||||
|  |  | ||||||
|     res.text().await.unwrap_err(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[tokio::test] |  | ||||||
| async fn test_accept_header_is_not_changed_if_set() { |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET /accept HTTP/1.1\r\n\ |  | ||||||
|             accept: application/json\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: test-accept\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|     let client = reqwest::Client::new(); |  | ||||||
|  |  | ||||||
|     let res = client |  | ||||||
|         .get(&format!("http://{}/accept", server.addr())) |  | ||||||
|         .header( |  | ||||||
|             reqwest::header::ACCEPT, |  | ||||||
|             reqwest::header::HeaderValue::from_static("application/json"), |  | ||||||
|         ) |  | ||||||
|         .send() |  | ||||||
|         .await |  | ||||||
|         .unwrap(); |  | ||||||
|  |  | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[tokio::test] |  | ||||||
| async fn test_accept_encoding_header_is_not_changed_if_set() { |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET /accept-encoding HTTP/1.1\r\n\ |  | ||||||
|             accept-encoding: identity\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: test-accept-encoding\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|     let client = reqwest::Client::new(); |  | ||||||
|  |  | ||||||
|     let res = client |  | ||||||
|         .get(&format!("http://{}/accept-encoding", server.addr())) |  | ||||||
|         .header( |  | ||||||
|             reqwest::header::ACCEPT_ENCODING, |  | ||||||
|             reqwest::header::HeaderValue::from_static("identity"), |  | ||||||
|         ) |  | ||||||
|         .send() |  | ||||||
|         .await |  | ||||||
|         .unwrap(); |  | ||||||
|  |  | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| #[macro_use] |  | ||||||
| mod support; | mod support; | ||||||
|  | use support::*; | ||||||
|  |  | ||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn text_part() { | async fn text_part() { | ||||||
| @@ -17,25 +17,26 @@ async fn text_part() { | |||||||
|         form.boundary() |         form.boundary() | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|     let server = server! { |     let ct = format!("multipart/form-data; boundary={}", form.boundary()); | ||||||
|         request: format!("\ |  | ||||||
|             POST /multipart/1 HTTP/1.1\r\n\ |     let server = server::http(move |mut req| { | ||||||
|             content-type: multipart/form-data; boundary={}\r\n\ |         let ct = ct.clone(); | ||||||
|             content-length: 125\r\n\ |         let expected_body = expected_body.clone(); | ||||||
|             user-agent: $USERAGENT\r\n\ |         async move { | ||||||
|             accept: */*\r\n\ |             assert_eq!(req.method(), "POST"); | ||||||
|             accept-encoding: gzip\r\n\ |             assert_eq!(req.headers()["content-type"], ct); | ||||||
|             host: $HOST\r\n\ |             assert_eq!(req.headers()["content-length"], "125"); | ||||||
|             \r\n\ |  | ||||||
|             {}\ |             let mut full: Vec<u8> = Vec::new(); | ||||||
|             ", form.boundary(), expected_body), |             while let Some(item) = req.body_mut().next().await { | ||||||
|         response: b"\ |                 full.extend(&*item.unwrap()); | ||||||
|             HTTP/1.1 200 OK\r\n\ |             } | ||||||
|             Server: multipart\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |             assert_eq!(full, expected_body.as_bytes()); | ||||||
|             \r\n\ |  | ||||||
|             " |             http::Response::default() | ||||||
|     }; |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     let url = format!("http://{}/multipart/1", server.addr()); |     let url = format!("http://{}/multipart/1", server.addr()); | ||||||
|  |  | ||||||
| @@ -50,9 +51,74 @@ async fn text_part() { | |||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[tokio::test] | ||||||
|  | async fn stream_part() { | ||||||
|  |     use futures_util::{future, stream}; | ||||||
|  |  | ||||||
|  |     let _ = env_logger::try_init(); | ||||||
|  |  | ||||||
|  |     let stream = reqwest::Body::wrap_stream(stream::once(future::ready(Ok::<_, reqwest::Error>( | ||||||
|  |         "part1 part2".to_owned(), | ||||||
|  |     )))); | ||||||
|  |     let part = reqwest::multipart::Part::stream(stream); | ||||||
|  |  | ||||||
|  |     let form = reqwest::multipart::Form::new() | ||||||
|  |         .text("foo", "bar") | ||||||
|  |         .part("part_stream", part); | ||||||
|  |  | ||||||
|  |     let expected_body = format!( | ||||||
|  |         "\ | ||||||
|  |          --{0}\r\n\ | ||||||
|  |          Content-Disposition: form-data; name=\"foo\"\r\n\ | ||||||
|  |          \r\n\ | ||||||
|  |          bar\r\n\ | ||||||
|  |          --{0}\r\n\ | ||||||
|  |          Content-Disposition: form-data; name=\"part_stream\"\r\n\ | ||||||
|  |          \r\n\ | ||||||
|  |          part1 part2\r\n\ | ||||||
|  |          --{0}--\r\n\ | ||||||
|  |          ", | ||||||
|  |         form.boundary() | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     let ct = format!("multipart/form-data; boundary={}", form.boundary()); | ||||||
|  |  | ||||||
|  |     let server = server::http(move |mut req| { | ||||||
|  |         let ct = ct.clone(); | ||||||
|  |         let expected_body = expected_body.clone(); | ||||||
|  |         async move { | ||||||
|  |             assert_eq!(req.method(), "POST"); | ||||||
|  |             assert_eq!(req.headers()["content-type"], ct); | ||||||
|  |             assert_eq!(req.headers()["transfer-encoding"], "chunked"); | ||||||
|  |  | ||||||
|  |             let mut full: Vec<u8> = Vec::new(); | ||||||
|  |             while let Some(item) = req.body_mut().next().await { | ||||||
|  |                 full.extend(&*item.unwrap()); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             assert_eq!(full, expected_body.as_bytes()); | ||||||
|  |  | ||||||
|  |             http::Response::default() | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     let url = format!("http://{}/multipart/1", server.addr()); | ||||||
|  |  | ||||||
|  |     let client = reqwest::Client::new(); | ||||||
|  |  | ||||||
|  |     let res = client | ||||||
|  |         .post(&url) | ||||||
|  |         .multipart(form) | ||||||
|  |         .send() | ||||||
|  |         .await | ||||||
|  |         .expect("Failed to post multipart"); | ||||||
|  |     assert_eq!(res.url().as_str(), &url); | ||||||
|  |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|  | } | ||||||
|  |  | ||||||
| #[cfg(feature = "blocking")] | #[cfg(feature = "blocking")] | ||||||
| #[test] | #[test] | ||||||
| fn file() { | fn blocking_file_part() { | ||||||
|     let _ = env_logger::try_init(); |     let _ = env_logger::try_init(); | ||||||
|  |  | ||||||
|     let form = reqwest::blocking::multipart::Form::new() |     let form = reqwest::blocking::multipart::Form::new() | ||||||
| @@ -73,25 +139,30 @@ fn file() { | |||||||
|         fcontents |         fcontents | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|     let server = server! { |     let ct = format!("multipart/form-data; boundary={}", form.boundary()); | ||||||
|         request: format!("\ |  | ||||||
|             POST /multipart/2 HTTP/1.1\r\n\ |     let server = server::http(move |mut req| { | ||||||
|             content-type: multipart/form-data; boundary={}\r\n\ |         let ct = ct.clone(); | ||||||
|             content-length: {}\r\n\ |         let expected_body = expected_body.clone(); | ||||||
|             user-agent: $USERAGENT\r\n\ |         async move { | ||||||
|             accept: */*\r\n\ |             assert_eq!(req.method(), "POST"); | ||||||
|             accept-encoding: gzip\r\n\ |             assert_eq!(req.headers()["content-type"], ct); | ||||||
|             host: $HOST\r\n\ |             // files know their exact size | ||||||
|             \r\n\ |             assert_eq!( | ||||||
|             {}\ |                 req.headers()["content-length"], | ||||||
|             ", form.boundary(), expected_body.len(), expected_body), |                 expected_body.len().to_string() | ||||||
|         response: b"\ |             ); | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: multipart\r\n\ |             let mut full: Vec<u8> = Vec::new(); | ||||||
|             Content-Length: 0\r\n\ |             while let Some(item) = req.body_mut().next().await { | ||||||
|             \r\n\ |                 full.extend(&*item.unwrap()); | ||||||
|             " |             } | ||||||
|     }; |  | ||||||
|  |             assert_eq!(full, expected_body.as_bytes()); | ||||||
|  |  | ||||||
|  |             http::Response::default() | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     let url = format!("http://{}/multipart/2", server.addr()); |     let url = format!("http://{}/multipart/2", server.addr()); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										178
									
								
								tests/proxy.rs
									
									
									
									
									
								
							
							
						
						
									
										178
									
								
								tests/proxy.rs
									
									
									
									
									
								
							| @@ -1,30 +1,21 @@ | |||||||
| #[macro_use] |  | ||||||
| mod support; | mod support; | ||||||
|  | use support::*; | ||||||
|  |  | ||||||
| use std::env; | use std::env; | ||||||
|  |  | ||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn http_proxy() { | async fn http_proxy() { | ||||||
|     let server = server! { |     let url = "http://hyper.rs/prox"; | ||||||
|         request: b"\ |     let server = server::http(move |req| { | ||||||
|             GET http://hyper.rs/prox HTTP/1.1\r\n\ |         assert_eq!(req.method(), "GET"); | ||||||
|             user-agent: $USERAGENT\r\n\ |         assert_eq!(req.uri(), url); | ||||||
|             accept: */*\r\n\ |         assert_eq!(req.headers()["host"], "hyper.rs"); | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: hyper.rs\r\n\ |         async { http::Response::default() } | ||||||
|             \r\n\ |     }); | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: proxied\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let proxy = format!("http://{}", server.addr()); |     let proxy = format!("http://{}", server.addr()); | ||||||
|  |  | ||||||
|     let url = "http://hyper.rs/prox"; |  | ||||||
|     let res = reqwest::Client::builder() |     let res = reqwest::Client::builder() | ||||||
|         .proxy(reqwest::Proxy::http(&proxy).unwrap()) |         .proxy(reqwest::Proxy::http(&proxy).unwrap()) | ||||||
|         .build() |         .build() | ||||||
| @@ -36,35 +27,25 @@ async fn http_proxy() { | |||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), url); |     assert_eq!(res.url().as_str(), url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::SERVER).unwrap(), |  | ||||||
|         &"proxied" |  | ||||||
|     ); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn http_proxy_basic_auth() { | async fn http_proxy_basic_auth() { | ||||||
|     let server = server! { |     let url = "http://hyper.rs/prox"; | ||||||
|         request: b"\ |     let server = server::http(move |req| { | ||||||
|             GET http://hyper.rs/prox HTTP/1.1\r\n\ |         assert_eq!(req.method(), "GET"); | ||||||
|             user-agent: $USERAGENT\r\n\ |         assert_eq!(req.uri(), url); | ||||||
|             accept: */*\r\n\ |         assert_eq!(req.headers()["host"], "hyper.rs"); | ||||||
|             accept-encoding: gzip\r\n\ |         assert_eq!( | ||||||
|             proxy-authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\r\n\ |             req.headers()["proxy-authorization"], | ||||||
|             host: hyper.rs\r\n\ |             "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" | ||||||
|             \r\n\ |         ); | ||||||
|             ", |  | ||||||
|         response: b"\ |         async { http::Response::default() } | ||||||
|             HTTP/1.1 200 OK\r\n\ |     }); | ||||||
|             Server: proxied\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let proxy = format!("http://{}", server.addr()); |     let proxy = format!("http://{}", server.addr()); | ||||||
|  |  | ||||||
|     let url = "http://hyper.rs/prox"; |  | ||||||
|     let res = reqwest::Client::builder() |     let res = reqwest::Client::builder() | ||||||
|         .proxy( |         .proxy( | ||||||
|             reqwest::Proxy::http(&proxy) |             reqwest::Proxy::http(&proxy) | ||||||
| @@ -80,35 +61,25 @@ async fn http_proxy_basic_auth() { | |||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), url); |     assert_eq!(res.url().as_str(), url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::SERVER).unwrap(), |  | ||||||
|         &"proxied" |  | ||||||
|     ); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn http_proxy_basic_auth_parsed() { | async fn http_proxy_basic_auth_parsed() { | ||||||
|     let server = server! { |     let url = "http://hyper.rs/prox"; | ||||||
|         request: b"\ |     let server = server::http(move |req| { | ||||||
|             GET http://hyper.rs/prox HTTP/1.1\r\n\ |         assert_eq!(req.method(), "GET"); | ||||||
|             user-agent: $USERAGENT\r\n\ |         assert_eq!(req.uri(), url); | ||||||
|             accept: */*\r\n\ |         assert_eq!(req.headers()["host"], "hyper.rs"); | ||||||
|             accept-encoding: gzip\r\n\ |         assert_eq!( | ||||||
|             proxy-authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\r\n\ |             req.headers()["proxy-authorization"], | ||||||
|             host: hyper.rs\r\n\ |             "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" | ||||||
|             \r\n\ |         ); | ||||||
|             ", |  | ||||||
|         response: b"\ |         async { http::Response::default() } | ||||||
|             HTTP/1.1 200 OK\r\n\ |     }); | ||||||
|             Server: proxied\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let proxy = format!("http://Aladdin:open sesame@{}", server.addr()); |     let proxy = format!("http://Aladdin:open sesame@{}", server.addr()); | ||||||
|  |  | ||||||
|     let url = "http://hyper.rs/prox"; |  | ||||||
|     let res = reqwest::Client::builder() |     let res = reqwest::Client::builder() | ||||||
|         .proxy(reqwest::Proxy::http(&proxy).unwrap()) |         .proxy(reqwest::Proxy::http(&proxy).unwrap()) | ||||||
|         .build() |         .build() | ||||||
| @@ -120,30 +91,16 @@ async fn http_proxy_basic_auth_parsed() { | |||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), url); |     assert_eq!(res.url().as_str(), url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::SERVER).unwrap(), |  | ||||||
|         &"proxied" |  | ||||||
|     ); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn test_no_proxy() { | async fn test_no_proxy() { | ||||||
|     let server = server! { |     let server = server::http(move |req| { | ||||||
|         request: b"\ |         assert_eq!(req.method(), "GET"); | ||||||
|             GET /4 HTTP/1.1\r\n\ |         assert_eq!(req.uri(), "/4"); | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |         async { http::Response::default() } | ||||||
|             accept-encoding: gzip\r\n\ |     }); | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: test\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|     let proxy = format!("http://{}", server.addr()); |     let proxy = format!("http://{}", server.addr()); | ||||||
|     let url = format!("http://{}/4", server.addr()); |     let url = format!("http://{}/4", server.addr()); | ||||||
|  |  | ||||||
| @@ -164,28 +121,20 @@ async fn test_no_proxy() { | |||||||
|  |  | ||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn test_using_system_proxy() { | async fn test_using_system_proxy() { | ||||||
|     let server = server! { |     let url = "http://hyper.rs/prox"; | ||||||
|         request: b"\ |     let server = server::http(move |req| { | ||||||
|             GET http://hyper.rs/prox HTTP/1.1\r\n\ |         assert_eq!(req.method(), "GET"); | ||||||
|             user-agent: $USERAGENT\r\n\ |         assert_eq!(req.uri(), url); | ||||||
|             accept: */*\r\n\ |         assert_eq!(req.headers()["host"], "hyper.rs"); | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: hyper.rs\r\n\ |         async { http::Response::default() } | ||||||
|             \r\n\ |     }); | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: proxied\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|     // save system setting first. |     // save system setting first. | ||||||
|     let system_proxy = env::var("http_proxy"); |     let system_proxy = env::var("http_proxy"); | ||||||
|     // set-up http proxy. |     // set-up http proxy. | ||||||
|     env::set_var("http_proxy", format!("http://{}", server.addr())); |     env::set_var("http_proxy", format!("http://{}", server.addr())); | ||||||
|  |  | ||||||
|     let url = "http://hyper.rs/prox"; |  | ||||||
|     let res = reqwest::Client::builder() |     let res = reqwest::Client::builder() | ||||||
|         .use_sys_proxy() |         .use_sys_proxy() | ||||||
|         .build() |         .build() | ||||||
| @@ -197,10 +146,6 @@ async fn test_using_system_proxy() { | |||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), url); |     assert_eq!(res.url().as_str(), url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::SERVER).unwrap(), |  | ||||||
|         &"proxied" |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     // reset user setting. |     // reset user setting. | ||||||
|     match system_proxy { |     match system_proxy { | ||||||
| @@ -208,3 +153,30 @@ async fn test_using_system_proxy() { | |||||||
|         Ok(proxy) => env::set_var("http_proxy", proxy), |         Ok(proxy) => env::set_var("http_proxy", proxy), | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[tokio::test] | ||||||
|  | async fn http_over_http() { | ||||||
|  |     let url = "http://hyper.rs/prox"; | ||||||
|  |  | ||||||
|  |     let server = server::http(move |req| { | ||||||
|  |         assert_eq!(req.method(), "GET"); | ||||||
|  |         assert_eq!(req.uri(), url); | ||||||
|  |         assert_eq!(req.headers()["host"], "hyper.rs"); | ||||||
|  |  | ||||||
|  |         async { http::Response::default() } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     let proxy = format!("http://{}", server.addr()); | ||||||
|  |  | ||||||
|  |     let res = reqwest::Client::builder() | ||||||
|  |         .proxy(reqwest::Proxy::http(&proxy).unwrap()) | ||||||
|  |         .build() | ||||||
|  |         .unwrap() | ||||||
|  |         .get(url) | ||||||
|  |         .send() | ||||||
|  |         .await | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|  |     assert_eq!(res.url().as_str(), url); | ||||||
|  |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,47 +1,32 @@ | |||||||
| #[macro_use] |  | ||||||
| mod support; | mod support; | ||||||
|  | use support::*; | ||||||
|  |  | ||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn test_redirect_301_and_302_and_303_changes_post_to_get() { | async fn test_redirect_301_and_302_and_303_changes_post_to_get() { | ||||||
|     let client = reqwest::Client::new(); |     let client = reqwest::Client::new(); | ||||||
|     let codes = [301, 302, 303]; |     let codes = [301u16, 302, 303]; | ||||||
|  |  | ||||||
|     for code in codes.iter() { |     for &code in codes.iter() { | ||||||
|         let redirect = server! { |         let redirect = server::http(move |req| { | ||||||
|             request: format!("\ |             async move { | ||||||
|                 POST /{} HTTP/1.1\r\n\ |                 if req.method() == "POST" { | ||||||
|                 user-agent: $USERAGENT\r\n\ |                     assert_eq!(req.uri(), &*format!("/{}", code)); | ||||||
|                 accept: */*\r\n\ |                     http::Response::builder() | ||||||
|                 accept-encoding: gzip\r\n\ |                         .status(code) | ||||||
|                 host: $HOST\r\n\ |                         .header("location", "/dst") | ||||||
|                 \r\n\ |                         .header("server", "test-redirect") | ||||||
|                 ", code), |                         .body(Default::default()) | ||||||
|             response: format!("\ |                         .unwrap() | ||||||
|                 HTTP/1.1 {} reason\r\n\ |                 } else { | ||||||
|                 Server: test-redirect\r\n\ |                     assert_eq!(req.method(), "GET"); | ||||||
|                 Content-Length: 0\r\n\ |  | ||||||
|                 Location: /dst\r\n\ |  | ||||||
|                 Connection: close\r\n\ |  | ||||||
|                 \r\n\ |  | ||||||
|                 ", code) |  | ||||||
|                 ; |  | ||||||
|  |  | ||||||
|             request: format!("\ |                     http::Response::builder() | ||||||
|                 GET /dst HTTP/1.1\r\n\ |                         .header("server", "test-dst") | ||||||
|                 user-agent: $USERAGENT\r\n\ |                         .body(Default::default()) | ||||||
|                 accept: */*\r\n\ |                         .unwrap() | ||||||
|                 accept-encoding: gzip\r\n\ |                 } | ||||||
|                 referer: http://$HOST/{}\r\n\ |             } | ||||||
|                 host: $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 url = format!("http://{}/{}", redirect.addr(), code); |         let url = format!("http://{}/{}", redirect.addr(), code); | ||||||
|         let dst = format!("http://{}/{}", redirect.addr(), "dst"); |         let dst = format!("http://{}/{}", redirect.addr(), "dst"); | ||||||
| @@ -58,43 +43,28 @@ async fn test_redirect_301_and_302_and_303_changes_post_to_get() { | |||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn test_redirect_307_and_308_tries_to_get_again() { | async fn test_redirect_307_and_308_tries_to_get_again() { | ||||||
|     let client = reqwest::Client::new(); |     let client = reqwest::Client::new(); | ||||||
|     let codes = [307, 308]; |     let codes = [307u16, 308]; | ||||||
|     for code in codes.iter() { |     for &code in codes.iter() { | ||||||
|         let redirect = server! { |         let redirect = server::http(move |req| { | ||||||
|             request: format!("\ |             async move { | ||||||
|                 GET /{} HTTP/1.1\r\n\ |                 assert_eq!(req.method(), "GET"); | ||||||
|                 user-agent: $USERAGENT\r\n\ |                 if req.uri() == &*format!("/{}", code) { | ||||||
|                 accept: */*\r\n\ |                     http::Response::builder() | ||||||
|                 accept-encoding: gzip\r\n\ |                         .status(code) | ||||||
|                 host: $HOST\r\n\ |                         .header("location", "/dst") | ||||||
|                 \r\n\ |                         .header("server", "test-redirect") | ||||||
|                 ", code), |                         .body(Default::default()) | ||||||
|             response: format!("\ |                         .unwrap() | ||||||
|                 HTTP/1.1 {} reason\r\n\ |                 } else { | ||||||
|                 Server: test-redirect\r\n\ |                     assert_eq!(req.uri(), "/dst"); | ||||||
|                 Content-Length: 0\r\n\ |  | ||||||
|                 Location: /dst\r\n\ |  | ||||||
|                 Connection: close\r\n\ |  | ||||||
|                 \r\n\ |  | ||||||
|                 ", code) |  | ||||||
|                 ; |  | ||||||
|  |  | ||||||
|             request: format!("\ |                     http::Response::builder() | ||||||
|                 GET /dst HTTP/1.1\r\n\ |                         .header("server", "test-dst") | ||||||
|                 user-agent: $USERAGENT\r\n\ |                         .body(Default::default()) | ||||||
|                 accept: */*\r\n\ |                         .unwrap() | ||||||
|                 accept-encoding: gzip\r\n\ |                 } | ||||||
|                 referer: http://$HOST/{}\r\n\ |             } | ||||||
|                 host: $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 url = format!("http://{}/{}", redirect.addr(), code); |         let url = format!("http://{}/{}", redirect.addr(), code); | ||||||
|         let dst = format!("http://{}/{}", redirect.addr(), "dst"); |         let dst = format!("http://{}/{}", redirect.addr(), "dst"); | ||||||
| @@ -111,47 +81,32 @@ async fn test_redirect_307_and_308_tries_to_get_again() { | |||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn test_redirect_307_and_308_tries_to_post_again() { | async fn test_redirect_307_and_308_tries_to_post_again() { | ||||||
|     let client = reqwest::Client::new(); |     let client = reqwest::Client::new(); | ||||||
|     let codes = [307, 308]; |     let codes = [307u16, 308]; | ||||||
|     for code in codes.iter() { |     for &code in codes.iter() { | ||||||
|         let redirect = server! { |         let redirect = server::http(move |mut req| { | ||||||
|             request: format!("\ |             async move { | ||||||
|                 POST /{} HTTP/1.1\r\n\ |                 assert_eq!(req.method(), "POST"); | ||||||
|                 user-agent: $USERAGENT\r\n\ |  | ||||||
|                 accept: */*\r\n\ |  | ||||||
|                 accept-encoding: gzip\r\n\ |  | ||||||
|                 host: $HOST\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!("\ |                 let data = req.body_mut().next().await.unwrap().unwrap(); | ||||||
|                 POST /dst HTTP/1.1\r\n\ |                 assert_eq!(&*data, b"Hello"); | ||||||
|                 user-agent: $USERAGENT\r\n\ |  | ||||||
|                 accept: */*\r\n\ |                 if req.uri() == &*format!("/{}", code) { | ||||||
|                 accept-encoding: gzip\r\n\ |                     http::Response::builder() | ||||||
|                 referer: http://$HOST/{}\r\n\ |                         .status(code) | ||||||
|                 host: $HOST\r\n\ |                         .header("location", "/dst") | ||||||
|                 content-length: 5\r\n\ |                         .header("server", "test-redirect") | ||||||
|                 \r\n\ |                         .body(Default::default()) | ||||||
|                 Hello\ |                         .unwrap() | ||||||
|                 ", code), |                 } else { | ||||||
|             response: b"\ |                     assert_eq!(req.uri(), "/dst"); | ||||||
|                 HTTP/1.1 200 OK\r\n\ |  | ||||||
|                 Server: test-dst\r\n\ |                     http::Response::builder() | ||||||
|                 Content-Length: 0\r\n\ |                         .header("server", "test-dst") | ||||||
|                 \r\n\ |                         .body(Default::default()) | ||||||
|                 " |                         .unwrap() | ||||||
|         }; |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|         let url = format!("http://{}/{}", redirect.addr(), code); |         let url = format!("http://{}/{}", redirect.addr(), code); | ||||||
|         let dst = format!("http://{}/{}", redirect.addr(), "dst"); |         let dst = format!("http://{}/{}", redirect.addr(), "dst"); | ||||||
| @@ -169,30 +124,25 @@ async fn test_redirect_307_and_308_tries_to_post_again() { | |||||||
| #[test] | #[test] | ||||||
| fn test_redirect_307_does_not_try_if_reader_cannot_reset() { | fn test_redirect_307_does_not_try_if_reader_cannot_reset() { | ||||||
|     let client = reqwest::blocking::Client::new(); |     let client = reqwest::blocking::Client::new(); | ||||||
|     let codes = [307, 308]; |     let codes = [307u16, 308]; | ||||||
|     for &code in codes.iter() { |     for &code in codes.iter() { | ||||||
|         let redirect = server! { |         let redirect = server::http(move |mut req| { | ||||||
|             request: format!("\ |             async move { | ||||||
|                 POST /{} HTTP/1.1\r\n\ |                 assert_eq!(req.method(), "POST"); | ||||||
|                 user-agent: $USERAGENT\r\n\ |                 assert_eq!(req.uri(), &*format!("/{}", code)); | ||||||
|                 accept: */*\r\n\ |                 assert_eq!(req.headers()["transfer-encoding"], "chunked"); | ||||||
|                 accept-encoding: gzip\r\n\ |  | ||||||
|                 host: $HOST\r\n\ |                 let data = req.body_mut().next().await.unwrap().unwrap(); | ||||||
|                 transfer-encoding: chunked\r\n\ |                 assert_eq!(&*data, b"Hello"); | ||||||
|                 \r\n\ |  | ||||||
|                 5\r\n\ |                 http::Response::builder() | ||||||
|                 Hello\r\n\ |                     .status(code) | ||||||
|                 0\r\n\r\n\ |                     .header("location", "/dst") | ||||||
|                 ", code), |                     .header("server", "test-redirect") | ||||||
|             response: format!("\ |                     .body(Default::default()) | ||||||
|                 HTTP/1.1 {} reason\r\n\ |                     .unwrap() | ||||||
|                 Server: test-redirect\r\n\ |             } | ||||||
|                 Content-Length: 0\r\n\ |         }); | ||||||
|                 Location: /dst\r\n\ |  | ||||||
|                 Connection: close\r\n\ |  | ||||||
|                 \r\n\ |  | ||||||
|                 ", code) |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         let url = format!("http://{}/{}", redirect.addr(), code); |         let url = format!("http://{}/{}", redirect.addr(), code); | ||||||
|         let res = client |         let res = client | ||||||
| @@ -201,50 +151,46 @@ fn test_redirect_307_does_not_try_if_reader_cannot_reset() { | |||||||
|             .send() |             .send() | ||||||
|             .unwrap(); |             .unwrap(); | ||||||
|         assert_eq!(res.url().as_str(), url); |         assert_eq!(res.url().as_str(), url); | ||||||
|         assert_eq!(res.status(), reqwest::StatusCode::from_u16(code).unwrap()); |         assert_eq!(res.status(), code); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn test_redirect_removes_sensitive_headers() { | async fn test_redirect_removes_sensitive_headers() { | ||||||
|     let end_server = server! { |     use tokio::sync::watch; | ||||||
|         request: b"\ |  | ||||||
|             GET /otherhost HTTP/1.1\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Server: test\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let mid_server = server! { |     let (tx, rx) = watch::channel(None); | ||||||
|         request: b"\ |  | ||||||
|             GET /sensitive HTTP/1.1\r\n\ |     let end_server = server::http(move |req| { | ||||||
|             cookie: foo=bar\r\n\ |         let mut rx = rx.clone(); | ||||||
|             user-agent: $USERAGENT\r\n\ |         async move { | ||||||
|             accept: */*\r\n\ |             assert_eq!(req.headers().get("cookie"), None); | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |             let mid_addr = rx.recv().await.unwrap().unwrap(); | ||||||
|             \r\n\ |             assert_eq!( | ||||||
|             ", |                 req.headers()["referer"], | ||||||
|         response: format!("\ |                 format!("http://{}/sensitive", mid_addr) | ||||||
|             HTTP/1.1 302 Found\r\n\ |             ); | ||||||
|             Server: test\r\n\ |             http::Response::default() | ||||||
|             Location: http://{}/otherhost\r\n\ |         } | ||||||
|             Content-Length: 0\r\n\ |     }); | ||||||
|             \r\n\ |  | ||||||
|             ", end_server.addr()) |     let end_addr = end_server.addr(); | ||||||
|     }; |  | ||||||
|  |     let mid_server = server::http(move |req| { | ||||||
|  |         async move { | ||||||
|  |             assert_eq!(req.headers()["cookie"], "foo=bar"); | ||||||
|  |             http::Response::builder() | ||||||
|  |                 .status(302) | ||||||
|  |                 .header("location", format!("http://{}/end", end_addr)) | ||||||
|  |                 .body(Default::default()) | ||||||
|  |                 .unwrap() | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     tx.broadcast(Some(mid_server.addr())).unwrap(); | ||||||
|  |  | ||||||
|     reqwest::Client::builder() |     reqwest::Client::builder() | ||||||
|         .referer(false) |  | ||||||
|         .build() |         .build() | ||||||
|         .unwrap() |         .unwrap() | ||||||
|         .get(&format!("http://{}/sensitive", mid_server.addr())) |         .get(&format!("http://{}/sensitive", mid_server.addr())) | ||||||
| @@ -259,23 +205,17 @@ async fn test_redirect_removes_sensitive_headers() { | |||||||
|  |  | ||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn test_redirect_policy_can_return_errors() { | async fn test_redirect_policy_can_return_errors() { | ||||||
|     let server = server! { |     let server = server::http(move |req| { | ||||||
|         request: b"\ |         async move { | ||||||
|             GET /loop HTTP/1.1\r\n\ |             assert_eq!(req.uri(), "/loop"); | ||||||
|             user-agent: $USERAGENT\r\n\ |             http::Response::builder() | ||||||
|             accept: */*\r\n\ |                 .status(302) | ||||||
|             accept-encoding: gzip\r\n\ |                 .header("location", "/loop") | ||||||
|             host: $HOST\r\n\ |                 .body(Default::default()) | ||||||
|             \r\n\ |                 .unwrap() | ||||||
|             ", |         } | ||||||
|         response: b"\ |     }); | ||||||
|             HTTP/1.1 302 Found\r\n\ |  | ||||||
|             Server: test\r\n\ |  | ||||||
|             Location: /loop\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|     let url = format!("http://{}/loop", server.addr()); |     let url = format!("http://{}/loop", server.addr()); | ||||||
|     let err = reqwest::get(&url).await.unwrap_err(); |     let err = reqwest::get(&url).await.unwrap_err(); | ||||||
|     assert!(err.is_redirect()); |     assert!(err.is_redirect()); | ||||||
| @@ -283,23 +223,16 @@ async fn test_redirect_policy_can_return_errors() { | |||||||
|  |  | ||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn test_redirect_policy_can_stop_redirects_without_an_error() { | async fn test_redirect_policy_can_stop_redirects_without_an_error() { | ||||||
|     let server = server! { |     let server = server::http(move |req| { | ||||||
|         request: b"\ |         async move { | ||||||
|             GET /no-redirect HTTP/1.1\r\n\ |             assert_eq!(req.uri(), "/no-redirect"); | ||||||
|             user-agent: $USERAGENT\r\n\ |             http::Response::builder() | ||||||
|             accept: */*\r\n\ |                 .status(302) | ||||||
|             accept-encoding: gzip\r\n\ |                 .header("location", "/dont") | ||||||
|             host: $HOST\r\n\ |                 .body(Default::default()) | ||||||
|             \r\n\ |                 .unwrap() | ||||||
|             ", |         } | ||||||
|         response: b"\ |     }); | ||||||
|             HTTP/1.1 302 Found\r\n\ |  | ||||||
|             Server: test-dont\r\n\ |  | ||||||
|             Location: /dont\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/no-redirect", server.addr()); |     let url = format!("http://{}/no-redirect", server.addr()); | ||||||
|  |  | ||||||
| @@ -314,48 +247,27 @@ async fn test_redirect_policy_can_stop_redirects_without_an_error() { | |||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), url); |     assert_eq!(res.url().as_str(), url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::FOUND); |     assert_eq!(res.status(), reqwest::StatusCode::FOUND); | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::SERVER).unwrap(), |  | ||||||
|         &"test-dont" |  | ||||||
|     ); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn test_referer_is_not_set_if_disabled() { | async fn test_referer_is_not_set_if_disabled() { | ||||||
|     let server = server! { |     let server = server::http(move |req| { | ||||||
|         request: b"\ |         async move { | ||||||
|             GET /no-refer HTTP/1.1\r\n\ |             if req.uri() == "/no-refer" { | ||||||
|             user-agent: $USERAGENT\r\n\ |                 http::Response::builder() | ||||||
|             accept: */*\r\n\ |                     .status(302) | ||||||
|             accept-encoding: gzip\r\n\ |                     .header("location", "/dst") | ||||||
|             host: $HOST\r\n\ |                     .body(Default::default()) | ||||||
|             \r\n\ |                     .unwrap() | ||||||
|             ", |             } else { | ||||||
|         response: b"\ |                 assert_eq!(req.uri(), "/dst"); | ||||||
|             HTTP/1.1 302 Found\r\n\ |                 assert_eq!(req.headers().get("referer"), None); | ||||||
|             Server: test-no-referer\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |                 http::Response::default() | ||||||
|             Location: /dst\r\n\ |             } | ||||||
|             Connection: close\r\n\ |         } | ||||||
|             \r\n\ |     }); | ||||||
|             " |  | ||||||
|             ; |  | ||||||
|  |  | ||||||
|         request: b"\ |  | ||||||
|             GET /dst HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\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\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|     reqwest::Client::builder() |     reqwest::Client::builder() | ||||||
|         .referer(false) |         .referer(false) | ||||||
|         .build() |         .build() | ||||||
| @@ -368,23 +280,15 @@ async fn test_referer_is_not_set_if_disabled() { | |||||||
|  |  | ||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn test_invalid_location_stops_redirect_gh484() { | async fn test_invalid_location_stops_redirect_gh484() { | ||||||
|     let server = server! { |     let server = server::http(move |_req| { | ||||||
|         request: b"\ |         async move { | ||||||
|             GET /yikes HTTP/1.1\r\n\ |             http::Response::builder() | ||||||
|             user-agent: $USERAGENT\r\n\ |                 .status(302) | ||||||
|             accept: */*\r\n\ |                 .header("location", "http://www.yikes{KABOOM}") | ||||||
|             accept-encoding: gzip\r\n\ |                 .body(Default::default()) | ||||||
|             host: $HOST\r\n\ |                 .unwrap() | ||||||
|             \r\n\ |         } | ||||||
|             ", |     }); | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 302 Found\r\n\ |  | ||||||
|             Server: test-yikes\r\n\ |  | ||||||
|             Location: http://www.yikes{KABOOM}\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             " |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/yikes", server.addr()); |     let url = format!("http://{}/yikes", server.addr()); | ||||||
|  |  | ||||||
| @@ -392,66 +296,38 @@ async fn test_invalid_location_stops_redirect_gh484() { | |||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), url); |     assert_eq!(res.url().as_str(), url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::FOUND); |     assert_eq!(res.status(), reqwest::StatusCode::FOUND); | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::SERVER).unwrap(), |  | ||||||
|         &"test-yikes" |  | ||||||
|     ); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[cfg(feature = "cookies")] | #[cfg(feature = "cookies")] | ||||||
| #[tokio::test] | #[tokio::test] | ||||||
| async fn test_redirect_302_with_set_cookies() { | async fn test_redirect_302_with_set_cookies() { | ||||||
|     let code = 302; |     let code = 302; | ||||||
|  |     let server = server::http(move |req| { | ||||||
|  |         async move { | ||||||
|  |             if req.uri() == "/302" { | ||||||
|  |                 http::Response::builder() | ||||||
|  |                     .status(302) | ||||||
|  |                     .header("location", "/dst") | ||||||
|  |                     .header("set-cookie", "key=value") | ||||||
|  |                     .body(Default::default()) | ||||||
|  |                     .unwrap() | ||||||
|  |             } else { | ||||||
|  |                 assert_eq!(req.uri(), "/dst"); | ||||||
|  |                 assert_eq!(req.headers()["cookie"], "key=value"); | ||||||
|  |                 http::Response::default() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     let url = format!("http://{}/{}", server.addr(), code); | ||||||
|  |     let dst = format!("http://{}/{}", server.addr(), "dst"); | ||||||
|  |  | ||||||
|     let client = reqwest::ClientBuilder::new() |     let client = reqwest::ClientBuilder::new() | ||||||
|         .cookie_store(true) |         .cookie_store(true) | ||||||
|         .build() |         .build() | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|     let server = server! { |  | ||||||
|         request: format!("\ |  | ||||||
|             GET /{} HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\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\ |  | ||||||
|             Set-Cookie: key=value\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", code) |  | ||||||
|             ; |  | ||||||
|  |  | ||||||
|         request: format!("\ |  | ||||||
|             GET /dst HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             referer: http://$HOST/{}\r\n\ |  | ||||||
|             cookie: key=value\r\n\ |  | ||||||
|             host: $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 url = format!("http://{}/{}", server.addr(), code); |  | ||||||
|     let dst = format!("http://{}/{}", server.addr(), "dst"); |  | ||||||
|     let res = client.get(&url).send().await.unwrap(); |     let res = client.get(&url).send().await.unwrap(); | ||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), dst); |     assert_eq!(res.url().as_str(), dst); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::SERVER).unwrap(), |  | ||||||
|         &"test-dst" |  | ||||||
|     ); |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,2 +1,6 @@ | |||||||
| #[macro_use] |  | ||||||
| pub mod server; | pub mod server; | ||||||
|  |  | ||||||
|  | // TODO: remove once done converting to new support server? | ||||||
|  | #[allow(unused)] | ||||||
|  | pub static DEFAULT_USER_AGENT: &'static str = | ||||||
|  |     concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")); | ||||||
|   | |||||||
| @@ -1,14 +1,18 @@ | |||||||
| //! A server builder helper for the integration tests. | use std::convert::Infallible; | ||||||
|  | use std::future::Future; | ||||||
| use std::io::{Read, Write}; |  | ||||||
| use std::net; | use std::net; | ||||||
| use std::sync::mpsc; | use std::sync::mpsc as std_mpsc; | ||||||
| use std::thread; | use std::thread; | ||||||
| use std::time::Duration; | use std::time::Duration; | ||||||
|  |  | ||||||
|  | use tokio::sync::oneshot; | ||||||
|  |  | ||||||
|  | pub use http::Response; | ||||||
|  |  | ||||||
| pub struct Server { | pub struct Server { | ||||||
|     addr: net::SocketAddr, |     addr: net::SocketAddr, | ||||||
|     panic_rx: mpsc::Receiver<()>, |     panic_rx: std_mpsc::Receiver<()>, | ||||||
|  |     shutdown_tx: Option<oneshot::Sender<()>>, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Server { | impl Server { | ||||||
| @@ -19,7 +23,11 @@ impl Server { | |||||||
|  |  | ||||||
| impl Drop for Server { | impl Drop for Server { | ||||||
|     fn drop(&mut self) { |     fn drop(&mut self) { | ||||||
|         if !thread::panicking() { |         if let Some(tx) = self.shutdown_tx.take() { | ||||||
|  |             let _ = tx.send(()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if !::std::thread::panicking() { | ||||||
|             self.panic_rx |             self.panic_rx | ||||||
|                 .recv_timeout(Duration::from_secs(3)) |                 .recv_timeout(Duration::from_secs(3)) | ||||||
|                 .expect("test server should not panic"); |                 .expect("test server should not panic"); | ||||||
| @@ -27,197 +35,46 @@ impl Drop for Server { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Default)] | pub fn http<F, Fut>(func: F) -> Server | ||||||
| pub struct Txn { | where | ||||||
|     pub request: Vec<u8>, |     F: Fn(http::Request<hyper::Body>) -> Fut + Clone + Send + 'static, | ||||||
|     pub response: Vec<u8>, |     Fut: Future<Output = http::Response<hyper::Body>> + Send + 'static, | ||||||
|  | { | ||||||
|     pub read_timeout: Option<Duration>, |     let srv = hyper::Server::bind(&([127, 0, 0, 1], 0).into()).serve( | ||||||
|     pub read_closes: bool, |         hyper::service::make_service_fn(move |_| { | ||||||
|     pub response_timeout: Option<Duration>, |             let func = func.clone(); | ||||||
|     pub write_timeout: Option<Duration>, |             async move { | ||||||
|     pub chunk_size: Option<usize>, |                 Ok::<_, Infallible>(hyper::service::service_fn(move |req| { | ||||||
|  |                     let fut = func(req); | ||||||
|  |                     async move { Ok::<_, Infallible>(fut.await) } | ||||||
|  |                 })) | ||||||
|             } |             } | ||||||
|  |         }), | ||||||
|  |     ); | ||||||
|  |  | ||||||
| static DEFAULT_USER_AGENT: &'static str = |     let addr = srv.local_addr(); | ||||||
|     concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")); |     let (shutdown_tx, shutdown_rx) = oneshot::channel(); | ||||||
|  |     let srv = srv.with_graceful_shutdown(async move { | ||||||
|  |         let _ = shutdown_rx.await; | ||||||
|  |     }); | ||||||
|  |  | ||||||
| pub fn spawn(txns: Vec<Txn>) -> Server { |     let (panic_tx, panic_rx) = std_mpsc::channel(); | ||||||
|     let listener = net::TcpListener::bind("127.0.0.1:0").unwrap(); |  | ||||||
|     let addr = listener.local_addr().unwrap(); |  | ||||||
|     let (panic_tx, panic_rx) = mpsc::channel(); |  | ||||||
|     let tname = format!( |     let tname = format!( | ||||||
|         "test({})-support-server", |         "test({})-support-server", | ||||||
|         thread::current().name().unwrap_or("<unknown>") |         thread::current().name().unwrap_or("<unknown>") | ||||||
|     ); |     ); | ||||||
|     thread::Builder::new().name(tname).spawn(move || { |     thread::Builder::new() | ||||||
|         'txns: for txn in txns { |         .name(tname) | ||||||
|             let mut expected = txn.request; |         .spawn(move || { | ||||||
|             let reply = txn.response; |             let mut rt = tokio::runtime::current_thread::Runtime::new().expect("rt new"); | ||||||
|             let (mut socket, _addr) = listener.accept().unwrap(); |             rt.block_on(srv).unwrap(); | ||||||
|  |  | ||||||
|             socket.set_read_timeout(Some(Duration::from_secs(5))).unwrap(); |  | ||||||
|  |  | ||||||
|             replace_expected_vars(&mut expected, addr.to_string().as_ref(), DEFAULT_USER_AGENT.as_ref()); |  | ||||||
|  |  | ||||||
|             if let Some(dur) = txn.read_timeout { |  | ||||||
|                 thread::park_timeout(dur); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             let mut buf = vec![0; expected.len() + 256]; |  | ||||||
|  |  | ||||||
|             let mut n = 0; |  | ||||||
|             while n < expected.len() { |  | ||||||
|                 match socket.read(&mut buf[n..]) { |  | ||||||
|                     Ok(0) => { |  | ||||||
|                         if !txn.read_closes { |  | ||||||
|                             panic!("server unexpected socket closed"); |  | ||||||
|                         } else { |  | ||||||
|                             continue 'txns; |  | ||||||
|                         } |  | ||||||
|                     }, |  | ||||||
|                     Ok(nread) => n += nread, |  | ||||||
|                     Err(err) => { |  | ||||||
|                         println!("server read error: {}", err); |  | ||||||
|                         break; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if txn.read_closes { |  | ||||||
|                 socket.set_read_timeout(Some(Duration::from_secs(1))).unwrap(); |  | ||||||
|                 match socket.read(&mut [0; 256]) { |  | ||||||
|                     Ok(0) => { |  | ||||||
|                         continue 'txns |  | ||||||
|                     }, |  | ||||||
|                     Ok(_) => { |  | ||||||
|                         panic!("server read expected EOF, found more bytes"); |  | ||||||
|                     }, |  | ||||||
|                     Err(err) => { |  | ||||||
|                         panic!("server read expected EOF, got error: {}", err); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             match (std::str::from_utf8(&expected), std::str::from_utf8(&buf[..n])) { |  | ||||||
|                 (Ok(expected), Ok(received)) => { |  | ||||||
|                     if expected.len() > 300 && std::env::var("REQWEST_TEST_BODY_FULL").is_err() { |  | ||||||
|                         assert_eq!( |  | ||||||
|                             expected.len(), |  | ||||||
|                             received.len(), |  | ||||||
|                             "expected len = {}, received len = {}; to skip length check and see exact contents, re-run with REQWEST_TEST_BODY_FULL=1", |  | ||||||
|                             expected.len(), |  | ||||||
|                             received.len(), |  | ||||||
|                         ); |  | ||||||
|                     } |  | ||||||
|                     assert_eq!(expected, received) |  | ||||||
|                 }, |  | ||||||
|                 _ => { |  | ||||||
|                     assert_eq!( |  | ||||||
|                         expected.len(), |  | ||||||
|                         n, |  | ||||||
|                         "expected len = {}, received len = {}", |  | ||||||
|                         expected.len(), |  | ||||||
|                         n, |  | ||||||
|                     ); |  | ||||||
|                     assert_eq!(expected, &buf[..n]) |  | ||||||
|                 }, |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if let Some(dur) = txn.response_timeout { |  | ||||||
|                 thread::park_timeout(dur); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if let Some(dur) = txn.write_timeout { |  | ||||||
|                 let headers_end = b"\r\n\r\n"; |  | ||||||
|                 let headers_end = reply.windows(headers_end.len()).position(|w| w == headers_end).unwrap() + 4; |  | ||||||
|                 socket.write_all(&reply[..headers_end]).unwrap(); |  | ||||||
|  |  | ||||||
|                 let body = &reply[headers_end..]; |  | ||||||
|  |  | ||||||
|                 if let Some(chunk_size) = txn.chunk_size { |  | ||||||
|                     for content in body.chunks(chunk_size) { |  | ||||||
|                         thread::park_timeout(dur); |  | ||||||
|                         socket.write_all(&content).unwrap(); |  | ||||||
|                     } |  | ||||||
|                 } else { |  | ||||||
|                     thread::park_timeout(dur); |  | ||||||
|                     socket.write_all(&body).unwrap(); |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 socket.write_all(&reply).unwrap(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|             let _ = panic_tx.send(()); |             let _ = panic_tx.send(()); | ||||||
|     }).expect("server thread spawn"); |  | ||||||
|  |  | ||||||
|     Server { addr, panic_rx } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn replace_expected_vars(bytes: &mut Vec<u8>, host: &[u8], ua: &[u8]) { |  | ||||||
|     // plenty horrible, but these are just tests, and gets the job done |  | ||||||
|     let mut index = 0; |  | ||||||
|     loop { |  | ||||||
|         if index == bytes.len() { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         for b in (&bytes[index..]).iter() { |  | ||||||
|             index += 1; |  | ||||||
|             if *b == b'$' { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         let has_host = (&bytes[index..]).starts_with(b"HOST"); |  | ||||||
|         if has_host { |  | ||||||
|             bytes.drain(index - 1..index + 4); |  | ||||||
|             for (i, b) in host.iter().enumerate() { |  | ||||||
|                 bytes.insert(index - 1 + i, *b); |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             let has_ua = (&bytes[index..]).starts_with(b"USERAGENT"); |  | ||||||
|             if has_ua { |  | ||||||
|                 bytes.drain(index - 1..index + 9); |  | ||||||
|                 for (i, b) in ua.iter().enumerate() { |  | ||||||
|                     bytes.insert(index - 1 + i, *b); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[macro_export] |  | ||||||
| macro_rules! server { |  | ||||||
|     ($($($f:ident: $v:expr),+);*) => ({ |  | ||||||
|         let txns = vec![ |  | ||||||
|             $(__internal__txn! { |  | ||||||
|                 $($f: $v,)+ |  | ||||||
|             }),* |  | ||||||
|         ]; |  | ||||||
|         crate::support::server::spawn(txns) |  | ||||||
|         }) |         }) | ||||||
| } |         .expect("thread spawn"); | ||||||
|  |  | ||||||
| #[macro_export] |     Server { | ||||||
| macro_rules! __internal__txn { |         addr, | ||||||
|     ($($field:ident: $val:expr,)+) => ( |         panic_rx, | ||||||
|         crate::support::server::Txn { |         shutdown_tx: Some(shutdown_tx), | ||||||
|             $( $field: __internal__prop!($field: $val), )+ |  | ||||||
|             .. Default::default() |  | ||||||
|     } |     } | ||||||
|     ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[macro_export] |  | ||||||
| macro_rules! __internal__prop { |  | ||||||
|     (request: $val:expr) => { |  | ||||||
|         From::from(&$val[..]) |  | ||||||
|     }; |  | ||||||
|     (response: $val:expr) => { |  | ||||||
|         From::from(&$val[..]) |  | ||||||
|     }; |  | ||||||
|     ($field:ident: $val:expr) => { |  | ||||||
|         From::from($val) |  | ||||||
|     }; |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,7 +1,64 @@ | |||||||
| #[macro_use] |  | ||||||
| mod support; | mod support; | ||||||
|  | use support::*; | ||||||
|  |  | ||||||
| use std::time::Duration; | use std::time::{Duration, Instant}; | ||||||
|  |  | ||||||
|  | #[tokio::test] | ||||||
|  | async fn request_timeout() { | ||||||
|  |     let _ = env_logger::try_init(); | ||||||
|  |  | ||||||
|  |     let server = server::http(move |_req| { | ||||||
|  |         async { | ||||||
|  |             // delay returning the response | ||||||
|  |             tokio::timer::delay(Instant::now() + Duration::from_secs(2)).await; | ||||||
|  |             http::Response::default() | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     let client = reqwest::Client::builder() | ||||||
|  |         .timeout(Duration::from_millis(500)) | ||||||
|  |         .build() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|  |     let url = format!("http://{}/slow", server.addr()); | ||||||
|  |  | ||||||
|  |     let res = client.get(&url).send().await; | ||||||
|  |  | ||||||
|  |     let err = res.unwrap_err(); | ||||||
|  |  | ||||||
|  |     assert!(err.is_timeout()); | ||||||
|  |     assert_eq!(err.url().map(|u| u.as_str()), Some(url.as_str())); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[tokio::test] | ||||||
|  | async fn response_timeout() { | ||||||
|  |     let _ = env_logger::try_init(); | ||||||
|  |  | ||||||
|  |     let server = server::http(move |_req| { | ||||||
|  |         async { | ||||||
|  |             // immediate response, but delayed body | ||||||
|  |             let body = hyper::Body::wrap_stream(futures_util::stream::once(async { | ||||||
|  |                 tokio::timer::delay(Instant::now() + Duration::from_secs(2)).await; | ||||||
|  |                 Ok::<_, std::convert::Infallible>("Hello") | ||||||
|  |             })); | ||||||
|  |  | ||||||
|  |             http::Response::new(body) | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     let client = reqwest::Client::builder() | ||||||
|  |         .timeout(Duration::from_millis(500)) | ||||||
|  |         .build() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|  |     let url = format!("http://{}/slow", server.addr()); | ||||||
|  |     let res = client.get(&url).send().await.expect("Failed to get"); | ||||||
|  |     let body = res.text().await; | ||||||
|  |  | ||||||
|  |     let err = body.unwrap_err(); | ||||||
|  |  | ||||||
|  |     assert!(err.is_timeout()); | ||||||
|  | } | ||||||
|  |  | ||||||
| /// Tests that internal client future cancels when the oneshot channel | /// Tests that internal client future cancels when the oneshot channel | ||||||
| /// is canceled. | /// is canceled. | ||||||
| @@ -17,24 +74,13 @@ fn timeout_closes_connection() { | |||||||
|         .build() |         .build() | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |     let server = server::http(move |_req| { | ||||||
|         request: b"\ |         async { | ||||||
|             GET /closes HTTP/1.1\r\n\ |             // delay returning the response | ||||||
|             user-agent: $USERAGENT\r\n\ |             tokio::timer::delay(Instant::now() + Duration::from_secs(2)).await; | ||||||
|             accept: */*\r\n\ |             http::Response::default() | ||||||
|             accept-encoding: gzip\r\n\ |         } | ||||||
|             host: $HOST\r\n\ |     }); | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Content-Length: 5\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             Hello\ |  | ||||||
|             ", |  | ||||||
|         read_timeout: Duration::from_secs(2), |  | ||||||
|         read_closes: true |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/closes", server.addr()); |     let url = format!("http://{}/closes", server.addr()); | ||||||
|     let err = client.get(&url).send().unwrap_err(); |     let err = client.get(&url).send().unwrap_err(); | ||||||
| @@ -47,7 +93,7 @@ fn timeout_closes_connection() { | |||||||
| #[test] | #[test] | ||||||
| fn write_timeout_large_body() { | fn write_timeout_large_body() { | ||||||
|     let _ = env_logger::try_init(); |     let _ = env_logger::try_init(); | ||||||
|     let body = String::from_utf8(vec![b'x'; 20_000]).unwrap(); |     let body = vec![b'x'; 20_000]; | ||||||
|     let len = 8192; |     let len = 8192; | ||||||
|  |  | ||||||
|     // Make Client drop *after* the Server, so the background doesn't |     // Make Client drop *after* the Server, so the background doesn't | ||||||
| @@ -57,28 +103,15 @@ fn write_timeout_large_body() { | |||||||
|         .build() |         .build() | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |     let server = server::http(move |_req| { | ||||||
|         request: format!("\ |         async { | ||||||
|             POST /write-timeout HTTP/1.1\r\n\ |             // delay returning the response | ||||||
|             user-agent: $USERAGENT\r\n\ |             tokio::timer::delay(Instant::now() + Duration::from_secs(2)).await; | ||||||
|             accept: */*\r\n\ |             http::Response::default() | ||||||
|             content-length: {}\r\n\ |         } | ||||||
|             accept-encoding: gzip\r\n\ |     }); | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             {}\ |  | ||||||
|             ", body.len(), body), |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Content-Length: 5\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             Hello\ |  | ||||||
|             ", |  | ||||||
|         read_timeout: Duration::from_secs(2), |  | ||||||
|         read_closes: true |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let cursor = std::io::Cursor::new(body.into_bytes()); |     let cursor = std::io::Cursor::new(body); | ||||||
|     let url = format!("http://{}/write-timeout", server.addr()); |     let url = format!("http://{}/write-timeout", server.addr()); | ||||||
|     let err = client |     let err = client | ||||||
|         .post(&url) |         .post(&url) | ||||||
| @@ -89,78 +122,3 @@ fn write_timeout_large_body() { | |||||||
|     assert!(err.is_timeout()); |     assert!(err.is_timeout()); | ||||||
|     assert_eq!(err.url().map(|u| u.as_str()), Some(url.as_str())); |     assert_eq!(err.url().map(|u| u.as_str()), Some(url.as_str())); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[tokio::test] |  | ||||||
| async fn test_response_timeout() { |  | ||||||
|     let _ = env_logger::try_init(); |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET /response-timeout HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Content-Length: 0\r\n\ |  | ||||||
|             ", |  | ||||||
|         response_timeout: Duration::from_secs(1) |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/response-timeout", server.addr()); |  | ||||||
|     let err = reqwest::Client::builder() |  | ||||||
|         .timeout(Duration::from_millis(500)) |  | ||||||
|         .build() |  | ||||||
|         .unwrap() |  | ||||||
|         .get(&url) |  | ||||||
|         .send() |  | ||||||
|         .await |  | ||||||
|         .unwrap_err(); |  | ||||||
|  |  | ||||||
|     assert!(err.is_timeout()); |  | ||||||
|     assert_eq!(err.url().map(|u| u.as_str()), Some(url.as_str())); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[tokio::test] |  | ||||||
| async fn test_read_timeout() { |  | ||||||
|     let _ = env_logger::try_init(); |  | ||||||
|     let server = server! { |  | ||||||
|         request: b"\ |  | ||||||
|             GET /read-timeout HTTP/1.1\r\n\ |  | ||||||
|             user-agent: $USERAGENT\r\n\ |  | ||||||
|             accept: */*\r\n\ |  | ||||||
|             accept-encoding: gzip\r\n\ |  | ||||||
|             host: $HOST\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             ", |  | ||||||
|         response: b"\ |  | ||||||
|             HTTP/1.1 200 OK\r\n\ |  | ||||||
|             Content-Length: 5\r\n\ |  | ||||||
|             \r\n\ |  | ||||||
|             Hello\ |  | ||||||
|             ", |  | ||||||
|         write_timeout: Duration::from_secs(1) |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let url = format!("http://{}/read-timeout", server.addr()); |  | ||||||
|     let res = reqwest::Client::builder() |  | ||||||
|         .timeout(Duration::from_millis(500)) |  | ||||||
|         .build() |  | ||||||
|         .unwrap() |  | ||||||
|         .get(&url) |  | ||||||
|         .send() |  | ||||||
|         .await |  | ||||||
|         .unwrap(); |  | ||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), &url); |  | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |  | ||||||
|     assert_eq!( |  | ||||||
|         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), |  | ||||||
|         &"5" |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     let err = res.text().await.unwrap_err(); |  | ||||||
|     assert!(err.is_timeout()); |  | ||||||
| } |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user