Make the async Client default (#626)

The previously default Client is moved to `reqwest::blocking`, while the
async client becomes the main API.

Closes #622
This commit is contained in:
Sean McArthur
2019-09-09 17:20:51 -07:00
committed by GitHub
parent 5fb04356fc
commit 87a09322d6
30 changed files with 1110 additions and 1066 deletions

View File

@@ -20,13 +20,29 @@ preparing breaking changes, for most recently released code, look to the
## Example
```rust,no_run
extern crate reqwest;
Async:
```rust,no_run
use std::collections::HashMap;
#[tokio::main]
async fn main() -> Result<(), Box<std::error::Error>> {
let resp: HashMap<String, String> = reqwest::get("https://httpbin.org/ip")
.await?
.json()
.await?;
println!("{:#?}", resp);
Ok(())
}
```
Blocking:
```rust,no_run
use std::collections::HashMap;
fn main() -> Result<(), Box<std::error::Error>> {
let resp: HashMap<String, String> = reqwest::get("https://httpbin.org/ip")?
let resp: HashMap<String, String> = reqwest::blocking::get("https://httpbin.org/ip")?
.json()?;
println!("{:#?}", resp);
Ok(())

View File

@@ -1,16 +0,0 @@
#![deny(warnings)]
use reqwest::r#async::Client;
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let mut res = Client::new().get("https://hyper.rs").send().await?;
println!("Status: {}", res.status());
let body = res.text().await?;
println!("Body:\n\n{}", body);
Ok(())
}

View File

@@ -1,41 +0,0 @@
#![deny(warnings)]
use reqwest::r#async::{Client, Response};
use serde::Deserialize;
use std::future::Future;
#[derive(Deserialize, Debug)]
struct Slideshow {
title: String,
author: String,
}
#[derive(Deserialize, Debug)]
struct SlideshowContainer {
slideshow: Slideshow,
}
async fn into_json<F>(f: F) -> Result<SlideshowContainer, reqwest::Error>
where
F: Future<Output = Result<Response, reqwest::Error>>,
{
let mut resp = f.await?;
resp.json::<SlideshowContainer>().await
}
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let client = Client::new();
let request1 = client.get("https://httpbin.org/json").send();
let request2 = client.get("https://httpbin.org/json").send();
let (try_json1, try_json2) =
futures::future::join(into_json(request1), into_json(request2)).await;
println!("{:?}", try_json1?);
println!("{:?}", try_json2?);
Ok(())
}

19
examples/blocking.rs Normal file
View File

@@ -0,0 +1,19 @@
//! `cargo run --example blocking`
#![deny(warnings)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
println!("GET https://www.rust-lang.org");
let mut res = reqwest::blocking::get("https://www.rust-lang.org/")?;
println!("Status: {}", res.status());
println!("Headers:\n{:?}", res.headers());
// copy the response body directly to stdout
res.copy_to(&mut std::io::stdout())?;
println!("\n\nDone.");
Ok(())
}

View File

