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 ## Example
```rust,no_run Async:
extern crate reqwest;
```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; use std::collections::HashMap;
fn main() -> Result<(), Box<std::error::Error>> { 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()?; .json()?;
println!("{:#?}", resp); println!("{:#?}", resp);
Ok(()) 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() reqwest::Client::new()
.post("http://www.baidu.com") .post("http://www.baidu.com")
.form(&[("one", "1")]) .form(&[("one", "1")])
.send() .send()
.await
.unwrap(); .unwrap();
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -186,7 +186,7 @@ impl Response {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// # use reqwest::r#async::Response; /// # use reqwest::Response;
/// fn on_response(res: Response) { /// fn on_response(res: Response) {
/// match res.error_for_status() { /// match res.error_for_status() {
/// Ok(_res) => (), /// Ok(_res) => (),
@@ -216,7 +216,7 @@ impl Response {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// # use reqwest::r#async::Response; /// # use reqwest::Response;
/// fn on_response(res: &Response) { /// fn on_response(res: &Response) {
/// match res.error_for_status_ref() { /// match res.error_for_status_ref() {
/// Ok(_res) => (), /// Ok(_res) => (),
@@ -347,25 +347,6 @@ struct ResponseUrl(Url);
pub trait ResponseBuilderExt { pub trait ResponseBuilderExt {
/// A builder method for the `http::response::Builder` type that allows the user to add a `Url` /// A builder method for the `http::response::Builder` type that allows the user to add a `Url`
/// to the `http::Response` /// 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; fn url(&mut self, url: Url) -> &mut Self;
} }

View File

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

View File

@@ -10,9 +10,10 @@ use futures::{StreamExt, TryFutureExt};
use log::{error, trace}; use log::{error, trace};
use crate::request::{Request, RequestBuilder}; use super::request::{Request, RequestBuilder};
use crate::response::Response; use super::response::Response;
use crate::{async_impl, header, wait, IntoUrl, Method, Proxy, RedirectPolicy}; use super::wait;
use crate::{async_impl, header, IntoUrl, Method, Proxy, RedirectPolicy};
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
use crate::{Certificate, Identity}; use crate::{Certificate, Identity};
@@ -28,9 +29,9 @@ use crate::{Certificate, Identity};
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # use reqwest::{Error, Client}; /// use reqwest::blocking::Client;
/// # /// #
/// # fn run() -> Result<(), Error> { /// # fn run() -> Result<(), reqwest::Error> {
/// let client = Client::new(); /// let client = Client::new();
/// let resp = client.get("http://httpbin.org/").send()?; /// let resp = client.get("http://httpbin.org/").send()?;
/// # drop(resp); /// # drop(resp);
@@ -51,7 +52,7 @@ pub struct Client {
/// # fn run() -> Result<(), reqwest::Error> { /// # fn run() -> Result<(), reqwest::Error> {
/// use std::time::Duration; /// use std::time::Duration;
/// ///
/// let client = reqwest::Client::builder() /// let client = reqwest::blocking::Client::builder()
/// .gzip(true) /// .gzip(true)
/// .timeout(Duration::from_secs(10)) /// .timeout(Duration::from_secs(10))
/// .build()?; /// .build()?;
@@ -130,7 +131,7 @@ impl ClientBuilder {
/// let cert = reqwest::Certificate::from_der(&buf)?; /// let cert = reqwest::Certificate::from_der(&buf)?;
/// ///
/// // get a client builder /// // get a client builder
/// let client = reqwest::Client::builder() /// let client = reqwest::blocking::Client::builder()
/// .add_root_certificate(cert) /// .add_root_certificate(cert)
/// .build()?; /// .build()?;
/// # drop(client); /// # drop(client);
@@ -170,7 +171,7 @@ impl ClientBuilder {
/// let pkcs12 = reqwest::Identity::from_pem(&buf)?; /// let pkcs12 = reqwest::Identity::from_pem(&buf)?;
/// ///
/// // get a client builder /// // get a client builder
/// let client = reqwest::Client::builder() /// let client = reqwest::blocking::Client::builder()
/// .identity(pkcs12) /// .identity(pkcs12)
/// .build()?; /// .build()?;
/// # drop(client); /// # drop(client);
@@ -224,7 +225,7 @@ impl ClientBuilder {
/// headers.insert(header::AUTHORIZATION, header::HeaderValue::from_static("secret")); /// headers.insert(header::AUTHORIZATION, header::HeaderValue::from_static("secret"));
/// ///
/// // get a client builder /// // get a client builder
/// let client = reqwest::Client::builder() /// let client = reqwest::blocking::Client::builder()
/// .default_headers(headers) /// .default_headers(headers)
/// .build()?; /// .build()?;
/// let res = client.get("https://www.rust-lang.org").send()?; /// 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")); /// headers.insert(header::AUTHORIZATION, header::HeaderValue::from_static("secret"));
/// ///
/// // get a client builder /// // get a client builder
/// let client = reqwest::Client::builder() /// let client = reqwest::blocking::Client::builder()
/// .default_headers(headers) /// .default_headers(headers)
/// .build()?; /// .build()?;
/// let res = client /// let res = client
@@ -337,7 +338,7 @@ impl ClientBuilder {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// let client = reqwest::Client::builder() /// let client = reqwest::blocking::Client::builder()
/// .h2_prior_knowledge() /// .h2_prior_knowledge()
/// .build().unwrap(); /// .build().unwrap();
/// ``` /// ```
@@ -350,7 +351,7 @@ impl ClientBuilder {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// let client = reqwest::Client::builder() /// let client = reqwest::blocking::Client::builder()
/// .http1_title_case_headers() /// .http1_title_case_headers()
/// .build().unwrap(); /// .build().unwrap();
/// ``` /// ```
@@ -365,7 +366,7 @@ impl ClientBuilder {
/// ``` /// ```
/// use std::net::IpAddr; /// use std::net::IpAddr;
/// let local_addr = IpAddr::from([12, 4, 1, 8]); /// let local_addr = IpAddr::from([12, 4, 1, 8]);
/// let client = reqwest::Client::builder() /// let client = reqwest::blocking::Client::builder()
/// .local_address(local_addr) /// .local_address(local_addr)
/// .build().unwrap(); /// .build().unwrap();
/// ``` /// ```
@@ -386,7 +387,7 @@ impl ClientBuilder {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// let client = reqwest::Client::builder() /// let client = reqwest::blocking::Client::builder()
/// .cookie_store(true) /// .cookie_store(true)
/// .build() /// .build()
/// .unwrap(); /// .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 //! # Example
//! //!
//! ``` //! ```
//! use reqwest::multipart; //! use reqwest::blocking::multipart;
//! //!
//! # fn run() -> Result<(), Box<dyn std::error::Error>> { //! # fn run() -> Result<(), Box<dyn std::error::Error>> {
//! let form = multipart::Form::new() //! let form = multipart::Form::new()
@@ -25,7 +25,7 @@
//! let form = form.part("biography", bio); //! let form = form.part("biography", bio);
//! //!
//! // And finally, send the form //! // And finally, send the form
//! let client = reqwest::Client::new(); //! let client = reqwest::blocking::Client::new();
//! let resp = client //! let resp = client
//! .post("http://localhost:8080/user") //! .post("http://localhost:8080/user")
//! .multipart(form) //! .multipart(form)
@@ -44,8 +44,8 @@ use std::path::Path;
use mime_guess::{self, Mime}; use mime_guess::{self, Mime};
use super::Body;
use crate::async_impl::multipart::{FormParts, PartMetadata, PartProps}; use crate::async_impl::multipart::{FormParts, PartMetadata, PartProps};
use crate::Body;
/// A multipart/form-data request. /// A multipart/form-data request.
pub struct Form { pub struct Form {
@@ -77,7 +77,7 @@ impl Form {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// let form = reqwest::multipart::Form::new() /// let form = reqwest::blocking::multipart::Form::new()
/// .text("username", "seanmonstar") /// .text("username", "seanmonstar")
/// .text("password", "secret"); /// .text("password", "secret");
/// ``` /// ```
@@ -97,7 +97,7 @@ impl Form {
/// ///
/// ```no_run /// ```no_run
/// # fn run() -> std::io::Result<()> { /// # fn run() -> std::io::Result<()> {
/// let files = reqwest::multipart::Form::new() /// let files = reqwest::blocking::multipart::Form::new()
/// .file("key", "/path/to/file")?; /// .file("key", "/path/to/file")?;
/// # Ok(()) /// # Ok(())
/// # } /// # }

View File

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

View File

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

View File

@@ -20,15 +20,15 @@ use crate::{StatusCode, Url};
/// } /// }
/// # fn main() { } /// # fn main() { }
/// ///
/// fn run() { /// async fn run() {
/// match make_request() { /// match make_request().await {
/// Err(e) => handler(e), /// Err(e) => handler(e),
/// Ok(_) => return, /// Ok(_) => return,
/// } /// }
/// } /// }
/// // Response is not a json object conforming to the Simple struct /// // Response is not a json object conforming to the Simple struct
/// fn make_request() -> Result<Simple, reqwest::Error> { /// async fn make_request() -> Result<Simple, reqwest::Error> {
/// reqwest::get("http://httpbin.org/ip")?.json() /// reqwest::get("http://httpbin.org/ip").await?.json().await
/// } /// }
/// ///
/// fn handler(e: reqwest::Error) { /// fn handler(e: reqwest::Error) {
@@ -75,9 +75,9 @@ impl Error {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # fn run() { /// # async fn run() {
/// // displays last stop of a redirect loop /// // 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 let Err(e) = response {
/// if e.is_redirect() { /// if e.is_redirect() {
/// if let Some(final_stop) = e.url() { /// if let Some(final_stop) = e.url() {
@@ -108,14 +108,14 @@ impl Error {
/// extern crate url; /// extern crate url;
/// # extern crate reqwest; /// # extern crate reqwest;
/// // retries requests with no host on localhost /// // retries requests with no host on localhost
/// # fn run() { /// # async fn run() {
/// let invalid_request = "http://"; /// let invalid_request = "http://";
/// let mut response = reqwest::get(invalid_request); /// let mut response = reqwest::get(invalid_request).await;
/// if let Err(e) = response { /// if let Err(e) = response {
/// match e.get_ref().and_then(|e| e.downcast_ref::<url::ParseError>()) { /// match e.get_ref().and_then(|e| e.downcast_ref::<url::ParseError>()) {
/// Some(&url::ParseError::EmptyHost) => { /// Some(&url::ParseError::EmptyHost) => {
/// let valid_request = format!("{}{}",invalid_request, "localhost"); /// 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() {} /// # fn main() {}
/// ``` /// ```
#[inline]
pub fn get_ref(&self) -> Option<&(dyn StdError + Send + Sync + 'static)> { pub fn get_ref(&self) -> Option<&(dyn StdError + Send + Sync + 'static)> {
match self.inner.kind { match self.inner.kind {
Kind::Http(ref e) => Some(e), Kind::Http(ref e) => Some(e),
@@ -152,7 +151,6 @@ impl Error {
} }
/// Returns true if the error is related to HTTP. /// Returns true if the error is related to HTTP.
#[inline]
pub fn is_http(&self) -> bool { pub fn is_http(&self) -> bool {
match self.inner.kind { match self.inner.kind {
Kind::Http(_) => true, Kind::Http(_) => true,
@@ -175,7 +173,6 @@ impl Error {
} }
/// Returns true if the error is serialization related. /// Returns true if the error is serialization related.
#[inline]
pub fn is_serialization(&self) -> bool { pub fn is_serialization(&self) -> bool {
match self.inner.kind { match self.inner.kind {
Kind::Json(_) | Kind::UrlEncoded(_) => true, Kind::Json(_) | Kind::UrlEncoded(_) => true,
@@ -184,7 +181,6 @@ impl Error {
} }
/// Returns true if the error is from a `RedirectPolicy`. /// Returns true if the error is from a `RedirectPolicy`.
#[inline]
pub fn is_redirect(&self) -> bool { pub fn is_redirect(&self) -> bool {
match self.inner.kind { match self.inner.kind {
Kind::TooManyRedirects | Kind::RedirectLoop => true, Kind::TooManyRedirects | Kind::RedirectLoop => true,
@@ -193,7 +189,6 @@ impl Error {
} }
/// Returns true if the error is from a request returning a 4xx error. /// Returns true if the error is from a request returning a 4xx error.
#[inline]
pub fn is_client_error(&self) -> bool { pub fn is_client_error(&self) -> bool {
match self.inner.kind { match self.inner.kind {
Kind::Status(code) => code.is_client_error(), 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. /// Returns true if the error is from a request returning a 5xx error.
#[inline]
pub fn is_server_error(&self) -> bool { pub fn is_server_error(&self) -> bool {
match self.inner.kind { match self.inner.kind {
Kind::Status(code) => code.is_server_error(), 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. /// Returns the status code, if the error was generated from a response.
#[inline]
pub fn status(&self) -> Option<StatusCode> { pub fn status(&self) -> Option<StatusCode> {
match self.inner.kind { match self.inner.kind {
Kind::Status(code) => Some(code), 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 where
T: Into<Kind>, T: Into<Kind>,
{ {
fn from(err: crate::wait::Waited<T>) -> Kind { fn from(err: crate::blocking::Waited<T>) -> Kind {
match err { match err {
crate::wait::Waited::TimedOut => io_timeout().into(), crate::blocking::Waited::TimedOut => io_timeout().into(),
crate::wait::Waited::Executor(e) => e.into(), crate::blocking::Waited::Executor(e) => e.into(),
crate::wait::Waited::Inner(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 //! It handles many of the things that most people just expect an HTTP client
//! to do for them. //! to do for them.
//! //!
//! - Async and [blocking](blocking) Clients
//! - Plain bodies, [JSON](#json), [urlencoded](#forms), [multipart](multipart) //! - Plain bodies, [JSON](#json), [urlencoded](#forms), [multipart](multipart)
//! - Customizable [redirect policy](#redirect-policy) //! - Customizable [redirect policy](#redirect-policy)
//! - HTTP [Proxies](#proxies) //! - HTTP [Proxies](#proxies)
//! - Uses system-native [TLS](#tls) //! - Uses system-native [TLS](#tls)
//! - Cookies //! - Cookies
//! //!
//! The [`reqwest::Client`][client] is synchronous, making it a great fit for //! The [`reqwest::Client`][client] is asynchronous. For applications wishing
//! applications that only require a few HTTP requests, and wish to handle //! to only make a few HTTP requests, the [`reqwest::blocking`](blocking) API
//! them synchronously. //! may be more convenient.
//! //!
//! Additional learning resources include: //! Additional learning resources include:
//! //!
@@ -31,22 +32,17 @@
//! For a single request, you can use the [`get`][get] shortcut method. //! For a single request, you can use the [`get`][get] shortcut method.
//! //!
//! ```rust //! ```rust
//! # use reqwest::{Error, Response}; //! # async fn run() -> Result<(), reqwest::Error> {
//! //! let body = reqwest::get("https://www.rust-lang.org")
//! # fn run() -> Result<(), Error> { //! .await?
//! let body = reqwest::get("https://www.rust-lang.org")? //! .text()
//! .text()?; //! .await?;
//! //!
//! println!("body = {:?}", body); //! println!("body = {:?}", body);
//! # Ok(()) //! # 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 //! **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 //! [`Client`][client] and reuse it, taking advantage of keep-alive connection
//! pooling. //! pooling.
@@ -57,16 +53,17 @@
//! by using the `body()` method of a [`RequestBuilder`][builder]. This lets you set the //! 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, //! 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 //! 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 //! ```rust
//! # use reqwest::Error; //! # use reqwest::Error;
//! # //! #
//! # fn run() -> Result<(), Error> { //! # async fn run() -> Result<(), Error> {
//! let client = reqwest::Client::new(); //! let client = reqwest::Client::new();
//! let res = client.post("http://httpbin.org/post") //! let res = client.post("http://httpbin.org/post")
//! .body("the exact body that is sent") //! .body("the exact body that is sent")
//! .send()?; //! .send()
//! .await?;
//! # Ok(()) //! # Ok(())
//! # } //! # }
//! ``` //! ```
@@ -82,13 +79,14 @@
//! ```rust //! ```rust
//! # use reqwest::Error; //! # use reqwest::Error;
//! # //! #
//! # fn run() -> Result<(), Error> { //! # async fn run() -> Result<(), Error> {
//! // This will POST a body of `foo=bar&baz=quux` //! // This will POST a body of `foo=bar&baz=quux`
//! let params = [("foo", "bar"), ("baz", "quux")]; //! let params = [("foo", "bar"), ("baz", "quux")];
//! let client = reqwest::Client::new(); //! let client = reqwest::Client::new();
//! let res = client.post("http://httpbin.org/post") //! let res = client.post("http://httpbin.org/post")
//! .form(&params) //! .form(&params)
//! .send()?; //! .send()
//! .await?;
//! # Ok(()) //! # Ok(())
//! # } //! # }
//! ``` //! ```
@@ -103,7 +101,7 @@
//! # use reqwest::Error; //! # use reqwest::Error;
//! # use std::collections::HashMap; //! # use std::collections::HashMap;
//! # //! #
//! # fn run() -> Result<(), Error> { //! # async fn run() -> Result<(), Error> {
//! // This will POST a body of `{"lang":"rust","body":"json"}` //! // This will POST a body of `{"lang":"rust","body":"json"}`
//! let mut map = HashMap::new(); //! let mut map = HashMap::new();
//! map.insert("lang", "rust"); //! map.insert("lang", "rust");
@@ -112,7 +110,8 @@
//! let client = reqwest::Client::new(); //! let client = reqwest::Client::new();
//! let res = client.post("http://httpbin.org/post") //! let res = client.post("http://httpbin.org/post")
//! .json(&map) //! .json(&map)
//! .send()?; //! .send()
//! .await?;
//! # Ok(()) //! # Ok(())
//! # } //! # }
//! ``` //! ```
@@ -157,8 +156,6 @@
//! `native-tls` library to connect over HTTPS. //! `native-tls` library to connect over HTTPS.
//! - **default-tls-vendored**: Enables the `vendored` feature of `native-tls`. //! - **default-tls-vendored**: Enables the `vendored` feature of `native-tls`.
//! - **rustls-tls**: Provides TLS support via the `rustls` library. //! - **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 //! [hyper]: http://hyper.rs
@@ -171,12 +168,11 @@
//! [Proxy]: ./struct.Proxy.html //! [Proxy]: ./struct.Proxy.html
//! [cargo-features]: https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-features-section //! [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 ////! - **trust-dns**: Enables a trust-dns async resolver instead of default
////! threadpool using `getaddrinfo`. ////! threadpool using `getaddrinfo`.
extern crate cookie as cookie_crate; extern crate cookie as cookie_crate;
#[cfg(feature = "hyper-011")]
pub use hyper_old_types as hyper_011;
#[cfg(test)] #[cfg(test)]
#[macro_use] #[macro_use]
@@ -191,14 +187,17 @@ pub use hyper::{StatusCode, Version};
pub use url::ParseError as UrlError; pub use url::ParseError as UrlError;
pub use url::Url; pub use url::Url;
pub use self::body::Body; pub use self::r#async::{
pub use self::client::{Client, ClientBuilder}; 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::error::{Error, Result};
pub use self::into_url::IntoUrl; pub use self::into_url::IntoUrl;
pub use self::proxy::Proxy; pub use self::proxy::Proxy;
pub use self::redirect::{RedirectAction, RedirectAttempt, RedirectPolicy}; pub use self::redirect::{RedirectAction, RedirectAttempt, RedirectPolicy};
pub use self::request::{Request, RequestBuilder}; //pub use self::request::{Request, RequestBuilder};
pub use self::response::Response; //pub use self::response::Response;
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
pub use self::tls::{Certificate, Identity}; pub use self::tls::{Certificate, Identity};
@@ -207,8 +206,7 @@ pub use self::tls::{Certificate, Identity};
mod error; mod error;
mod async_impl; mod async_impl;
mod body; pub mod blocking;
mod client;
mod connect; mod connect;
pub mod cookie; pub mod cookie;
//#[cfg(feature = "trust-dns")] //#[cfg(feature = "trust-dns")]
@@ -216,19 +214,16 @@ pub mod cookie;
mod into_url; mod into_url;
mod proxy; mod proxy;
mod redirect; mod redirect;
mod request;
mod response;
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
mod 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 mod r#async {
pub use crate::async_impl::{ pub use crate::async_impl::{
multipart, Body, Chunk, Client, ClientBuilder, Decoder, Request, RequestBuilder, Response, multipart, Body, Chunk, Client, ClientBuilder, Decoder, Request, RequestBuilder, Response,
ResponseBuilderExt,
}; };
} }
@@ -244,12 +239,11 @@ pub mod r#async {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # fn run() -> Result<(), reqwest::Error> { /// # async fn run() -> Result<(), reqwest::Error> {
/// let body = reqwest::get("https://www.rust-lang.org")? /// let body = reqwest::get("https://www.rust-lang.org").await?
/// .text()?; /// .text().await?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// # fn main() { }
/// ``` /// ```
/// ///
/// # Errors /// # Errors
@@ -261,8 +255,8 @@ pub mod r#async {
/// - there was an error while sending request /// - there was an error while sending request
/// - redirect loop was detected /// - redirect loop was detected
/// - redirect limit was exhausted /// - redirect limit was exhausted
pub fn get<T: IntoUrl>(url: T) -> crate::Result<Response> { pub async fn get<T: IntoUrl>(url: T) -> crate::Result<Response> {
Client::builder().build()?.get(url).send() Client::builder().build()?.get(url).send().await
} }
fn _assert_impls() { 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")] #[cfg(feature = "tls")]
#[test] #[tokio::test]
fn test_badssl_modern() { async fn test_badssl_modern() {
let text = reqwest::get("https://mozilla-modern.badssl.com/") let text = reqwest::get("https://mozilla-modern.badssl.com/")
.await
.unwrap() .unwrap()
.text() .text()
.await
.unwrap(); .unwrap();
assert!(text.contains("<title>mozilla-modern.badssl.com</title>")); assert!(text.contains("<title>mozilla-modern.badssl.com</title>"));
} }
#[cfg(feature = "rustls-tls")] #[cfg(feature = "rustls-tls")]
#[test] #[tokio::test]
fn test_rustls_badssl_modern() { async fn test_rustls_badssl_modern() {
let text = reqwest::Client::builder() let text = reqwest::Client::builder()
.use_rustls_tls() .use_rustls_tls()
.build() .build()
.unwrap() .unwrap()
.get("https://mozilla-modern.badssl.com/") .get("https://mozilla-modern.badssl.com/")
.send() .send()
.await
.unwrap() .unwrap()
.text() .text()
.await
.unwrap(); .unwrap();
assert!(text.contains("<title>mozilla-modern.badssl.com</title>")); assert!(text.contains("<title>mozilla-modern.badssl.com</title>"));
} }
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
#[test] #[tokio::test]
fn test_badssl_self_signed() { async fn test_badssl_self_signed() {
let text = reqwest::Client::builder() let text = reqwest::Client::builder()
.danger_accept_invalid_certs(true) .danger_accept_invalid_certs(true)
.build() .build()
.unwrap() .unwrap()
.get("https://self-signed.badssl.com/") .get("https://self-signed.badssl.com/")
.send() .send()
.await
.unwrap() .unwrap()
.text() .text()
.await
.unwrap(); .unwrap();
assert!(text.contains("<title>self-signed.badssl.com</title>")); assert!(text.contains("<title>self-signed.badssl.com</title>"));
} }
#[cfg(feature = "default-tls")] #[cfg(feature = "default-tls")]
#[test] #[tokio::test]
fn test_badssl_wrong_host() { async fn test_badssl_wrong_host() {
let text = reqwest::Client::builder() let text = reqwest::Client::builder()
.danger_accept_invalid_hostnames(true) .danger_accept_invalid_hostnames(true)
.build() .build()
.unwrap() .unwrap()
.get("https://wrong.host.badssl.com/") .get("https://wrong.host.badssl.com/")
.send() .send()
.await
.unwrap() .unwrap()
.text() .text()
.await
.unwrap(); .unwrap();
assert!(text.contains("<title>wrong.host.badssl.com</title>")); assert!(text.contains("<title>wrong.host.badssl.com</title>"));
@@ -61,7 +69,8 @@ fn test_badssl_wrong_host() {
.build() .build()
.unwrap() .unwrap()
.get("https://self-signed.badssl.com/") .get("https://self-signed.badssl.com/")
.send(); .send()
.await;
assert!(result.is_err()); 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] #[macro_use]
mod support; mod support;
#[test] use std::io::Write;
fn test_response_text() { 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! { let server = server! {
request: b"\ request: b"\
GET /text HTTP/1.1\r\n\ GET /text HTTP/1.1\r\n\
@@ -14,65 +36,27 @@ fn test_response_text() {
", ",
response: b"\ response: b"\
HTTP/1.1 200 OK\r\n\ HTTP/1.1 200 OK\r\n\
Server: test\r\n\
Content-Length: 5\r\n\ Content-Length: 5\r\n\
\r\n\ \r\n\
Hello\ Hello\
" "
}; };
let url = format!("http://{}/text", server.addr()); let client = Client::new();
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 body = res.text().unwrap(); let mut res = client
assert_eq!(b"Hello", body.as_bytes()); .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] #[tokio::test]
fn test_response_non_utf_8_text() { async fn response_json() {
let server = server! { let _ = env_logger::try_init();
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::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! { let server = server! {
request: b"\ request: b"\
GET /json HTTP/1.1\r\n\ GET /json HTTP/1.1\r\n\
@@ -84,32 +68,100 @@ fn test_response_json() {
", ",
response: b"\ response: b"\
HTTP/1.1 200 OK\r\n\ HTTP/1.1 200 OK\r\n\
Server: test\r\n\
Content-Length: 7\r\n\ Content-Length: 7\r\n\
\r\n\ \r\n\
\"Hello\"\ \"Hello\"\
" "
}; };
let url = format!("http://{}/json", server.addr()); let client = Client::new();
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 body = res.json::<String>().unwrap(); let mut res = client
assert_eq!("Hello", body); .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] #[tokio::test]
fn test_response_copy_to() { 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! { let server = server! {
request: b"\ request: b"\
GET /1 HTTP/1.1\r\n\ GET /slow HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\ user-agent: $USERAGENT\r\n\
accept: */*\r\n\ accept: */*\r\n\
accept-encoding: gzip\r\n\ accept-encoding: gzip\r\n\
@@ -118,31 +170,35 @@ fn test_response_copy_to() {
", ",
response: b"\ response: b"\
HTTP/1.1 200 OK\r\n\ HTTP/1.1 200 OK\r\n\
Server: test\r\n\
Content-Length: 5\r\n\ Content-Length: 5\r\n\
\r\n\ \r\n\
Hello\ Hello\
" ",
read_timeout: Duration::from_secs(2)
}; };
let url = format!("http://{}/1", server.addr()); let client = Client::builder()
let mut res = reqwest::get(&url).unwrap(); .timeout(Duration::from_millis(500))
assert_eq!(res.url().as_str(), &url); .build()
assert_eq!(res.status(), reqwest::StatusCode::OK); .unwrap();
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 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] #[tokio::test]
fn test_get() { async fn response_timeout() {
let _ = env_logger::try_init();
let server = server! { let server = server! {
request: b"\ request: b"\
GET /1 HTTP/1.1\r\n\ GET /slow HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\ user-agent: $USERAGENT\r\n\
accept: */*\r\n\ accept: */*\r\n\
accept-encoding: gzip\r\n\ accept-encoding: gzip\r\n\
@@ -151,362 +207,119 @@ fn test_get() {
", ",
response: b"\ response: b"\
HTTP/1.1 200 OK\r\n\ HTTP/1.1 200 OK\r\n\
Server: test\r\n\ Content-Length: 5\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\
\r\n\ \r\n\
Hello\ 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"\ response: b"\
HTTP/1.1 200 OK\r\n\ HTTP/1.1 200 OK\r\n\
Server: post\r\n\ Server: post\r\n\
Content-Length: 0\r\n\ Content-Length: 7\r\n\
\r\n\ \r\n\
" "
}; };
let url = format!("http://{}/2", server.addr()); let url = format!("http://{}/post", server.addr());
let mut res = reqwest::Client::new()
let client = Client::new();
let res = client
.post(&url) .post(&url)
.body("Hello") .body(Body::wrap_stream(source))
.send() .send()
.unwrap(); .await
.expect("Failed to post");
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");
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);
} }
/// 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), write_timeout: Duration::from_millis(10),
response: response 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(); let mut body = String::new();
res.read_to_string(&mut body).unwrap(); res.read_to_string(&mut body).unwrap();
@@ -68,7 +68,7 @@ fn test_gzip_empty_body() {
\r\n" \r\n"
}; };
let client = reqwest::Client::new(); let client = reqwest::blocking::Client::new();
let mut res = client let mut res = client
.head(&format!("http://{}/gzip", server.addr())) .head(&format!("http://{}/gzip", server.addr()))
.send() .send()
@@ -100,7 +100,7 @@ fn test_gzip_invalid_body() {
0" 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 // this tests that the request.send() didn't error, but that the error
// is in reading the body // is in reading the body
@@ -126,7 +126,7 @@ fn test_accept_header_is_not_changed_if_set() {
\r\n\ \r\n\
" "
}; };
let client = reqwest::Client::new(); let client = reqwest::blocking::Client::new();
let res = client let res = client
.get(&format!("http://{}/accept", server.addr())) .get(&format!("http://{}/accept", server.addr()))
@@ -158,7 +158,7 @@ fn test_accept_encoding_header_is_not_changed_if_set() {
\r\n\ \r\n\
" "
}; };
let client = reqwest::Client::new(); let client = reqwest::blocking::Client::new();
let res = client let res = client
.get(&format!("http://{}/accept-encoding", server.addr())) .get(&format!("http://{}/accept-encoding", server.addr()))

View File

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

View File

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

View File

@@ -1,8 +1,8 @@
#[macro_use] #[macro_use]
mod support; mod support;
#[test] #[tokio::test]
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 = [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 url = format!("http://{}/{}", redirect.addr(), code);
let dst = format!("http://{}/{}", redirect.addr(), "dst"); 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.url().as_str(), dst);
assert_eq!(res.status(), reqwest::StatusCode::OK); assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!( assert_eq!(
@@ -55,8 +55,8 @@ fn test_redirect_301_and_302_and_303_changes_post_to_get() {
} }
} }
#[test] #[tokio::test]
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 = [307, 308];
for code in codes.iter() { 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 url = format!("http://{}/{}", redirect.addr(), code);
let dst = format!("http://{}/{}", redirect.addr(), "dst"); 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.url().as_str(), dst);
assert_eq!(res.status(), reqwest::StatusCode::OK); assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!( assert_eq!(
@@ -108,19 +108,19 @@ fn test_redirect_307_and_308_tries_to_get_again() {
} }
} }
#[test] #[tokio::test]
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 = [307, 308];
for code in codes.iter() { for code in codes.iter() {
let redirect = server! { let redirect = server! {
request: format!("\ request: format!("\
POST /{} HTTP/1.1\r\n\ POST /{} HTTP/1.1\r\n\
content-length: 5\r\n\
user-agent: $USERAGENT\r\n\ user-agent: $USERAGENT\r\n\
accept: */*\r\n\ accept: */*\r\n\
accept-encoding: gzip\r\n\ accept-encoding: gzip\r\n\
host: $HOST\r\n\ host: $HOST\r\n\
content-length: 5\r\n\
\r\n\ \r\n\
Hello\ Hello\
", code), ", code),
@@ -136,12 +136,12 @@ fn test_redirect_307_and_308_tries_to_post_again() {
request: format!("\ request: format!("\
POST /dst HTTP/1.1\r\n\ POST /dst HTTP/1.1\r\n\
content-length: 5\r\n\
user-agent: $USERAGENT\r\n\ user-agent: $USERAGENT\r\n\
accept: */*\r\n\ accept: */*\r\n\
accept-encoding: gzip\r\n\ accept-encoding: gzip\r\n\
referer: http://$HOST/{}\r\n\ referer: http://$HOST/{}\r\n\
host: $HOST\r\n\ host: $HOST\r\n\
content-length: 5\r\n\
\r\n\ \r\n\
Hello\ Hello\
", code), ", code),
@@ -155,7 +155,7 @@ fn test_redirect_307_and_308_tries_to_post_again() {
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");
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.url().as_str(), dst);
assert_eq!(res.status(), reqwest::StatusCode::OK); assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!( assert_eq!(
@@ -167,7 +167,7 @@ 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::Client::new(); let client = reqwest::blocking::Client::new();
let codes = [307, 308]; let codes = [307, 308];
for &code in codes.iter() { for &code in codes.iter() {
let redirect = server! { 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 url = format!("http://{}/{}", redirect.addr(), code);
let res = client let res = client
.post(&url) .post(&url)
.body(reqwest::Body::new(&b"Hello"[..])) .body(reqwest::blocking::Body::new(&b"Hello"[..]))
.send() .send()
.unwrap(); .unwrap();
assert_eq!(res.url().as_str(), url); assert_eq!(res.url().as_str(), url);
@@ -204,8 +204,8 @@ fn test_redirect_307_does_not_try_if_reader_cannot_reset() {
} }
} }
#[test] #[tokio::test]
fn test_redirect_removes_sensitive_headers() { async fn test_redirect_removes_sensitive_headers() {
let end_server = server! { let end_server = server! {
request: b"\ request: b"\
GET /otherhost HTTP/1.1\r\n\ GET /otherhost HTTP/1.1\r\n\
@@ -252,11 +252,12 @@ fn test_redirect_removes_sensitive_headers() {
reqwest::header::HeaderValue::from_static("foo=bar"), reqwest::header::HeaderValue::from_static("foo=bar"),
) )
.send() .send()
.await
.unwrap(); .unwrap();
} }
#[test] #[tokio::test]
fn test_redirect_policy_can_return_errors() { async fn test_redirect_policy_can_return_errors() {
let server = server! { let server = server! {
request: b"\ request: b"\
GET /loop HTTP/1.1\r\n\ GET /loop HTTP/1.1\r\n\
@@ -274,13 +275,13 @@ fn test_redirect_policy_can_return_errors() {
\r\n\ \r\n\
" "
}; };
let url = format!("http://{}/loop", server.addr());
let err = reqwest::get(&format!("http://{}/loop", server.addr())).unwrap_err(); let err = reqwest::get(&url).await.unwrap_err();
assert!(err.is_redirect()); assert!(err.is_redirect());
} }
#[test] #[tokio::test]
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! {
request: b"\ request: b"\
GET /no-redirect HTTP/1.1\r\n\ GET /no-redirect HTTP/1.1\r\n\
@@ -307,6 +308,7 @@ fn test_redirect_policy_can_stop_redirects_without_an_error() {
.unwrap() .unwrap()
.get(&url) .get(&url)
.send() .send()
.await
.unwrap(); .unwrap();
assert_eq!(res.url().as_str(), url); assert_eq!(res.url().as_str(), url);
@@ -317,8 +319,8 @@ fn test_redirect_policy_can_stop_redirects_without_an_error() {
); );
} }
#[test] #[tokio::test]
fn test_referer_is_not_set_if_disabled() { async fn test_referer_is_not_set_if_disabled() {
let server = server! { let server = server! {
request: b"\ request: b"\
GET /no-refer HTTP/1.1\r\n\ GET /no-refer HTTP/1.1\r\n\
@@ -357,14 +359,14 @@ fn test_referer_is_not_set_if_disabled() {
.referer(false) .referer(false)
.build() .build()
.unwrap() .unwrap()
//client
.get(&format!("http://{}/no-refer", server.addr())) .get(&format!("http://{}/no-refer", server.addr()))
.send() .send()
.await
.unwrap(); .unwrap();
} }
#[test] #[tokio::test]
fn test_invalid_location_stops_redirect_gh484() { async fn test_invalid_location_stops_redirect_gh484() {
let server = server! { let server = server! {
request: b"\ request: b"\
GET /yikes HTTP/1.1\r\n\ 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 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.url().as_str(), url);
assert_eq!(res.status(), reqwest::StatusCode::FOUND); assert_eq!(res.status(), reqwest::StatusCode::FOUND);
@@ -395,8 +397,8 @@ fn test_invalid_location_stops_redirect_gh484() {
); );
} }
#[test] #[tokio::test]
fn test_redirect_302_with_set_cookies() { async fn test_redirect_302_with_set_cookies() {
let code = 302; let code = 302;
let client = reqwest::ClientBuilder::new() let client = reqwest::ClientBuilder::new()
.cookie_store(true) .cookie_store(true)
@@ -442,7 +444,7 @@ fn test_redirect_302_with_set_cookies() {
let url = format!("http://{}/{}", server.addr(), code); let url = format!("http://{}/{}", server.addr(), code);
let dst = format!("http://{}/{}", server.addr(), "dst"); 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.url().as_str(), dst);
assert_eq!(res.status(), reqwest::StatusCode::OK); 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 // Make Client drop *after* the Server, so the background doesn't
// close too early. // close too early.
let client = reqwest::Client::builder() let client = reqwest::blocking::Client::builder()
.timeout(Duration::from_millis(500)) .timeout(Duration::from_millis(500))
.build() .build()
.unwrap(); .unwrap();
@@ -50,7 +50,7 @@ fn write_timeout_large_body() {
// Make Client drop *after* the Server, so the background doesn't // Make Client drop *after* the Server, so the background doesn't
// close too early. // close too early.
let client = reqwest::Client::builder() let client = reqwest::blocking::Client::builder()
.timeout(Duration::from_millis(500)) .timeout(Duration::from_millis(500))
.build() .build()
.unwrap(); .unwrap();
@@ -80,7 +80,7 @@ fn write_timeout_large_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)
.body(reqwest::Body::sized(cursor, len as u64)) .body(reqwest::blocking::Body::sized(cursor, len as u64))
.send() .send()
.unwrap_err(); .unwrap_err();
@@ -108,7 +108,7 @@ fn test_response_timeout() {
}; };
let url = format!("http://{}/response-timeout", server.addr()); let url = format!("http://{}/response-timeout", server.addr());
let err = reqwest::Client::builder() let err = reqwest::blocking::Client::builder()
.timeout(Duration::from_millis(500)) .timeout(Duration::from_millis(500))
.build() .build()
.unwrap() .unwrap()
@@ -142,7 +142,7 @@ fn test_read_timeout() {
}; };
let url = format!("http://{}/read-timeout", server.addr()); 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)) .timeout(Duration::from_millis(500))
.build() .build()
.unwrap() .unwrap()