@@ -1,7 +1,9 @@
fn main() {
#[tokio::main]
async fn main() {
reqwest::Client::new()
.post("http://www.baidu.com")
.form(&[("one", "1")])
.send()
.await
.unwrap();
}

View File

@@ -4,7 +4,8 @@
//! really care about the structure of the JSON and just need to display it or
//! process it at runtime.
fn main() -> Result<(), reqwest::Error> {
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let echo_json: serde_json::Value = reqwest::Client::new()
.post("https://jsonplaceholder.typicode.com/posts")
.json(&serde_json::json!({
@@ -12,8 +13,10 @@ fn main() -> Result<(), reqwest::Error> {
"body": "https://docs.rs/reqwest",
"userId": 1
}))
.send()?
.json()?;
.send()
.await?
.json()
.await?;
println!("{:#?}", echo_json);
// Object(

View File

@@ -15,7 +15,8 @@ struct Post {
user_id: i32,
}
fn main() -> Result<(), reqwest::Error> {
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let new_post = Post {
id: None,
title: "Reqwest.rs".into(),
@@ -25,8 +26,10 @@ fn main() -> Result<(), reqwest::Error> {
let new_post: Post = reqwest::Client::new()
.post("https://jsonplaceholder.typicode.com/posts")
.json(&new_post)
.send()?
.json()?;
.send()
.await?
.json()
.await?;
println!("{:#?}", new_post);
// Post {

View File

@@ -1,19 +1,17 @@
//! `cargo run --example simple`
#![deny(warnings)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
println!("GET https://www.rust-lang.org");
let mut res = reqwest::get("https://www.rust-lang.org/")?;
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let mut res = reqwest::Client::new()
.get("https://hyper.rs")
.send()
.await?;
println!("Status: {}", res.status());
println!("Headers:\n{:?}", res.headers());
// copy the response body directly to stdout
res.copy_to(&mut std::io::stdout())?;
let body = res.text().await?;
println!("Body:\n\n{}", body);
println!("\n\nDone.");
Ok(())
}

View File

@@ -33,7 +33,7 @@ impl Body {
/// # Example
///
/// ```
/// # use reqwest::r#async::Body;
/// # use reqwest::Body;
/// # use futures;
/// # fn main() {
/// let chunks: Vec<Result<_, ::std::io::Error>> = vec![

View File

@@ -61,7 +61,7 @@ impl Form {
/// # Examples
///
/// ```
/// let form = reqwest::r#async::multipart::Form::new()
/// let form = reqwest::multipart::Form::new()
/// .text("username", "seanmonstar")
/// .text("password", "secret");
/// ```

View File

@@ -194,8 +194,8 @@ impl RequestBuilder {
/// # use reqwest::Error;
///
/// # async fn run() -> Result<(), Error> {
/// let client = reqwest::r#async::Client::new();
/// let form = reqwest::r#async::multipart::Form::new()
/// let client = reqwest::Client::new();
/// let form = reqwest::multipart::Form::new()
/// .text("key3", "value3")
/// .text("key4", "value4");
///
@@ -329,7 +329,7 @@ impl RequestBuilder {
/// # use reqwest::Error;
/// #
/// # async fn run() -> Result<(), Error> {
/// let response = reqwest::r#async::Client::new()
/// let response = reqwest::Client::new()
/// .get("https://hyper.rs")
/// .send()
/// .await?;

View File

@@ -186,7 +186,7 @@ impl Response {
/// # Example
///
/// ```
/// # use reqwest::r#async::Response;
/// # use reqwest::Response;
/// fn on_response(res: Response) {
/// match res.error_for_status() {
/// Ok(_res) => (),
@@ -216,7 +216,7 @@ impl Response {
/// # Example
///
/// ```
/// # use reqwest::r#async::Response;
/// # use reqwest::Response;
/// fn on_response(res: &Response) {
/// match res.error_for_status_ref() {
/// Ok(_res) => (),
@@ -347,25 +347,6 @@ struct ResponseUrl(Url);
pub trait ResponseBuilderExt {
/// A builder method for the `http::response::Builder` type that allows the user to add a `Url`
/// to the `http::Response`
///
/// # Example
///
/// ```
/// # extern crate url;
/// # extern crate http;
/// # extern crate reqwest;
/// # use std::error::Error;
/// use url::Url;
/// use http::response::Builder;
/// use reqwest::r#async::ResponseBuilderExt;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let response = Builder::new()
/// .status(200)
/// .url(Url::parse("http://example.com")?)
/// .body(())?;
///
/// # Ok(())
/// # }
fn url(&mut self, url: Url) -> &mut Self;
}

View File

@@ -32,7 +32,7 @@ impl Body {
///
/// ```rust
/// # use std::fs::File;
/// # use reqwest::Body;
/// # use reqwest::blocking::Body;
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let file = File::open("national_secrets.txt")?;
/// let body = Body::new(file);
@@ -45,7 +45,7 @@ impl Body {
/// it can be reused.
///
/// ```rust
/// # use reqwest::Body;
/// # use reqwest::blocking::Body;
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let s = "A stringy body";
/// let body = Body::from(s);
@@ -64,7 +64,7 @@ impl Body {
///
/// ```rust
/// # use std::fs::File;
/// # use reqwest::Body;
/// # use reqwest::blocking::Body;
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let file = File::open("a_large_file.txt")?;
/// let file_size = file.metadata()?.len();

View File

@@ -10,9 +10,10 @@ use futures::{StreamExt, TryFutureExt};
use log::{error, trace};
use crate::request::{Request, RequestBuilder};
use crate::response::Response;
use crate::{async_impl, header, wait, IntoUrl, Method, Proxy, RedirectPolicy};
use super::request::{Request, RequestBuilder};
use super::response::Response;
use super::wait;
use crate::{async_impl, header, IntoUrl, Method, Proxy, RedirectPolicy};
#[cfg(feature = "tls")]
use crate::{Certificate, Identity};
@@ -28,9 +29,9 @@ use crate::{Certificate, Identity};
/// # Examples
///
/// ```rust
/// # use reqwest::{Error, Client};
/// use reqwest::blocking::Client;
/// #
/// # fn run() -> Result<(), Error> {
/// # fn run() -> Result<(), reqwest::Error> {
/// let client = Client::new();
/// let resp = client.get("http://httpbin.org/").send()?;
/// # drop(resp);
@@ -51,7 +52,7 @@ pub struct Client {
/// # fn run() -> Result<(), reqwest::Error> {
/// use std::time::Duration;
///
/// let client = reqwest::Client::builder()
/// let client = reqwest::blocking::Client::builder()
/// .gzip(true)
/// .timeout(Duration::from_secs(10))
/// .build()?;
@@ -130,7 +131,7 @@ impl ClientBuilder {
/// let cert = reqwest::Certificate::from_der(&buf)?;
///
/// // get a client builder
/// let client = reqwest::Client::builder()
/// let client = reqwest::blocking::Client::builder()
/// .add_root_certificate(cert)
/// .build()?;
/// # drop(client);
@@ -170,7 +171,7 @@ impl ClientBuilder {
/// let pkcs12 = reqwest::Identity::from_pem(&buf)?;
///
/// // get a client builder
/// let client = reqwest::Client::builder()
/// let client = reqwest::blocking::Client::builder()
/// .identity(pkcs12)
/// .build()?;
/// # drop(client);
@@ -224,7 +225,7 @@ impl ClientBuilder {
/// headers.insert(header::AUTHORIZATION, header::HeaderValue::from_static("secret"));
///
/// // get a client builder
/// let client = reqwest::Client::builder()
/// let client = reqwest::blocking::Client::builder()
/// .default_headers(headers)
/// .build()?;
/// let res = client.get("https://www.rust-lang.org").send()?;
@@ -241,7 +242,7 @@ impl ClientBuilder {
/// headers.insert(header::AUTHORIZATION, header::HeaderValue::from_static("secret"));
///
/// // get a client builder
/// let client = reqwest::Client::builder()
/// let client = reqwest::blocking::Client::builder()
/// .default_headers(headers)
/// .build()?;
/// let res = client
@@ -337,7 +338,7 @@ impl ClientBuilder {
/// # Example
///
/// ```
/// let client = reqwest::Client::builder()
/// let client = reqwest::blocking::Client::builder()
/// .h2_prior_knowledge()
/// .build().unwrap();
/// ```
@@ -350,7 +351,7 @@ impl ClientBuilder {
/// # Example
///
/// ```
/// let client = reqwest::Client::builder()
/// let client = reqwest::blocking::Client::builder()
/// .http1_title_case_headers()
/// .build().unwrap();
/// ```
@@ -365,7 +366,7 @@ impl ClientBuilder {
/// ```
/// use std::net::IpAddr;
/// let local_addr = IpAddr::from([12, 4, 1, 8]);
/// let client = reqwest::Client::builder()
/// let client = reqwest::blocking::Client::builder()
/// .local_address(local_addr)
/// .build().unwrap();
/// ```
@@ -386,7 +387,7 @@ impl ClientBuilder {
/// # Example
///
/// ```
/// let client = reqwest::Client::builder()
/// let client = reqwest::blocking::Client::builder()
/// .cookie_store(true)
/// .build()
/// .unwrap();

98
src/blocking/mod.rs Normal file
View File

@@ -0,0 +1,98 @@
//! A blocking Client API.
//!
//! The blocking `Client` will block the current thread to execute, instead
//! of returning futures that need to be executed on a runtime.
//!
//! ## Making a GET request
//!
//! For a single request, you can use the [`get`](get) shortcut method.
//!
//! ```rust
//! # use reqwest::{Error, Response};
//!
//! # fn run() -> Result<(), Error> {
//! let body = reqwest::blocking::get("https://www.rust-lang.org")?
//! .text()?;
//!
//! println!("body = {:?}", body);
//! # Ok(())
//! # }
//! ```
//!
//! Additionally, the blocking [`Response`](Response) struct implements Rust's
//! `Read` trait, so many useful standard library and third party crates will
//! have convenience methods that take a `Response` anywhere `T: Read` is
//! acceptable.
//!
//! **NOTE**: If you plan to perform multiple requests, it is best to create a
//! [`Client`](Client) and reuse it, taking advantage of keep-alive connection
//! pooling.
//!
//! ## Making POST requests (or setting request bodies)
//!
//! There are several ways you can set the body of a request. The basic one is
//! by using the `body()` method of a [`RequestBuilder`](RequestBuilder). This lets you set the
//! exact raw bytes of what the body should be. It accepts various types,
//! including `String`, `Vec<u8>`, and `File`. If you wish to pass a custom
//! Reader, you can use the `reqwest::blocking::Body::new()` constructor.
//!
//! ```rust
//! # use reqwest::Error;
//! #
//! # fn run() -> Result<(), Error> {
//! let client = reqwest::blocking::Client::new();
//! let res = client.post("http://httpbin.org/post")
//! .body("the exact body that is sent")
//! .send()?;
//! # Ok(())
//! # }
//! ```
//!
//! ## And More
//!
//! Most features available to the asynchronous `Client` are also available,
//! on the blocking `Client`, see those docs for more.
mod body;
mod client;
pub mod multipart;
mod request;
mod response;
mod wait;
pub(crate) use self::wait::Waited;
pub use self::body::Body;
pub use self::client::{Client, ClientBuilder};
pub use self::request::{Request, RequestBuilder};
pub use self::response::Response;
/// Shortcut method to quickly make a *blocking* `GET` request.
///
/// **NOTE**: This function creates a new internal `Client` on each call,
/// and so should not be used if making many requests. Create a
/// [`Client`](./struct.Client.html) instead.
///
/// # Examples
///
/// ```rust
/// # fn run() -> Result<(), reqwest::Error> {
/// let body = reqwest::blocking::get("https://www.rust-lang.org")?
/// .text()?;
/// # Ok(())
/// # }
/// # fn main() { }
/// ```
///
/// # Errors
///
/// This function fails if:
///
/// - native TLS backend cannot be initialized
/// - supplied `Url` cannot be parsed
/// - there was an error while sending request
/// - redirect loop was detected
/// - redirect limit was exhausted
pub fn get<T: crate::IntoUrl>(url: T) -> crate::Result<Response> {
Client::builder().build()?.get(url).send()
}

View File

@@ -7,7 +7,7 @@
//! # Example
//!
//! ```
//! use reqwest::multipart;
//! use reqwest::blocking::multipart;
//!
//! # fn run() -> Result<(), Box<dyn std::error::Error>> {
//! let form = multipart::Form::new()
@@ -25,7 +25,7 @@
//! let form = form.part("biography", bio);
//!
//! // And finally, send the form
//! let client = reqwest::Client::new();
//! let client = reqwest::blocking::Client::new();
//! let resp = client
//! .post("http://localhost:8080/user")
//! .multipart(form)
@@ -44,8 +44,8 @@ use std::path::Path;
use mime_guess::{self, Mime};
use super::Body;
use crate::async_impl::multipart::{FormParts, PartMetadata, PartProps};
use crate::Body;
/// A multipart/form-data request.
pub struct Form {
@@ -77,7 +77,7 @@ impl Form {
/// # Examples
///
/// ```
/// let form = reqwest::multipart::Form::new()
/// let form = reqwest::blocking::multipart::Form::new()
/// .text("username", "seanmonstar")
/// .text("password", "secret");
/// ```
@@ -97,7 +97,7 @@ impl Form {
///
/// ```no_run
/// # fn run() -> std::io::Result<()> {
/// let files = reqwest::multipart::Form::new()
/// let files = reqwest::blocking::multipart::Form::new()
/// .file("key", "/path/to/file")?;
/// # Ok(())
/// # }

View File

@@ -1,14 +1,16 @@
use std::fmt;
use base64::encode;
use http::HttpTryFrom;
use serde::Serialize;
use serde_json;
use serde_urlencoded;
use crate::body::{self, Body};
use super::body::{self, Body};
use super::multipart;
use super::Client;
use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE};
use crate::{async_impl, Client, Method, Url};
use http::HttpTryFrom;
use crate::{async_impl, Method, Url};
/// A request which can be executed with `Client::execute()`.
pub struct Request {
@@ -128,7 +130,7 @@ impl RequestBuilder {
/// use reqwest::header::USER_AGENT;
///
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let client = reqwest::Client::new();
/// let client = reqwest::blocking::Client::new();
/// let res = client.get("https://www.rust-lang.org")
/// .header(USER_AGENT, "foo")
/// .send()?;
@@ -175,7 +177,7 @@ impl RequestBuilder {
///
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let file = fs::File::open("much_beauty.png")?;
/// let client = reqwest::Client::new();
/// let client = reqwest::blocking::Client::new();
/// let res = client.post("http://httpbin.org/post")
/// .headers(construct_headers())
/// .body(file)
@@ -190,36 +192,11 @@ impl RequestBuilder {
self
}
/// Set a header with a type implementing hyper v0.11's `Header` trait.
///
/// This method is provided to ease migration, and requires the `hyper-011`
/// Cargo feature enabled on `reqwest`.
#[cfg(feature = "hyper-011")]
pub fn header_011<H>(self, header: H) -> RequestBuilder
where
H: crate::hyper_011::header::Header,
{
let mut headers = crate::hyper_011::Headers::new();
headers.set(header);
let map = crate::header::HeaderMap::from(headers);
self.headers(map)
}
/// Set multiple headers using hyper v0.11's `Headers` map.
///
/// This method is provided to ease migration, and requires the `hyper-011`
/// Cargo feature enabled on `reqwest`.
#[cfg(feature = "hyper-011")]
pub fn headers_011(self, headers: crate::hyper_011::Headers) -> RequestBuilder {
let map = crate::header::HeaderMap::from(headers);
self.headers(map)
}
/// Enable HTTP basic authentication.
///
/// ```rust
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let client = reqwest::Client::new();
/// let client = reqwest::blocking::Client::new();
/// let resp = client.delete("http://httpbin.org/delete")
/// .basic_auth("admin", Some("good password"))
/// .send()?;
@@ -243,7 +220,7 @@ impl RequestBuilder {
///
/// ```rust
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let client = reqwest::Client::new();
/// let client = reqwest::blocking::Client::new();
/// let resp = client.delete("http://httpbin.org/delete")
/// .bearer_auth("token")
/// .send()?;
@@ -266,7 +243,7 @@ impl RequestBuilder {
///
/// ```rust
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let client = reqwest::Client::new();
/// let client = reqwest::blocking::Client::new();
/// let res = client.post("http://httpbin.org/post")
/// .body("from a &str!")
/// .send()?;
@@ -277,10 +254,9 @@ impl RequestBuilder {
/// Using a `File`:
///
/// ```rust
/// # use std::fs;
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let file = fs::File::open("from_a_file.txt")?;
/// let client = reqwest::Client::new();
/// let file = std::fs::File::open("from_a_file.txt")?;
/// let client = reqwest::blocking::Client::new();
/// let res = client.post("http://httpbin.org/post")
/// .body(file)
/// .send()?;
@@ -295,7 +271,7 @@ impl RequestBuilder {
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// // from bytes!
/// let bytes: Vec<u8> = vec![1, 10, 100];
/// let client = reqwest::Client::new();
/// let client = reqwest::blocking::Client::new();
/// let res = client.post("http://httpbin.org/post")
/// .body(bytes)
/// .send()?;
@@ -322,7 +298,7 @@ impl RequestBuilder {
/// # use reqwest::Error;
/// #
/// # fn run() -> Result<(), Error> {
/// let client = reqwest::Client::new();
/// let client = reqwest::blocking::Client::new();
/// let res = client.get("http://httpbin.org")
/// .query(&[("lang", "rust")])
/// .send()?;
@@ -375,7 +351,7 @@ impl RequestBuilder {
/// let mut params = HashMap::new();
/// params.insert("lang", "rust");
///
/// let client = reqwest::Client::new();
/// let client = reqwest::blocking::Client::new();
/// let res = client.post("http://httpbin.org")
/// .form(&params)
/// .send()?;
@@ -420,7 +396,7 @@ impl RequestBuilder {
/// let mut map = HashMap::new();
/// map.insert("lang", "rust");
///
/// let client = reqwest::Client::new();
/// let client = reqwest::blocking::Client::new();
/// let res = client.post("http://httpbin.org")
/// .json(&map)
/// .send()?;
@@ -456,8 +432,8 @@ impl RequestBuilder {
/// # use reqwest::Error;
///
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let client = reqwest::Client::new();
/// let form = reqwest::multipart::Form::new()
/// let client = reqwest::blocking::Client::new();
/// let form = reqwest::blocking::multipart::Form::new()
/// .text("key3", "value3")
/// .file("file", "/path/to/field")?;
///
@@ -469,7 +445,7 @@ impl RequestBuilder {
/// ```
///
/// See [`multipart`](multipart/) for more examples.
pub fn multipart(self, mut multipart: crate::multipart::Form) -> RequestBuilder {
pub fn multipart(self, mut multipart: multipart::Form) -> RequestBuilder {
let mut builder = self.header(
CONTENT_TYPE,
format!("multipart/form-data; boundary={}", multipart.boundary()).as_str(),
@@ -495,7 +471,7 @@ impl RequestBuilder {
///
/// This method fails if there was an error while sending request,
/// redirect loop was detected or redirect limit was exhausted.
pub fn send(self) -> crate::Result<crate::Response> {
pub fn send(self) -> crate::Result<super::Response> {
self.client.execute(self.request?)
}
@@ -510,7 +486,7 @@ impl RequestBuilder {
///
/// ```rust
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let client = reqwest::Client::new();
/// let client = reqwest::blocking::Client::new();
/// let builder = client.post("http://httpbin.org/post")
/// .body("from a &str!");
/// let clone = builder.try_clone();
@@ -523,7 +499,7 @@ impl RequestBuilder {
///
/// ```rust
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let client = reqwest::Client::new();
/// let client = reqwest::blocking::Client::new();
/// let builder = client.get("http://httpbin.org/get");
/// let clone = builder.try_clone();
/// assert!(clone.is_some());
@@ -535,9 +511,9 @@ impl RequestBuilder {
///
/// ```rust
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let client = reqwest::Client::new();
/// let client = reqwest::blocking::Client::new();
/// let builder = client.get("http://httpbin.org/get")
/// .body(reqwest::Body::new(std::io::empty()));
/// .body(reqwest::blocking::Body::new(std::io::empty()));
/// let clone = builder.try_clone();
/// assert!(clone.is_none());
/// # Ok(())
@@ -572,8 +548,9 @@ fn fmt_request_fields<'a, 'b>(
#[cfg(test)]
mod tests {
use super::super::{body, Client};
use crate::header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE, HOST};
use crate::{body, Client, Method};
use crate::Method;
use serde::Serialize;
use serde_json;
use serde_urlencoded;

View File

@@ -6,12 +6,13 @@ use std::pin::Pin;
use std::time::Duration;
use http;
use hyper::header::HeaderMap;
use serde::de::DeserializeOwned;
use crate::client::KeepCoreThreadAlive;
use super::client::KeepCoreThreadAlive;
use super::wait;
use crate::cookie;
use crate::{async_impl, wait, StatusCode, Url, Version};
use hyper::header::HeaderMap;
use crate::{async_impl, StatusCode, Url, Version};
/// A Response to a submitted `Request`.
pub struct Response {
@@ -49,7 +50,7 @@ impl Response {
///
/// ```rust
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let resp = reqwest::get("http://httpbin.org/get")?;
/// let resp = reqwest::blocking::get("http://httpbin.org/get")?;
/// if resp.status().is_success() {
/// println!("success!");
/// } else if resp.status().is_server_error() {
@@ -64,7 +65,7 @@ impl Response {
/// Checking for specific status codes:
///
/// ```rust
/// use reqwest::Client;
/// use reqwest::blocking::Client;
/// use reqwest::StatusCode;
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let client = Client::new();
@@ -95,7 +96,7 @@ impl Response {
/// Saving an etag when caching a file:
///
/// ```
/// use reqwest::Client;
/// use reqwest::blocking::Client;
/// use reqwest::header::ETAG;
///
/// # fn run() -> Result<(), Box<std::error::Error>> {
@@ -136,7 +137,7 @@ impl Response {
///
/// ```rust
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let resp = reqwest::get("http://httpbin.org/redirect/1")?;
/// let resp = reqwest::blocking::get("http://httpbin.org/redirect/1")?;
/// assert_eq!(resp.url().as_str(), "http://httpbin.org/get");
/// # Ok(())
/// # }
@@ -152,7 +153,7 @@ impl Response {
///
/// ```rust
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let resp = reqwest::get("http://httpbin.org/redirect/1")?;
/// let resp = reqwest::blocking::get("http://httpbin.org/redirect/1")?;
/// println!("httpbin.org address: {:?}", resp.remote_addr());
/// # Ok(())
/// # }
@@ -189,7 +190,7 @@ impl Response {
/// }
///
/// # fn run() -> Result<(), Error> {
/// let json: Ip = reqwest::get("http://httpbin.org/ip")?.json()?;
/// let json: Ip = reqwest::blocking::get("http://httpbin.org/ip")?.json()?;
/// # Ok(())
/// # }
/// #
@@ -223,7 +224,7 @@ impl Response {
/// ```rust
/// # extern crate reqwest;
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let content = reqwest::get("http://httpbin.org/range/26")?.text()?;
/// let content = reqwest::blocking::get("http://httpbin.org/range/26")?.text()?;
/// # Ok(())
/// # }
/// ```
@@ -250,7 +251,8 @@ impl Response {
/// ```rust
/// # extern crate reqwest;
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let content = reqwest::get("http://httpbin.org/range/26")?.text_with_charset("utf-8")?;
/// let content = reqwest::blocking::get("http://httpbin.org/range/26")?
/// .text_with_charset("utf-8")?;
/// # Ok(())
/// # }
/// ```
@@ -282,7 +284,7 @@ impl Response {
///
/// ```rust
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let mut resp = reqwest::get("http://httpbin.org/range/5")?;
/// let mut resp = reqwest::blocking::get("http://httpbin.org/range/5")?;
/// let mut buf: Vec<u8> = vec![];
/// resp.copy_to(&mut buf)?;
/// assert_eq!(b"abcde", buf.as_slice());
@@ -303,7 +305,7 @@ impl Response {
/// ```rust,no_run
/// # extern crate reqwest;
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let res = reqwest::get("http://httpbin.org/status/400")?
/// let res = reqwest::blocking::get("http://httpbin.org/status/400")?
/// .error_for_status();
/// if let Err(err) = res {
/// assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST));
@@ -312,7 +314,6 @@ impl Response {
/// # }
/// # fn main() {}
/// ```
#[inline]
pub fn error_for_status(self) -> crate::Result<Self> {
let Response {
body,
@@ -335,7 +336,7 @@ impl Response {
/// ```rust,no_run
/// # extern crate reqwest;
/// # fn run() -> Result<(), Box<std::error::Error>> {
/// let res = reqwest::get("http://httpbin.org/status/400")?;
/// let res = reqwest::blocking::get("http://httpbin.org/status/400")?;
/// let res = res.error_for_status_ref();
/// if let Err(err) = res {
/// assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST));
@@ -344,7 +345,6 @@ impl Response {
/// # }
/// # fn main() {}
/// ```
#[inline]
pub fn error_for_status_ref(&self) -> crate::Result<&Self> {
self.inner.error_for_status_ref().and_then(|_| Ok(self))
}

View File

@@ -20,15 +20,15 @@ use crate::{StatusCode, Url};
/// }
/// # fn main() { }
///
/// fn run() {
/// match make_request() {
/// async fn run() {
/// match make_request().await {
/// Err(e) => handler(e),
/// Ok(_) => return,
/// }
/// }
/// // Response is not a json object conforming to the Simple struct
/// fn make_request() -> Result<Simple, reqwest::Error> {
/// reqwest::get("http://httpbin.org/ip")?.json()
/// async fn make_request() -> Result<Simple, reqwest::Error> {
/// reqwest::get("http://httpbin.org/ip").await?.json().await
/// }
///
/// fn handler(e: reqwest::Error) {
@@ -75,9 +75,9 @@ impl Error {
/// # Examples
///
/// ```
/// # fn run() {
/// # async fn run() {
/// // displays last stop of a redirect loop
/// let response = reqwest::get("http://site.with.redirect.loop");
/// let response = reqwest::get("http://site.with.redirect.loop").await;
/// if let Err(e) = response {
/// if e.is_redirect() {
/// if let Some(final_stop) = e.url() {
@@ -108,14 +108,14 @@ impl Error {
/// extern crate url;
/// # extern crate reqwest;
/// // retries requests with no host on localhost
/// # fn run() {
/// # async fn run() {
/// let invalid_request = "http://";
/// let mut response = reqwest::get(invalid_request);
/// let mut response = reqwest::get(invalid_request).await;
/// if let Err(e) = response {
/// match e.get_ref().and_then(|e| e.downcast_ref::<url::ParseError>()) {
/// Some(&url::ParseError::EmptyHost) => {
/// let valid_request = format!("{}{}",invalid_request, "localhost");
/// response = reqwest::get(&valid_request);
/// response = reqwest::get(&valid_request).await;
/// },
/// _ => (),
/// }
@@ -123,7 +123,6 @@ impl Error {
/// # }
/// # fn main() {}
/// ```
#[inline]
pub fn get_ref(&self) -> Option<&(dyn StdError + Send + Sync + 'static)> {
match self.inner.kind {
Kind::Http(ref e) => Some(e),
@@ -152,7 +151,6 @@ impl Error {
}
/// Returns true if the error is related to HTTP.
#[inline]
pub fn is_http(&self) -> bool {
match self.inner.kind {
Kind::Http(_) => true,
@@ -175,7 +173,6 @@ impl Error {
}
/// Returns true if the error is serialization related.
#[inline]
pub fn is_serialization(&self) -> bool {
match self.inner.kind {
Kind::Json(_) | Kind::UrlEncoded(_) => true,
@@ -184,7 +181,6 @@ impl Error {
}
/// Returns true if the error is from a `RedirectPolicy`.
#[inline]
pub fn is_redirect(&self) -> bool {
match self.inner.kind {
Kind::TooManyRedirects | Kind::RedirectLoop => true,
@@ -193,7 +189,6 @@ impl Error {
}
/// Returns true if the error is from a request returning a 4xx error.
#[inline]
pub fn is_client_error(&self) -> bool {
match self.inner.kind {
Kind::Status(code) => code.is_client_error(),
@@ -202,7 +197,6 @@ impl Error {
}
/// Returns true if the error is from a request returning a 5xx error.
#[inline]
pub fn is_server_error(&self) -> bool {
match self.inner.kind {
Kind::Status(code) => code.is_server_error(),
@@ -211,7 +205,6 @@ impl Error {
}
/// Returns the status code, if the error was generated from a response.
#[inline]
pub fn status(&self) -> Option<StatusCode> {
match self.inner.kind {
Kind::Status(code) => Some(code),
@@ -466,15 +459,15 @@ impl From<rustls::TLSError> for Kind {
}
}
impl<T> From<crate::wait::Waited<T>> for Kind
impl<T> From<crate::blocking::Waited<T>> for Kind
where
T: Into<Kind>,
{
fn from(err: crate::wait::Waited<T>) -> Kind {
fn from(err: crate::blocking::Waited<T>) -> Kind {
match err {
crate::wait::Waited::TimedOut => io_timeout().into(),
crate::wait::Waited::Executor(e) => e.into(),
crate::wait::Waited::Inner(e) => e.into(),
crate::blocking::Waited::TimedOut => io_timeout().into(),
crate::blocking::Waited::Executor(e) => e.into(),
crate::blocking::Waited::Inner(e) => e.into(),
}
}
}

View File

@@ -11,15 +11,16 @@
//! It handles many of the things that most people just expect an HTTP client
//! to do for them.
//!
//! - Async and [blocking](blocking) Clients
//! - Plain bodies, [JSON](#json), [urlencoded](#forms), [multipart](multipart)
//! - Customizable [redirect policy](#redirect-policy)
//! - HTTP [Proxies](#proxies)
//! - Uses system-native [TLS](#tls)
//! - Cookies
//!
//! The [`reqwest::Client`][client] is synchronous, making it a great fit for
//! applications that only require a few HTTP requests, and wish to handle
//! them synchronously.
//! The [`reqwest::Client`][client] is asynchronous. For applications wishing
//! to only make a few HTTP requests, the [`reqwest::blocking`](blocking) API
//! may be more convenient.
//!
//! Additional learning resources include:
//!
@@ -31,22 +32,17 @@
//! For a single request, you can use the [`get`][get] shortcut method.
//!
//! ```rust
//! # use reqwest::{Error, Response};
//!
//! # fn run() -> Result<(), Error> {
//! let body = reqwest::get("https://www.rust-lang.org")?
//! .text()?;
//! # async fn run() -> Result<(), reqwest::Error> {
//! let body = reqwest::get("https://www.rust-lang.org")
//! .await?
//! .text()
//! .await?;
//!
//! println!("body = {:?}", body);
//! # Ok(())
//! # }
//! ```
//!
//! Additionally, reqwest's [`Response`][response] struct implements Rust's
//! `Read` trait, so many useful standard library and third party crates will
//! have convenience methods that take a `Response` anywhere `T: Read` is
//! acceptable.
//!
//! **NOTE**: If you plan to perform multiple requests, it is best to create a
//! [`Client`][client] and reuse it, taking advantage of keep-alive connection
//! pooling.
@@ -57,16 +53,17 @@
//! by using the `body()` method of a [`RequestBuilder`][builder]. This lets you set the
//! exact raw bytes of what the body should be. It accepts various types,
//! including `String`, `Vec<u8>`, and `File`. If you wish to pass a custom
//! Reader, you can use the `reqwest::Body::new()` constructor.
//! type, you can use the `reqwest::Body` constructors.
//!
//! ```rust
//! # use reqwest::Error;
//! #
//! # fn run() -> Result<(), Error> {
//! # async fn run() -> Result<(), Error> {
//! let client = reqwest::Client::new();
//! let res = client.post("http://httpbin.org/post")
//! .body("the exact body that is sent")
//! .send()?;
//! .send()
//! .await?;
//! # Ok(())
//! # }
//! ```
@@ -82,13 +79,14 @@
//! ```rust
//! # use reqwest::Error;
//! #
//! # fn run() -> Result<(), Error> {
//! # async fn run() -> Result<(), Error> {
//! // This will POST a body of `foo=bar&baz=quux`
//! let params = [("foo", "bar"), ("baz", "quux")];
//! let client = reqwest::Client::new();
//! let res = client.post("http://httpbin.org/post")
//! .form(&params)
//! .send()?;
//! .send()
//! .await?;
//! # Ok(())
//! # }
//! ```
@@ -103,7 +101,7 @@
//! # use reqwest::Error;
//! # use std::collections::HashMap;
//! #
//! # fn run() -> Result<(), Error> {
//! # async fn run() -> Result<(), Error> {
//! // This will POST a body of `{"lang":"rust","body":"json"}`
//! let mut map = HashMap::new();
//! map.insert("lang", "rust");
@@ -112,7 +110,8 @@
//! let client = reqwest::Client::new();
//! let res = client.post("http://httpbin.org/post")
//! .json(&map)
//! .send()?;
//! .send()
//! .await?;
//! # Ok(())
//! # }
//! ```
@@ -157,8 +156,6 @@
//! `native-tls` library to connect over HTTPS.
//! - **default-tls-vendored**: Enables the `vendored` feature of `native-tls`.
//! - **rustls-tls**: Provides TLS support via the `rustls` library.
//! - **socks**: Provides SOCKS5 proxy support.
//! - **hyper-011**: Provides support for hyper's old typed headers.
//!
//!
//! [hyper]: http://hyper.rs
@@ -171,12 +168,11 @@
//! [Proxy]: ./struct.Proxy.html
//! [cargo-features]: https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-features-section
////! - **socks**: Provides SOCKS5 proxy support.
////! - **trust-dns**: Enables a trust-dns async resolver instead of default
////! threadpool using `getaddrinfo`.
extern crate cookie as cookie_crate;
#[cfg(feature = "hyper-011")]
pub use hyper_old_types as hyper_011;
#[cfg(test)]
#[macro_use]
@@ -191,14 +187,17 @@ pub use hyper::{StatusCode, Version};
pub use url::ParseError as UrlError;
pub use url::Url;
pub use self::body::Body;
pub use self::client::{Client, ClientBuilder};
pub use self::r#async::{
multipart, Body, Client, ClientBuilder, Decoder, Request, RequestBuilder, Response,
};
//pub use self::body::Body;
//pub use self::client::{Client, ClientBuilder};
pub use self::error::{Error, Result};
pub use self::into_url::IntoUrl;
pub use self::proxy::Proxy;
pub use self::redirect::{RedirectAction, RedirectAttempt, RedirectPolicy};
pub use self::request::{Request, RequestBuilder};
pub use self::response::Response;
//pub use self::request::{Request, RequestBuilder};
//pub use self::response::Response;
#[cfg(feature = "tls")]
pub use self::tls::{Certificate, Identity};
@@ -207,8 +206,7 @@ pub use self::tls::{Certificate, Identity};
mod error;
mod async_impl;
mod body;
mod client;
pub mod blocking;
mod connect;
pub mod cookie;
//#[cfg(feature = "trust-dns")]
@@ -216,19 +214,16 @@ pub mod cookie;
mod into_url;
mod proxy;
mod redirect;
mod request;
mod response;
#[cfg(feature = "tls")]
mod tls;
mod wait;
pub mod multipart;
//pub mod multipart;
/// An 'async' implementation of the reqwest `Client`.
#[doc(hidden)]
#[deprecated(note = "types moved to top of crate")]
pub mod r#async {
pub use crate::async_impl::{
multipart, Body, Chunk, Client, ClientBuilder, Decoder, Request, RequestBuilder, Response,
ResponseBuilderExt,
};
}
@@ -244,12 +239,11 @@ pub mod r#async {
/// # Examples
///
/// ```rust
/// # fn run() -> Result<(), reqwest::Error> {
/// let body = reqwest::get("https://www.rust-lang.org")?
/// .text()?;
/// # async fn run() -> Result<(), reqwest::Error> {
/// let body = reqwest::get("https://www.rust-lang.org").await?
/// .text().await?;
/// # Ok(())
/// # }
/// # fn main() { }
/// ```
///
/// # Errors
@@ -261,8 +255,8 @@ pub mod r#async {
/// - there was an error while sending request
/// - redirect loop was detected
/// - redirect limit was exhausted
pub fn get<T: IntoUrl>(url: T) -> crate::Result<Response> {
Client::builder().build()?.get(url).send()
pub async fn get<T: IntoUrl>(url: T) -> crate::Result<Response> {
Client::builder().build()?.get(url).send().await
}
fn _assert_impls() {

View File

@@ -1,325 +0,0 @@
#[macro_use]
mod support;
use std::io::Write;
use std::time::Duration;
use futures::TryStreamExt;
use reqwest::r#async::multipart::{Form, Part};
use reqwest::r#async::{Body, Client};
use bytes::Bytes;
#[tokio::test]
async fn gzip_response() {
gzip_case(10_000, 4096).await;
}
#[tokio::test]
async fn gzip_single_byte_chunks() {
gzip_case(10, 1).await;
}
#[tokio::test]
async fn response_text() {
let _ = env_logger::try_init();
let server = server! {
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 mut res = client
.get(&format!("http://{}/text", server.addr()))
.send()
.await
.expect("Failed to get");
let text = res.text().await.expect("Failed to get text");
assert_eq!("Hello", text);
}
#[tokio::test]
async fn response_json() {
let _ = env_logger::try_init();
let server = server! {
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 mut res = client
.get(&format!("http://{}/json", server.addr()))
.send()
.await
.expect("Failed to get");
let text = res.json::<String>().await.expect("Failed to get json");
assert_eq!("Hello", text);
}
#[tokio::test]
async fn multipart() {
let _ = env_logger::try_init();
let stream = futures::stream::once(futures::future::ready::<Result<_, hyper::Error>>(Ok(
hyper::Chunk::from("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: Result<_, _> = res.into_body().try_concat().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 mut 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::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);
}

View File

@@ -1,57 +1,65 @@
#[cfg(feature = "tls")]
#[test]
fn test_badssl_modern() {
#[tokio::test]
async fn test_badssl_modern() {
let text = reqwest::get("https://mozilla-modern.badssl.com/")
.await
.unwrap()
.text()
.await
.unwrap();
assert!(text.contains("<title>mozilla-modern.badssl.com</title>"));
}
#[cfg(feature = "rustls-tls")]
#[test]
fn test_rustls_badssl_modern() {
#[tokio::test]
async fn test_rustls_badssl_modern() {
let text = reqwest::Client::builder()
.use_rustls_tls()
.build()
.unwrap()
.get("https://mozilla-modern.badssl.com/")
.send()
.await
.unwrap()
.text()
.await
.unwrap();
assert!(text.contains("<title>mozilla-modern.badssl.com</title>"));
}
#[cfg(feature = "tls")]
#[test]
fn test_badssl_self_signed() {
#[tokio::test]
async fn test_badssl_self_signed() {
let text = reqwest::Client::builder()
.danger_accept_invalid_certs(true)
.build()
.unwrap()
.get("https://self-signed.badssl.com/")
.send()
.await
.unwrap()
.text()
.await
.unwrap();
assert!(text.contains("<title>self-signed.badssl.com</title>"));
}
#[cfg(feature = "default-tls")]
#[test]
fn test_badssl_wrong_host() {
#[tokio::test]
async fn test_badssl_wrong_host() {
let text = reqwest::Client::builder()
.danger_accept_invalid_hostnames(true)
.build()
.unwrap()
.get("https://wrong.host.badssl.com/")
.send()
.await
.unwrap()
.text()
.await
.unwrap();
assert!(text.contains("<title>wrong.host.badssl.com</title>"));
@@ -61,7 +69,8 @@ fn test_badssl_wrong_host() {
.build()
.unwrap()
.get("https://self-signed.badssl.com/")
.send();
.send()
.await;
assert!(result.is_err());
}

512
tests/blocking.rs Normal file
View File

@@ -0,0 +1,512 @@
#[macro_use]
mod support;
#[test]
fn test_response_text() {
let server = server! {
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 mut res = reqwest::blocking::get(&url).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(),
&"5"
);
let body = res.text().unwrap();
assert_eq!(b"Hello", body.as_bytes());
}
#[test]
fn test_response_non_utf_8_text() {
let server = server! {
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: 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 mut res = reqwest::blocking::get(&url).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(),
&"4"
);
let body = res.text().unwrap();
assert_eq!("你好", &body);
assert_eq!(b"\xe4\xbd\xa0\xe5\xa5\xbd", body.as_bytes()); // Now it's utf-8
}
#[test]
fn test_response_json() {
let server = server! {
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 mut res = reqwest::blocking::get(&url).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(),
&"7"
);
let body = res.json::<String>().unwrap();
assert_eq!("Hello", body);
}
#[test]
fn test_response_copy_to() {
let server = server! {
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 mut res = reqwest::blocking::get(&url).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(),
&"5"
);
assert_eq!("Hello".to_owned(), res.text().unwrap());
}
#[test]
fn test_get() {
let server = server! {
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 mut res = reqwest::blocking::get(&url).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"
);
assert_eq!(res.remote_addr(), Some(server.addr()));
assert_eq!(res.text().unwrap().len(), 0)
}
#[test]
fn test_post() {
let server = server! {
request: b"\
POST /2 HTTP/1.1\r\n\
content-length: 5\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
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 mut res = reqwest::blocking::Client::new()
.post(&url)
.body("Hello")
.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(), &"post");
assert_eq!(
res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(),
&"0"
);
assert_eq!(res.text().unwrap().len(), 0)
}
#[test]
fn test_post_form() {
let server = server! {
request: b"\
POST /form HTTP/1.1\r\n\
content-type: application/x-www-form-urlencoded\r\n\
content-length: 24\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
hello=world&sean=monstar\
",
response: b"\
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 url = format!("http://{}/form", server.addr());
let res = reqwest::blocking::Client::new()
.post(&url)
.form(form)
.send()
.expect("request send");
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
}
/// Calling `Response::error_for_status`` on a response with status in 4xx
/// returns a error.
#[test]
fn test_error_for_status_4xx() {
let server = server! {
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 400 OK\r\n\
Server: test\r\n\
Content-Length: 0\r\n\
\r\n\
"
};
let url = format!("http://{}/1", server.addr());
let res = reqwest::blocking::get(&url).unwrap();
let err = res.error_for_status().err().unwrap();
assert!(err.is_client_error());
assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST));
}
/// Calling `Response::error_for_status`` on a response with status in 5xx
/// returns a error.
#[test]
fn test_error_for_status_5xx() {
let server = server! {
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 500 OK\r\n\
Server: test\r\n\
Content-Length: 0\r\n\
\r\n\
"
};
let url = format!("http://{}/1", server.addr());
let res = reqwest::blocking::get(&url).unwrap();
let err = res.error_for_status().err().unwrap();
assert!(err.is_server_error());
assert_eq!(
err.status(),
Some(reqwest::StatusCode::INTERNAL_SERVER_ERROR)
);
}
#[test]
fn test_default_headers() {
use reqwest::header;
let mut headers = header::HeaderMap::with_capacity(1);
headers.insert(header::COOKIE, header::HeaderValue::from_static("a=b;c=d"));
let client = reqwest::blocking::Client::builder()
.default_headers(headers)
.build()
.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 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"
);
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]
fn test_override_default_headers() {
use reqwest::header;
let mut headers = header::HeaderMap::with_capacity(1);
headers.insert(
header::AUTHORIZATION,
header::HeaderValue::from_static("iamatoken"),
);
let client = reqwest::blocking::Client::builder()
.default_headers(headers)
.build()
.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 res = client
.get(&url)
.header(
header::AUTHORIZATION,
header::HeaderValue::from_static("secret"),
)
.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]
fn test_appended_headers_not_overwritten() {
let client = reqwest::blocking::Client::new();
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 res = client
.get(&url)
.header(header::ACCEPT, "application/json")
.header(header::ACCEPT, "application/json+hal")
.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"
);
// make sure this also works with default headers
use reqwest::header;
let mut headers = header::HeaderMap::with_capacity(1);
headers.insert(
header::ACCEPT,
header::HeaderValue::from_static("text/html"),
);
let client = reqwest::blocking::Client::builder()
.default_headers(headers)
.build()
.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 res = client
.get(&url)
.header(header::ACCEPT, "application/json")
.header(header::ACCEPT, "application/json+hal")
.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"
);
}

View File

@@ -1,8 +1,30 @@
#[macro_use]
mod support;
#[test]
fn test_response_text() {
use std::io::Write;
use std::time::Duration;
use futures::TryStreamExt;
use reqwest::multipart::{Form, Part};
use reqwest::{Body, Client};
use bytes::Bytes;
#[tokio::test]
async fn gzip_response() {
gzip_case(10_000, 4096).await;
}
#[tokio::test]
async fn gzip_single_byte_chunks() {
gzip_case(10, 1).await;
}
#[tokio::test]
async fn response_text() {
let _ = env_logger::try_init();
let server = server! {
request: b"\
GET /text HTTP/1.1\r\n\
@@ -14,65 +36,27 @@ fn test_response_text() {
",
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 mut res = reqwest::get(&url).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(),
&"5"
);
let client = Client::new();
let body = res.text().unwrap();
assert_eq!(b"Hello", body.as_bytes());
let mut res = client
.get(&format!("http://{}/text", server.addr()))
.send()
.await
.expect("Failed to get");
let text = res.text().await.expect("Failed to get text");
assert_eq!("Hello", text);
}
#[test]
fn test_response_non_utf_8_text() {
let server = server! {
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: 4\r\n\
Content-Type: text/plain; charset=gbk\r\n\
\r\n\
\xc4\xe3\xba\xc3\
"
};
#[tokio::test]
async fn response_json() {
let _ = env_logger::try_init();
let url = format!("http://{}/text", server.addr());
let mut res = reqwest::get(&url).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(),
&"4"
);
let body = res.text().unwrap();
assert_eq!("你好", &body);
assert_eq!(b"\xe4\xbd\xa0\xe5\xa5\xbd", body.as_bytes()); // Now it's utf-8
}
#[test]
fn test_response_json() {
let server = server! {
request: b"\
GET /json HTTP/1.1\r\n\
@@ -84,32 +68,100 @@ fn test_response_json() {
",
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 mut res = reqwest::get(&url).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(),
&"7"
);
let client = Client::new();
let body = res.json::<String>().unwrap();
assert_eq!("Hello", body);
let mut res = client
.get(&format!("http://{}/json", server.addr()))
.send()
.await
.expect("Failed to get");
let text = res.json::<String>().await.expect("Failed to get json");
assert_eq!("Hello", text);
}
#[test]
fn test_response_copy_to() {
#[tokio::test]
async fn multipart() {
let _ = env_logger::try_init();
let stream = futures::stream::once(futures::future::ready::<Result<_, hyper::Error>>(Ok(
hyper::Chunk::from("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 /1 HTTP/1.1\r\n\
GET /slow HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
@@ -118,31 +170,35 @@ fn test_response_copy_to() {
",
response: b"\
HTTP/1.1 200 OK\r\n\
Server: test\r\n\
Content-Length: 5\r\n\
\r\n\
Hello\
"
",
read_timeout: Duration::from_secs(2)
};
let url = format!("http://{}/1", server.addr());
let mut res = reqwest::get(&url).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(),
&"5"
);
let client = Client::builder()
.timeout(Duration::from_millis(500))
.build()
.unwrap();
assert_eq!("Hello".to_owned(), res.text().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()));
}
#[test]
fn test_get() {
#[tokio::test]
async fn response_timeout() {
let _ = env_logger::try_init();
let server = server! {
request: b"\
GET /1 HTTP/1.1\r\n\
GET /slow HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
@@ -151,362 +207,119 @@ fn test_get() {
",
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 mut res = reqwest::get(&url).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"
);
assert_eq!(res.remote_addr(), Some(server.addr()));
assert_eq!(res.text().unwrap().len(), 0)
}
#[test]
fn test_post() {
let server = server! {
request: b"\
POST /2 HTTP/1.1\r\n\
content-length: 5\r\n\
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\
",
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: Result<_, _> = res.into_body().try_concat().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 mut 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::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: 0\r\n\
Content-Length: 7\r\n\
\r\n\
"
};
let url = format!("http://{}/2", server.addr());
let mut res = reqwest::Client::new()
let url = format!("http://{}/post", server.addr());
let client = Client::new();
let res = client
.post(&url)
.body("Hello")
.body(Body::wrap_stream(source))
.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(), &"post");
assert_eq!(
res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(),
&"0"
);
assert_eq!(res.text().unwrap().len(), 0)
}
#[test]
fn test_post_form() {
let server = server! {
request: b"\
POST /form HTTP/1.1\r\n\
content-type: application/x-www-form-urlencoded\r\n\
content-length: 24\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
hello=world&sean=monstar\
",
response: b"\
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 url = format!("http://{}/form", server.addr());
let res = reqwest::Client::new()
.post(&url)
.form(form)
.send()
.expect("request send");
.await
.expect("Failed to post");
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
}
/// Calling `Response::error_for_status`` on a response with status in 4xx
/// returns a error.
#[test]
fn test_error_for_status_4xx() {
let server = server! {
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 400 OK\r\n\
Server: test\r\n\
Content-Length: 0\r\n\
\r\n\
"
};
let url = format!("http://{}/1", server.addr());
let res = reqwest::get(&url).unwrap();
let err = res.error_for_status().err().unwrap();
assert!(err.is_client_error());
assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST));
}
/// Calling `Response::error_for_status`` on a response with status in 5xx
/// returns a error.
#[test]
fn test_error_for_status_5xx() {
let server = server! {
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 500 OK\r\n\
Server: test\r\n\
Content-Length: 0\r\n\
\r\n\
"
};
let url = format!("http://{}/1", server.addr());
let res = reqwest::get(&url).unwrap();
let err = res.error_for_status().err().unwrap();
assert!(err.is_server_error());
assert_eq!(
err.status(),
Some(reqwest::StatusCode::INTERNAL_SERVER_ERROR)
);
}
#[test]
fn test_default_headers() {
use reqwest::header;
let mut headers = header::HeaderMap::with_capacity(1);
headers.insert(header::COOKIE, header::HeaderValue::from_static("a=b;c=d"));
let client = reqwest::Client::builder()
.default_headers(headers)
.build()
.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 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"
);
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]
fn test_override_default_headers() {
use reqwest::header;
let mut headers = header::HeaderMap::with_capacity(1);
headers.insert(
header::AUTHORIZATION,
header::HeaderValue::from_static("iamatoken"),
);
let client = reqwest::Client::builder()
.default_headers(headers)
.build()
.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 res = client
.get(&url)
.header(
header::AUTHORIZATION,
header::HeaderValue::from_static("secret"),
)
.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]
fn test_appended_headers_not_overwritten() {
let client = reqwest::Client::new();
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 res = client
.get(&url)
.header(header::ACCEPT, "application/json")
.header(header::ACCEPT, "application/json+hal")
.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"
);
// make sure this also works with default headers
use reqwest::header;
let mut headers = header::HeaderMap::with_capacity(1);
headers.insert(
header::ACCEPT,
header::HeaderValue::from_static("text/html"),
);
let client = reqwest::Client::builder()
.default_headers(headers)
.build()
.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 res = client
.get(&url)
.header(header::ACCEPT, "application/json")
.header(header::ACCEPT, "application/json+hal")
.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"
);
}

View File

@@ -41,7 +41,7 @@ fn test_gzip_response() {
write_timeout: Duration::from_millis(10),
response: response
};
let mut res = reqwest::get(&format!("http://{}/gzip", server.addr())).unwrap();
let mut res = reqwest::blocking::get(&format!("http://{}/gzip", server.addr())).unwrap();
let mut body = String::new();
res.read_to_string(&mut body).unwrap();
@@ -68,7 +68,7 @@ fn test_gzip_empty_body() {
\r\n"
};
let client = reqwest::Client::new();
let client = reqwest::blocking::Client::new();
let mut res = client
.head(&format!("http://{}/gzip", server.addr()))
.send()
@@ -100,7 +100,7 @@ fn test_gzip_invalid_body() {
0"
};
let mut res = reqwest::get(&format!("http://{}/gzip", server.addr())).unwrap();
let mut res = reqwest::blocking::get(&format!("http://{}/gzip", server.addr())).unwrap();
// this tests that the request.send() didn't error, but that the error
// is in reading the body
@@ -126,7 +126,7 @@ fn test_accept_header_is_not_changed_if_set() {
\r\n\
"
};
let client = reqwest::Client::new();
let client = reqwest::blocking::Client::new();
let res = client
.get(&format!("http://{}/accept", server.addr()))
@@ -158,7 +158,7 @@ fn test_accept_encoding_header_is_not_changed_if_set() {
\r\n\
"
};
let client = reqwest::Client::new();
let client = reqwest::blocking::Client::new();
let res = client
.get(&format!("http://{}/accept-encoding", server.addr()))

View File

@@ -5,7 +5,7 @@ mod support;
fn text_part() {
let _ = env_logger::try_init();
let form = reqwest::multipart::Form::new().text("foo", "bar");
let form = reqwest::blocking::multipart::Form::new().text("foo", "bar");
let expected_body = format!(
"\
@@ -39,7 +39,7 @@ fn text_part() {
let url = format!("http://{}/multipart/1", server.addr());
let res = reqwest::Client::new()
let res = reqwest::blocking::Client::new()
.post(&url)
.multipart(form)
.send()
@@ -53,7 +53,7 @@ fn text_part() {
fn file() {
let _ = env_logger::try_init();
let form = reqwest::multipart::Form::new()
let form = reqwest::blocking::multipart::Form::new()
.file("foo", "Cargo.lock")
.unwrap();
@@ -93,7 +93,7 @@ fn file() {
let url = format!("http://{}/multipart/2", server.addr());
let res = reqwest::Client::new()
let res = reqwest::blocking::Client::new()
.post(&url)
.multipart(form)
.send()

View File

@@ -3,8 +3,8 @@ mod support;
use std::env;
#[test]
fn http_proxy() {
#[tokio::test]
async fn http_proxy() {
let server = server! {
request: b"\
GET http://hyper.rs/prox HTTP/1.1\r\n\
@@ -31,6 +31,7 @@ fn http_proxy() {
.unwrap()
.get(url)
.send()
.await
.unwrap();
assert_eq!(res.url().as_str(), url);
@@ -41,8 +42,8 @@ fn http_proxy() {
);
}
#[test]
fn http_proxy_basic_auth() {
#[tokio::test]
async fn http_proxy_basic_auth() {
let server = server! {
request: b"\
GET http://hyper.rs/prox HTTP/1.1\r\n\
@@ -74,6 +75,7 @@ fn http_proxy_basic_auth() {
.unwrap()
.get(url)
.send()
.await
.unwrap();
assert_eq!(res.url().as_str(), url);
@@ -84,8 +86,8 @@ fn http_proxy_basic_auth() {
);
}
#[test]
fn http_proxy_basic_auth_parsed() {
#[tokio::test]
async fn http_proxy_basic_auth_parsed() {
let server = server! {
request: b"\
GET http://hyper.rs/prox HTTP/1.1\r\n\
@@ -113,6 +115,7 @@ fn http_proxy_basic_auth_parsed() {
.unwrap()
.get(url)
.send()
.await
.unwrap();
assert_eq!(res.url().as_str(), url);
@@ -123,8 +126,8 @@ fn http_proxy_basic_auth_parsed() {
);
}
#[test]
fn test_no_proxy() {
#[tokio::test]
async fn test_no_proxy() {
let server = server! {
request: b"\
GET /4 HTTP/1.1\r\n\
@@ -152,14 +155,15 @@ fn test_no_proxy() {
.unwrap()
.get(&url)
.send()
.await
.unwrap();
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
}
#[test]
fn test_using_system_proxy() {
#[tokio::test]
async fn test_using_system_proxy() {
let server = server! {
request: b"\
GET http://hyper.rs/prox HTTP/1.1\r\n\
@@ -188,6 +192,7 @@ fn test_using_system_proxy() {
.unwrap()
.get(url)
.send()
.await
.unwrap();
assert_eq!(res.url().as_str(), url);

View File

@@ -1,8 +1,8 @@
#[macro_use]
mod support;
#[test]
fn test_redirect_301_and_302_and_303_changes_post_to_get() {
#[tokio::test]
async fn test_redirect_301_and_302_and_303_changes_post_to_get() {
let client = reqwest::Client::new();
let codes = [301, 302, 303];
@@ -45,7 +45,7 @@ fn test_redirect_301_and_302_and_303_changes_post_to_get() {
let url = format!("http://{}/{}", redirect.addr(), code);
let dst = format!("http://{}/{}", redirect.addr(), "dst");
let res = client.post(&url).send().unwrap();
let res = client.post(&url).send().await.unwrap();
assert_eq!(res.url().as_str(), dst);
assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!(
@@ -55,8 +55,8 @@ fn test_redirect_301_and_302_and_303_changes_post_to_get() {
}
}
#[test]
fn test_redirect_307_and_308_tries_to_get_again() {
#[tokio::test]
async fn test_redirect_307_and_308_tries_to_get_again() {
let client = reqwest::Client::new();
let codes = [307, 308];
for code in codes.iter() {
@@ -98,7 +98,7 @@ fn test_redirect_307_and_308_tries_to_get_again() {
let url = format!("http://{}/{}", redirect.addr(), code);
let dst = format!("http://{}/{}", redirect.addr(), "dst");
let res = client.get(&url).send().unwrap();
let res = client.get(&url).send().await.unwrap();
assert_eq!(res.url().as_str(), dst);
assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!(
@@ -108,19 +108,19 @@ fn test_redirect_307_and_308_tries_to_get_again() {
}
}
#[test]
fn test_redirect_307_and_308_tries_to_post_again() {
#[tokio::test]
async fn test_redirect_307_and_308_tries_to_post_again() {
let client = reqwest::Client::new();
let codes = [307, 308];
for code in codes.iter() {
let redirect = server! {
request: format!("\
POST /{} HTTP/1.1\r\n\
content-length: 5\r\n\
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),
@@ -136,12 +136,12 @@ fn test_redirect_307_and_308_tries_to_post_again() {
request: format!("\
POST /dst HTTP/1.1\r\n\
content-length: 5\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
referer: http://$HOST/{}\r\n\
host: $HOST\r\n\
content-length: 5\r\n\
\r\n\
Hello\
", code),
@@ -155,7 +155,7 @@ fn test_redirect_307_and_308_tries_to_post_again() {
let url = format!("http://{}/{}", redirect.addr(), code);
let dst = format!("http://{}/{}", redirect.addr(), "dst");
let res = client.post(&url).body("Hello").send().unwrap();
let res = client.post(&url).body("Hello").send().await.unwrap();
assert_eq!(res.url().as_str(), dst);
assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!(
@@ -167,7 +167,7 @@ fn test_redirect_307_and_308_tries_to_post_again() {
#[test]
fn test_redirect_307_does_not_try_if_reader_cannot_reset() {
let client = reqwest::Client::new();
let client = reqwest::blocking::Client::new();
let codes = [307, 308];
for &code in codes.iter() {
let redirect = server! {
@@ -196,7 +196,7 @@ fn test_redirect_307_does_not_try_if_reader_cannot_reset() {
let url = format!("http://{}/{}", redirect.addr(), code);
let res = client
.post(&url)
.body(reqwest::Body::new(&b"Hello"[..]))
.body(reqwest::blocking::Body::new(&b"Hello"[..]))
.send()
.unwrap();
assert_eq!(res.url().as_str(), url);
@@ -204,8 +204,8 @@ fn test_redirect_307_does_not_try_if_reader_cannot_reset() {
}
}
#[test]
fn test_redirect_removes_sensitive_headers() {
#[tokio::test]
async fn test_redirect_removes_sensitive_headers() {
let end_server = server! {
request: b"\
GET /otherhost HTTP/1.1\r\n\
@@ -252,11 +252,12 @@ fn test_redirect_removes_sensitive_headers() {
reqwest::header::HeaderValue::from_static("foo=bar"),
)
.send()
.await
.unwrap();
}
#[test]
fn test_redirect_policy_can_return_errors() {
#[tokio::test]
async fn test_redirect_policy_can_return_errors() {
let server = server! {
request: b"\
GET /loop HTTP/1.1\r\n\
@@ -274,13 +275,13 @@ fn test_redirect_policy_can_return_errors() {
\r\n\
"
};
let err = reqwest::get(&format!("http://{}/loop", server.addr())).unwrap_err();
let url = format!("http://{}/loop", server.addr());
let err = reqwest::get(&url).await.unwrap_err();
assert!(err.is_redirect());
}
#[test]
fn test_redirect_policy_can_stop_redirects_without_an_error() {
#[tokio::test]
async fn test_redirect_policy_can_stop_redirects_without_an_error() {
let server = server! {
request: b"\
GET /no-redirect HTTP/1.1\r\n\
@@ -307,6 +308,7 @@ fn test_redirect_policy_can_stop_redirects_without_an_error() {
.unwrap()
.get(&url)
.send()
.await
.unwrap();
assert_eq!(res.url().as_str(), url);
@@ -317,8 +319,8 @@ fn test_redirect_policy_can_stop_redirects_without_an_error() {
);
}
#[test]
fn test_referer_is_not_set_if_disabled() {
#[tokio::test]
async fn test_referer_is_not_set_if_disabled() {
let server = server! {
request: b"\
GET /no-refer HTTP/1.1\r\n\
@@ -357,14 +359,14 @@ fn test_referer_is_not_set_if_disabled() {
.referer(false)
.build()
.unwrap()
//client
.get(&format!("http://{}/no-refer", server.addr()))
.send()
.await
.unwrap();
}
#[test]
fn test_invalid_location_stops_redirect_gh484() {
#[tokio::test]
async fn test_invalid_location_stops_redirect_gh484() {
let server = server! {
request: b"\
GET /yikes HTTP/1.1\r\n\
@@ -385,7 +387,7 @@ fn test_invalid_location_stops_redirect_gh484() {
let url = format!("http://{}/yikes", server.addr());
let res = reqwest::get(&url).unwrap();
let res = reqwest::get(&url).await.unwrap();
assert_eq!(res.url().as_str(), url);
assert_eq!(res.status(), reqwest::StatusCode::FOUND);
@@ -395,8 +397,8 @@ fn test_invalid_location_stops_redirect_gh484() {
);
}
#[test]
fn test_redirect_302_with_set_cookies() {
#[tokio::test]
async fn test_redirect_302_with_set_cookies() {
let code = 302;
let client = reqwest::ClientBuilder::new()
.cookie_store(true)
@@ -442,7 +444,7 @@ fn test_redirect_302_with_set_cookies() {
let url = format!("http://{}/{}", server.addr(), code);
let dst = format!("http://{}/{}", server.addr(), "dst");
let res = client.get(&url).send().unwrap();
let res = client.get(&url).send().await.unwrap();
assert_eq!(res.url().as_str(), dst);
assert_eq!(res.status(), reqwest::StatusCode::OK);

View File

@@ -11,7 +11,7 @@ fn timeout_closes_connection() {
// Make Client drop *after* the Server, so the background doesn't
// close too early.
let client = reqwest::Client::builder()
let client = reqwest::blocking::Client::builder()
.timeout(Duration::from_millis(500))
.build()
.unwrap();
@@ -50,7 +50,7 @@ fn write_timeout_large_body() {
// Make Client drop *after* the Server, so the background doesn't
// close too early.
let client = reqwest::Client::builder()
let client = reqwest::blocking::Client::builder()
.timeout(Duration::from_millis(500))
.build()
.unwrap();
@@ -80,7 +80,7 @@ fn write_timeout_large_body() {
let url = format!("http://{}/write-timeout", server.addr());
let err = client
.post(&url)
.body(reqwest::Body::sized(cursor, len as u64))
.body(reqwest::blocking::Body::sized(cursor, len as u64))
.send()
.unwrap_err();
@@ -108,7 +108,7 @@ fn test_response_timeout() {
};
let url = format!("http://{}/response-timeout", server.addr());
let err = reqwest::Client::builder()
let err = reqwest::blocking::Client::builder()
.timeout(Duration::from_millis(500))
.build()
.unwrap()
@@ -142,7 +142,7 @@ fn test_read_timeout() {
};
let url = format!("http://{}/read-timeout", server.addr());
let mut res = reqwest::Client::builder()
let mut res = reqwest::blocking::Client::builder()
.timeout(Duration::from_millis(500))
.build()
.unwrap()