UTF encoded form
This commit is contained in:
@@ -10,15 +10,15 @@ use serde::Serialize;
|
||||
use serde_json;
|
||||
|
||||
use super::body::Body;
|
||||
use super::client::{Client, Pending};
|
||||
use super::client::{ Client, Pending };
|
||||
#[cfg(feature = "multipart")]
|
||||
use super::multipart;
|
||||
use super::response::Response;
|
||||
#[cfg(feature = "multipart")]
|
||||
use crate::header::CONTENT_LENGTH;
|
||||
use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE};
|
||||
use crate::{Method, Url};
|
||||
use http::{request::Parts, Request as HttpRequest, Version};
|
||||
use crate::header::{ HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE };
|
||||
use crate::{ Method, Url };
|
||||
use http::{ request::Parts, Request as HttpRequest, Version };
|
||||
|
||||
/// A request which can be executed with `Client::execute()`.
|
||||
pub struct Request {
|
||||
@@ -142,23 +142,9 @@ impl Request {
|
||||
}
|
||||
|
||||
pub(super) fn pieces(
|
||||
self,
|
||||
) -> (
|
||||
Method,
|
||||
Url,
|
||||
HeaderMap,
|
||||
Option<Body>,
|
||||
Option<Duration>,
|
||||
Version,
|
||||
) {
|
||||
(
|
||||
self.method,
|
||||
self.url,
|
||||
self.headers,
|
||||
self.body,
|
||||
self.timeout,
|
||||
self.version,
|
||||
)
|
||||
self
|
||||
) -> (Method, Url, HeaderMap, Option<Body>, Option<Duration>, Version) {
|
||||
(self.method, self.url, self.headers, self.body, self.timeout, self.version)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,8 +152,7 @@ impl RequestBuilder {
|
||||
pub(super) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder {
|
||||
let mut builder = RequestBuilder { client, request };
|
||||
|
||||
let auth = builder
|
||||
.request
|
||||
let auth = builder.request
|
||||
.as_mut()
|
||||
.ok()
|
||||
.and_then(|req| extract_authority(&mut req.url));
|
||||
@@ -181,39 +166,44 @@ impl RequestBuilder {
|
||||
|
||||
/// Add a `Header` to this Request.
|
||||
pub fn header<K, V>(self, key: K, value: V) -> RequestBuilder
|
||||
where
|
||||
HeaderName: TryFrom<K>,
|
||||
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
||||
HeaderValue: TryFrom<V>,
|
||||
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
|
||||
where
|
||||
HeaderName: TryFrom<K>,
|
||||
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
||||
HeaderValue: TryFrom<V>,
|
||||
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>
|
||||
{
|
||||
self.header_sensitive(key, value, false)
|
||||
}
|
||||
|
||||
/// Add a `Header` to this Request with ability to define if header_value is sensitive.
|
||||
fn header_sensitive<K, V>(mut self, key: K, value: V, sensitive: bool) -> RequestBuilder
|
||||
where
|
||||
HeaderName: TryFrom<K>,
|
||||
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
||||
HeaderValue: TryFrom<V>,
|
||||
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
|
||||
where
|
||||
HeaderName: TryFrom<K>,
|
||||
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
||||
HeaderValue: TryFrom<V>,
|
||||
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>
|
||||
{
|
||||
let mut error = None;
|
||||
if let Ok(ref mut req) = self.request {
|
||||
match <HeaderName as TryFrom<K>>::try_from(key) {
|
||||
Ok(key) => match <HeaderValue as TryFrom<V>>::try_from(value) {
|
||||
Ok(mut value) => {
|
||||
// We want to potentially make an unsensitive header
|
||||
// to be sensitive, not the reverse. So, don't turn off
|
||||
// a previously sensitive header.
|
||||
if sensitive {
|
||||
value.set_sensitive(true);
|
||||
Ok(key) =>
|
||||
match <HeaderValue as TryFrom<V>>::try_from(value) {
|
||||
Ok(mut value) => {
|
||||
// We want to potentially make an unsensitive header
|
||||
// to be sensitive, not the reverse. So, don't turn off
|
||||
// a previously sensitive header.
|
||||
if sensitive {
|
||||
value.set_sensitive(true);
|
||||
}
|
||||
req.headers_mut().append(key, value);
|
||||
}
|
||||
Err(e) => {
|
||||
error = Some(crate::error::builder(e.into()));
|
||||
}
|
||||
req.headers_mut().append(key, value);
|
||||
}
|
||||
Err(e) => error = Some(crate::error::builder(e.into())),
|
||||
},
|
||||
Err(e) => error = Some(crate::error::builder(e.into())),
|
||||
Err(e) => {
|
||||
error = Some(crate::error::builder(e.into()));
|
||||
}
|
||||
};
|
||||
}
|
||||
if let Some(err) = error {
|
||||
@@ -247,14 +237,14 @@ impl RequestBuilder {
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
|
||||
where
|
||||
U: fmt::Display,
|
||||
P: fmt::Display,
|
||||
where U: fmt::Display, P: fmt::Display
|
||||
{
|
||||
let mut header_value = b"Basic ".to_vec();
|
||||
{
|
||||
let mut encoder =
|
||||
Base64Encoder::from(&mut header_value, &base64::engine::DEFAULT_ENGINE);
|
||||
let mut encoder = Base64Encoder::from(
|
||||
&mut header_value,
|
||||
&base64::engine::DEFAULT_ENGINE
|
||||
);
|
||||
// The unwraps here are fine because Vec::write* is infallible.
|
||||
write!(encoder, "{}:", username).unwrap();
|
||||
if let Some(password) = password {
|
||||
@@ -266,10 +256,7 @@ impl RequestBuilder {
|
||||
}
|
||||
|
||||
/// Enable HTTP bearer authentication.
|
||||
pub fn bearer_auth<T>(self, token: T) -> RequestBuilder
|
||||
where
|
||||
T: fmt::Display,
|
||||
{
|
||||
pub fn bearer_auth<T>(self, token: T) -> RequestBuilder where T: fmt::Display {
|
||||
let header_value = format!("Bearer {}", token);
|
||||
self.header_sensitive(crate::header::AUTHORIZATION, header_value, true)
|
||||
}
|
||||
@@ -318,7 +305,7 @@ impl RequestBuilder {
|
||||
pub fn multipart(self, mut multipart: multipart::Form) -> RequestBuilder {
|
||||
let mut builder = self.header(
|
||||
CONTENT_TYPE,
|
||||
format!("multipart/form-data; boundary={}", multipart.boundary()).as_str(),
|
||||
format!("multipart/form-data; boundary={}", multipart.boundary()).as_str()
|
||||
);
|
||||
|
||||
builder = match multipart.compute_length() {
|
||||
@@ -327,7 +314,7 @@ impl RequestBuilder {
|
||||
};
|
||||
|
||||
if let Ok(ref mut req) = builder.request {
|
||||
*req.body_mut() = Some(multipart.stream())
|
||||
*req.body_mut() = Some(multipart.stream());
|
||||
}
|
||||
builder
|
||||
}
|
||||
@@ -414,11 +401,13 @@ impl RequestBuilder {
|
||||
Ok(body) => {
|
||||
req.headers_mut().insert(
|
||||
CONTENT_TYPE,
|
||||
HeaderValue::from_static("application/x-www-form-urlencoded"),
|
||||
HeaderValue::from_static("application/x-www-form-urlencoded; charset=UTF-8")
|
||||
);
|
||||
*req.body_mut() = Some(body.into());
|
||||
}
|
||||
Err(err) => error = Some(crate::error::builder(err)),
|
||||
Err(err) => {
|
||||
error = Some(crate::error::builder(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(err) = error {
|
||||
@@ -444,11 +433,15 @@ impl RequestBuilder {
|
||||
if let Ok(ref mut req) = self.request {
|
||||
match serde_json::to_vec(json) {
|
||||
Ok(body) => {
|
||||
req.headers_mut()
|
||||
.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
|
||||
req.headers_mut().insert(
|
||||
CONTENT_TYPE,
|
||||
HeaderValue::from_static("application/json")
|
||||
);
|
||||
*req.body_mut() = Some(body.into());
|
||||
}
|
||||
Err(err) => error = Some(crate::error::builder(err)),
|
||||
Err(err) => {
|
||||
error = Some(crate::error::builder(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(err) = error {
|
||||
@@ -553,11 +546,9 @@ impl fmt::Debug for RequestBuilder {
|
||||
|
||||
fn fmt_request_fields<'a, 'b>(
|
||||
f: &'a mut fmt::DebugStruct<'a, 'b>,
|
||||
req: &Request,
|
||||
req: &Request
|
||||
) -> &'a mut fmt::DebugStruct<'a, 'b> {
|
||||
f.field("method", &req.method)
|
||||
.field("url", &req.url)
|
||||
.field("headers", &req.headers)
|
||||
f.field("method", &req.method).field("url", &req.url).field("headers", &req.headers)
|
||||
}
|
||||
|
||||
/// Check the request URL for a "username:password" type authority, and if
|
||||
@@ -566,21 +557,15 @@ pub(crate) fn extract_authority(url: &mut Url) -> Option<(String, Option<String>
|
||||
use percent_encoding::percent_decode;
|
||||
|
||||
if url.has_authority() {
|
||||
let username: String = percent_decode(url.username().as_bytes())
|
||||
.decode_utf8()
|
||||
.ok()?
|
||||
.into();
|
||||
let password = url.password().and_then(|pass| {
|
||||
percent_decode(pass.as_bytes())
|
||||
.decode_utf8()
|
||||
.ok()
|
||||
.map(String::from)
|
||||
});
|
||||
let username: String = percent_decode(url.username().as_bytes()).decode_utf8().ok()?.into();
|
||||
let password = url
|
||||
.password()
|
||||
.and_then(|pass| {
|
||||
percent_decode(pass.as_bytes()).decode_utf8().ok().map(String::from)
|
||||
});
|
||||
if !username.is_empty() || password.is_some() {
|
||||
url.set_username("")
|
||||
.expect("has_authority means set_username shouldn't fail");
|
||||
url.set_password(None)
|
||||
.expect("has_authority means set_password shouldn't fail");
|
||||
url.set_username("").expect("has_authority means set_username shouldn't fail");
|
||||
url.set_password(None).expect("has_authority means set_password shouldn't fail");
|
||||
return Some((username, password));
|
||||
}
|
||||
}
|
||||
@@ -588,21 +573,12 @@ pub(crate) fn extract_authority(url: &mut Url) -> Option<(String, Option<String>
|
||||
None
|
||||
}
|
||||
|
||||
impl<T> TryFrom<HttpRequest<T>> for Request
|
||||
where
|
||||
T: Into<Body>,
|
||||
{
|
||||
impl<T> TryFrom<HttpRequest<T>> for Request where T: Into<Body> {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
|
||||
let (parts, body) = req.into_parts();
|
||||
let Parts {
|
||||
method,
|
||||
uri,
|
||||
headers,
|
||||
version,
|
||||
..
|
||||
} = parts;
|
||||
let Parts { method, uri, headers, version, .. } = parts;
|
||||
let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
|
||||
Ok(Request {
|
||||
method,
|
||||
@@ -619,14 +595,7 @@ impl TryFrom<Request> for HttpRequest<Body> {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(req: Request) -> crate::Result<Self> {
|
||||
let Request {
|
||||
method,
|
||||
url,
|
||||
headers,
|
||||
body,
|
||||
version,
|
||||
..
|
||||
} = req;
|
||||
let Request { method, url, headers, body, version, .. } = req;
|
||||
|
||||
let mut req = HttpRequest::builder()
|
||||
.version(version)
|
||||
@@ -642,7 +611,7 @@ impl TryFrom<Request> for HttpRequest<Body> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Client, HttpRequest, Request, Version};
|
||||
use super::{ Client, HttpRequest, Request, Version };
|
||||
use crate::Method;
|
||||
use serde::Serialize;
|
||||
use std::collections::BTreeMap;
|
||||
@@ -667,7 +636,12 @@ mod tests {
|
||||
let some_url = "https://google.com/";
|
||||
let r = client.get(some_url);
|
||||
|
||||
let r = r.query(&[("foo", "a"), ("foo", "b")]);
|
||||
let r = r.query(
|
||||
&[
|
||||
("foo", "a"),
|
||||
("foo", "b"),
|
||||
]
|
||||
);
|
||||
|
||||
let req = r.build().expect("request is valid");
|
||||
assert_eq!(req.url().query(), Some("foo=a&foo=b"));
|
||||
@@ -743,11 +717,7 @@ mod tests {
|
||||
let some_url = "https://google.com/";
|
||||
let empty_query: &[(&str, &str)] = &[];
|
||||
|
||||
let req = client
|
||||
.get(some_url)
|
||||
.query(empty_query)
|
||||
.build()
|
||||
.expect("request build");
|
||||
let req = client.get(some_url).query(empty_query).build().expect("request build");
|
||||
|
||||
assert_eq!(req.url().query(), None);
|
||||
assert_eq!(req.url().as_str(), "https://google.com/");
|
||||
@@ -760,11 +730,7 @@ mod tests {
|
||||
.post("http://httpbin.org/post")
|
||||
.header("foo", "bar")
|
||||
.body("from a &str!");
|
||||
let req = builder
|
||||
.try_clone()
|
||||
.expect("clone successful")
|
||||
.build()
|
||||
.expect("request is valid");
|
||||
let req = builder.try_clone().expect("clone successful").build().expect("request is valid");
|
||||
assert_eq!(req.url().as_str(), "http://httpbin.org/post");
|
||||
assert_eq!(req.method(), Method::POST);
|
||||
assert_eq!(req.headers()["foo"], "bar");
|
||||
@@ -774,11 +740,7 @@ mod tests {
|
||||
fn try_clone_no_body() {
|
||||
let client = Client::new();
|
||||
let builder = client.get("http://httpbin.org/get");
|
||||
let req = builder
|
||||
.try_clone()
|
||||
.expect("clone successful")
|
||||
.build()
|
||||
.expect("request is valid");
|
||||
let req = builder.try_clone().expect("clone successful").build().expect("request is valid");
|
||||
assert_eq!(req.url().as_str(), "http://httpbin.org/get");
|
||||
assert_eq!(req.method(), Method::GET);
|
||||
assert!(req.body().is_none());
|
||||
@@ -790,9 +752,7 @@ mod tests {
|
||||
let chunks: Vec<Result<_, ::std::io::Error>> = vec![Ok("hello"), Ok(" "), Ok("world")];
|
||||
let stream = futures_util::stream::iter(chunks);
|
||||
let client = Client::new();
|
||||
let builder = client
|
||||
.get("http://httpbin.org/get")
|
||||
.body(super::Body::wrap_stream(stream));
|
||||
let builder = client.get("http://httpbin.org/get").body(super::Body::wrap_stream(stream));
|
||||
let clone = builder.try_clone();
|
||||
assert!(clone.is_none());
|
||||
}
|
||||
@@ -805,10 +765,7 @@ mod tests {
|
||||
let req = client.get(some_url).build().expect("request build");
|
||||
|
||||
assert_eq!(req.url().as_str(), "https://localhost/");
|
||||
assert_eq!(
|
||||
req.headers()["authorization"],
|
||||
"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
|
||||
);
|
||||
assert_eq!(req.headers()["authorization"], "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -823,10 +780,7 @@ mod tests {
|
||||
.expect("request build");
|
||||
|
||||
assert_eq!(req.url().as_str(), "https://localhost/");
|
||||
assert_eq!(
|
||||
req.headers()["authorization"],
|
||||
"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
|
||||
);
|
||||
assert_eq!(req.headers()["authorization"], "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
|
||||
assert!(req.headers()["authorization"].is_sensitive());
|
||||
}
|
||||
|
||||
@@ -835,11 +789,7 @@ mod tests {
|
||||
let client = Client::new();
|
||||
let some_url = "https://localhost/";
|
||||
|
||||
let req = client
|
||||
.get(some_url)
|
||||
.bearer_auth("Hold my bear")
|
||||
.build()
|
||||
.expect("request build");
|
||||
let req = client.get(some_url).bearer_auth("Hold my bear").build().expect("request build");
|
||||
|
||||
assert_eq!(req.url().as_str(), "https://localhost/");
|
||||
assert_eq!(req.headers()["authorization"], "Bearer Hold my bear");
|
||||
@@ -854,11 +804,7 @@ mod tests {
|
||||
let mut header = http::HeaderValue::from_static("in plain sight");
|
||||
header.set_sensitive(true);
|
||||
|
||||
let req = client
|
||||
.get(some_url)
|
||||
.header("hiding", header)
|
||||
.build()
|
||||
.expect("request build");
|
||||
let req = client.get(some_url).header("hiding", header).build().expect("request build");
|
||||
|
||||
assert_eq!(req.url().as_str(), "https://localhost/");
|
||||
assert_eq!(req.headers()["hiding"], "in plain sight");
|
||||
|
||||
@@ -3,18 +3,18 @@ use std::fmt;
|
||||
use std::time::Duration;
|
||||
|
||||
use base64::encode;
|
||||
use http::{request::Parts, Request as HttpRequest, Version};
|
||||
use http::{ request::Parts, Request as HttpRequest, Version };
|
||||
use serde::Serialize;
|
||||
#[cfg(feature = "json")]
|
||||
use serde_json;
|
||||
use serde_urlencoded;
|
||||
|
||||
use super::body::{self, Body};
|
||||
use super::body::{ self, Body };
|
||||
#[cfg(feature = "multipart")]
|
||||
use super::multipart;
|
||||
use super::Client;
|
||||
use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE};
|
||||
use crate::{async_impl, Method, Url};
|
||||
use crate::header::{ HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE };
|
||||
use crate::{ async_impl, Method, Url };
|
||||
|
||||
/// A request which can be executed with `Client::execute()`.
|
||||
pub struct Request {
|
||||
@@ -155,8 +155,7 @@ impl RequestBuilder {
|
||||
pub(crate) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder {
|
||||
let mut builder = RequestBuilder { client, request };
|
||||
|
||||
let auth = builder
|
||||
.request
|
||||
let auth = builder.request
|
||||
.as_mut()
|
||||
.ok()
|
||||
.and_then(|req| async_impl::request::extract_authority(req.url_mut()));
|
||||
@@ -182,34 +181,39 @@ impl RequestBuilder {
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn header<K, V>(self, key: K, value: V) -> RequestBuilder
|
||||
where
|
||||
HeaderName: TryFrom<K>,
|
||||
HeaderValue: TryFrom<V>,
|
||||
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
||||
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
|
||||
where
|
||||
HeaderName: TryFrom<K>,
|
||||
HeaderValue: TryFrom<V>,
|
||||
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
||||
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>
|
||||
{
|
||||
self.header_sensitive(key, value, false)
|
||||
}
|
||||
|
||||
/// Add a `Header` to this Request with ability to define if header_value is sensitive.
|
||||
fn header_sensitive<K, V>(mut self, key: K, value: V, sensitive: bool) -> RequestBuilder
|
||||
where
|
||||
HeaderName: TryFrom<K>,
|
||||
HeaderValue: TryFrom<V>,
|
||||
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
||||
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
|
||||
where
|
||||
HeaderName: TryFrom<K>,
|
||||
HeaderValue: TryFrom<V>,
|
||||
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
||||
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>
|
||||
{
|
||||
let mut error = None;
|
||||
if let Ok(ref mut req) = self.request {
|
||||
match <HeaderName as TryFrom<K>>::try_from(key) {
|
||||
Ok(key) => match <HeaderValue as TryFrom<V>>::try_from(value) {
|
||||
Ok(mut value) => {
|
||||
value.set_sensitive(sensitive);
|
||||
req.headers_mut().append(key, value);
|
||||
Ok(key) =>
|
||||
match <HeaderValue as TryFrom<V>>::try_from(value) {
|
||||
Ok(mut value) => {
|
||||
value.set_sensitive(sensitive);
|
||||
req.headers_mut().append(key, value);
|
||||
}
|
||||
Err(e) => {
|
||||
error = Some(crate::error::builder(e.into()));
|
||||
}
|
||||
}
|
||||
Err(e) => error = Some(crate::error::builder(e.into())),
|
||||
},
|
||||
Err(e) => error = Some(crate::error::builder(e.into())),
|
||||
Err(e) => {
|
||||
error = Some(crate::error::builder(e.into()));
|
||||
}
|
||||
};
|
||||
}
|
||||
if let Some(err) = error {
|
||||
@@ -262,9 +266,7 @@ impl RequestBuilder {
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
|
||||
where
|
||||
U: fmt::Display,
|
||||
P: fmt::Display,
|
||||
where U: fmt::Display, P: fmt::Display
|
||||
{
|
||||
let auth = match password {
|
||||
Some(password) => format!("{}:{}", username, password),
|
||||
@@ -285,10 +287,7 @@ impl RequestBuilder {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn bearer_auth<T>(self, token: T) -> RequestBuilder
|
||||
where
|
||||
T: fmt::Display,
|
||||
{
|
||||
pub fn bearer_auth<T>(self, token: T) -> RequestBuilder where T: fmt::Display {
|
||||
let header_value = format!("Bearer {}", token);
|
||||
self.header_sensitive(crate::header::AUTHORIZATION, &*header_value, true)
|
||||
}
|
||||
@@ -448,11 +447,13 @@ impl RequestBuilder {
|
||||
Ok(body) => {
|
||||
req.headers_mut().insert(
|
||||
CONTENT_TYPE,
|
||||
HeaderValue::from_static("application/x-www-form-urlencoded"),
|
||||
HeaderValue::from_static("application/x-www-form-urlencoded; charset=UTF-8")
|
||||
);
|
||||
*req.body_mut() = Some(body.into());
|
||||
}
|
||||
Err(err) => error = Some(crate::error::builder(err)),
|
||||
Err(err) => {
|
||||
error = Some(crate::error::builder(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(err) = error {
|
||||
@@ -499,11 +500,15 @@ impl RequestBuilder {
|
||||
if let Ok(ref mut req) = self.request {
|
||||
match serde_json::to_vec(json) {
|
||||
Ok(body) => {
|
||||
req.headers_mut()
|
||||
.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
|
||||
req.headers_mut().insert(
|
||||
CONTENT_TYPE,
|
||||
HeaderValue::from_static("application/json")
|
||||
);
|
||||
*req.body_mut() = Some(body.into());
|
||||
}
|
||||
Err(err) => error = Some(crate::error::builder(err)),
|
||||
Err(err) => {
|
||||
error = Some(crate::error::builder(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(err) = error {
|
||||
@@ -536,13 +541,13 @@ impl RequestBuilder {
|
||||
pub fn multipart(self, mut multipart: multipart::Form) -> RequestBuilder {
|
||||
let mut builder = self.header(
|
||||
CONTENT_TYPE,
|
||||
format!("multipart/form-data; boundary={}", multipart.boundary()).as_str(),
|
||||
format!("multipart/form-data; boundary={}", multipart.boundary()).as_str()
|
||||
);
|
||||
if let Ok(ref mut req) = builder.request {
|
||||
*req.body_mut() = Some(match multipart.compute_length() {
|
||||
Some(length) => Body::sized(multipart.reader(), length),
|
||||
None => Body::new(multipart.reader()),
|
||||
})
|
||||
});
|
||||
}
|
||||
builder
|
||||
}
|
||||
@@ -619,20 +624,12 @@ impl RequestBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TryFrom<HttpRequest<T>> for Request
|
||||
where
|
||||
T: Into<Body>,
|
||||
{
|
||||
impl<T> TryFrom<HttpRequest<T>> for Request where T: Into<Body> {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
|
||||
let (parts, body) = req.into_parts();
|
||||
let Parts {
|
||||
method,
|
||||
uri,
|
||||
headers,
|
||||
..
|
||||
} = parts;
|
||||
let Parts { method, uri, headers, .. } = parts;
|
||||
let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
|
||||
let mut inner = async_impl::Request::new(method, url);
|
||||
crate::util::replace_headers(inner.headers_mut(), headers);
|
||||
@@ -651,24 +648,22 @@ impl fmt::Debug for Request {
|
||||
|
||||
fn fmt_request_fields<'a, 'b>(
|
||||
f: &'a mut fmt::DebugStruct<'a, 'b>,
|
||||
req: &Request,
|
||||
req: &Request
|
||||
) -> &'a mut fmt::DebugStruct<'a, 'b> {
|
||||
f.field("method", req.method())
|
||||
.field("url", req.url())
|
||||
.field("headers", req.headers())
|
||||
f.field("method", req.method()).field("url", req.url()).field("headers", req.headers())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::{body, Client};
|
||||
use super::{HttpRequest, Request, Version};
|
||||
use crate::header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE, HOST};
|
||||
use super::super::{ body, Client };
|
||||
use super::{ HttpRequest, Request, Version };
|
||||
use crate::header::{ HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE, HOST };
|
||||
use crate::Method;
|
||||
use serde::Serialize;
|
||||
#[cfg(feature = "json")]
|
||||
use serde_json;
|
||||
use serde_urlencoded;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::collections::{ BTreeMap, HashMap };
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[test]
|
||||
@@ -822,7 +817,12 @@ mod tests {
|
||||
let some_url = "https://google.com/";
|
||||
let mut r = client.get(some_url);
|
||||
|
||||
r = r.query(&[("foo", "a"), ("foo", "b")]);
|
||||
r = r.query(
|
||||
&[
|
||||
("foo", "a"),
|
||||
("foo", "b"),
|
||||
]
|
||||
);
|
||||
|
||||
let req = r.build().expect("request is valid");
|
||||
assert_eq!(req.url().query(), Some("foo=a&foo=b"));
|
||||
@@ -881,7 +881,7 @@ mod tests {
|
||||
// Make sure the content type was set
|
||||
assert_eq!(
|
||||
r.headers().get(CONTENT_TYPE).unwrap(),
|
||||
&"application/x-www-form-urlencoded"
|
||||
&"application/x-www-form-urlencoded; charset=UTF-8"
|
||||
);
|
||||
|
||||
let buf = body::read_to_string(r.body_mut().take().unwrap()).unwrap();
|
||||
@@ -915,14 +915,11 @@ mod tests {
|
||||
#[cfg(feature = "json")]
|
||||
fn add_json_fail() {
|
||||
use serde::ser::Error as _;
|
||||
use serde::{Serialize, Serializer};
|
||||
use serde::{ Serialize, Serializer };
|
||||
use std::error::Error as _;
|
||||
struct MyStruct;
|
||||
impl Serialize for MyStruct {
|
||||
fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
|
||||
Err(S::Error::custom("nope"))
|
||||
}
|
||||
}
|
||||
@@ -967,11 +964,7 @@ mod tests {
|
||||
let some_url = "https://google.com/";
|
||||
let empty_query: &[(&str, &str)] = &[];
|
||||
|
||||
let req = client
|
||||
.get(some_url)
|
||||
.query(empty_query)
|
||||
.build()
|
||||
.expect("request build");
|
||||
let req = client.get(some_url).query(empty_query).build().expect("request build");
|
||||
|
||||
assert_eq!(req.url().query(), None);
|
||||
assert_eq!(req.url().as_str(), "https://google.com/");
|
||||
@@ -985,10 +978,7 @@ mod tests {
|
||||
let req = client.get(some_url).build().expect("request build");
|
||||
|
||||
assert_eq!(req.url().as_str(), "https://localhost/");
|
||||
assert_eq!(
|
||||
req.headers()["authorization"],
|
||||
"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
|
||||
);
|
||||
assert_eq!(req.headers()["authorization"], "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1041,10 +1031,7 @@ mod tests {
|
||||
.expect("request build");
|
||||
|
||||
assert_eq!(req.url().as_str(), "https://localhost/");
|
||||
assert_eq!(
|
||||
req.headers()["authorization"],
|
||||
"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
|
||||
);
|
||||
assert_eq!(req.headers()["authorization"], "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
|
||||
assert_eq!(req.headers()["authorization"].is_sensitive(), true);
|
||||
}
|
||||
|
||||
@@ -1053,11 +1040,7 @@ mod tests {
|
||||
let client = Client::new();
|
||||
let some_url = "https://localhost/";
|
||||
|
||||
let req = client
|
||||
.get(some_url)
|
||||
.bearer_auth("Hold my bear")
|
||||
.build()
|
||||
.expect("request build");
|
||||
let req = client.get(some_url).bearer_auth("Hold my bear").build().expect("request build");
|
||||
|
||||
assert_eq!(req.url().as_str(), "https://localhost/");
|
||||
assert_eq!(req.headers()["authorization"], "Bearer Hold my bear");
|
||||
|
||||
@@ -4,15 +4,15 @@ use std::io::Write;
|
||||
|
||||
use base64::write::EncoderWriter as Base64Encoder;
|
||||
use bytes::Bytes;
|
||||
use http::{request::Parts, Method, Request as HttpRequest};
|
||||
use http::{ request::Parts, Method, Request as HttpRequest };
|
||||
use serde::Serialize;
|
||||
#[cfg(feature = "json")]
|
||||
use serde_json;
|
||||
use url::Url;
|
||||
use web_sys::RequestCredentials;
|
||||
|
||||
use super::{Body, Client, Response};
|
||||
use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE};
|
||||
use super::{ Body, Client, Response };
|
||||
use crate::header::{ HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE };
|
||||
|
||||
/// A request which can be executed with `Client::execute()`.
|
||||
pub struct Request {
|
||||
@@ -174,11 +174,13 @@ impl RequestBuilder {
|
||||
Ok(body) => {
|
||||
req.headers_mut().insert(
|
||||
CONTENT_TYPE,
|
||||
HeaderValue::from_static("application/x-www-form-urlencoded"),
|
||||
HeaderValue::from_static("application/x-www-form-urlencoded; charset=UTF-8")
|
||||
);
|
||||
*req.body_mut() = Some(body.into());
|
||||
}
|
||||
Err(err) => error = Some(crate::error::builder(err)),
|
||||
Err(err) => {
|
||||
error = Some(crate::error::builder(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(err) = error {
|
||||
@@ -195,11 +197,15 @@ impl RequestBuilder {
|
||||
if let Ok(ref mut req) = self.request {
|
||||
match serde_json::to_vec(json) {
|
||||
Ok(body) => {
|
||||
req.headers_mut()
|
||||
.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
|
||||
req.headers_mut().insert(
|
||||
CONTENT_TYPE,
|
||||
HeaderValue::from_static("application/json")
|
||||
);
|
||||
*req.body_mut() = Some(body.into());
|
||||
}
|
||||
Err(err) => error = Some(crate::error::builder(err)),
|
||||
Err(err) => {
|
||||
error = Some(crate::error::builder(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(err) = error {
|
||||
@@ -210,14 +216,14 @@ impl RequestBuilder {
|
||||
|
||||
/// Enable HTTP basic authentication.
|
||||
pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
|
||||
where
|
||||
U: fmt::Display,
|
||||
P: fmt::Display,
|
||||
where U: fmt::Display, P: fmt::Display
|
||||
{
|
||||
let mut header_value = b"Basic ".to_vec();
|
||||
{
|
||||
let mut encoder =
|
||||
Base64Encoder::from(&mut header_value, &base64::engine::DEFAULT_ENGINE);
|
||||
let mut encoder = Base64Encoder::from(
|
||||
&mut header_value,
|
||||
&base64::engine::DEFAULT_ENGINE
|
||||
);
|
||||
// The unwraps here are fine because Vec::write* is infallible.
|
||||
write!(encoder, "{}:", username).unwrap();
|
||||
if let Some(password) = password {
|
||||
@@ -229,10 +235,7 @@ impl RequestBuilder {
|
||||
}
|
||||
|
||||
/// Enable HTTP bearer authentication.
|
||||
pub fn bearer_auth<T>(self, token: T) -> RequestBuilder
|
||||
where
|
||||
T: fmt::Display,
|
||||
{
|
||||
pub fn bearer_auth<T>(self, token: T) -> RequestBuilder where T: fmt::Display {
|
||||
let header_value = format!("Bearer {}", token);
|
||||
self.header(crate::header::AUTHORIZATION, header_value)
|
||||
}
|
||||
@@ -250,29 +253,34 @@ impl RequestBuilder {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
|
||||
pub fn multipart(mut self, multipart: super::multipart::Form) -> RequestBuilder {
|
||||
if let Ok(ref mut req) = self.request {
|
||||
*req.body_mut() = Some(Body::from_form(multipart))
|
||||
*req.body_mut() = Some(Body::from_form(multipart));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a `Header` to this Request.
|
||||
pub fn header<K, V>(mut self, key: K, value: V) -> RequestBuilder
|
||||
where
|
||||
HeaderName: TryFrom<K>,
|
||||
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
||||
HeaderValue: TryFrom<V>,
|
||||
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
|
||||
where
|
||||
HeaderName: TryFrom<K>,
|
||||
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
||||
HeaderValue: TryFrom<V>,
|
||||
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>
|
||||
{
|
||||
let mut error = None;
|
||||
if let Ok(ref mut req) = self.request {
|
||||
match <HeaderName as TryFrom<K>>::try_from(key) {
|
||||
Ok(key) => match <HeaderValue as TryFrom<V>>::try_from(value) {
|
||||
Ok(value) => {
|
||||
req.headers_mut().append(key, value);
|
||||
Ok(key) =>
|
||||
match <HeaderValue as TryFrom<V>>::try_from(value) {
|
||||
Ok(value) => {
|
||||
req.headers_mut().append(key, value);
|
||||
}
|
||||
Err(e) => {
|
||||
error = Some(crate::error::builder(e.into()));
|
||||
}
|
||||
}
|
||||
Err(e) => error = Some(crate::error::builder(e.into())),
|
||||
},
|
||||
Err(e) => error = Some(crate::error::builder(e.into())),
|
||||
Err(e) => {
|
||||
error = Some(crate::error::builder(e.into()));
|
||||
}
|
||||
};
|
||||
}
|
||||
if let Some(err) = error {
|
||||
@@ -434,27 +442,17 @@ impl fmt::Debug for RequestBuilder {
|
||||
|
||||
fn fmt_request_fields<'a, 'b>(
|
||||
f: &'a mut fmt::DebugStruct<'a, 'b>,
|
||||
req: &Request,
|
||||
req: &Request
|
||||
) -> &'a mut fmt::DebugStruct<'a, 'b> {
|
||||
f.field("method", &req.method)
|
||||
.field("url", &req.url)
|
||||
.field("headers", &req.headers)
|
||||
f.field("method", &req.method).field("url", &req.url).field("headers", &req.headers)
|
||||
}
|
||||
|
||||
impl<T> TryFrom<HttpRequest<T>> for Request
|
||||
where
|
||||
T: Into<Body>,
|
||||
{
|
||||
impl<T> TryFrom<HttpRequest<T>> for Request where T: Into<Body> {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
|
||||
let (parts, body) = req.into_parts();
|
||||
let Parts {
|
||||
method,
|
||||
uri,
|
||||
headers,
|
||||
..
|
||||
} = parts;
|
||||
let Parts { method, uri, headers, .. } = parts;
|
||||
let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
|
||||
Ok(Request {
|
||||
method,
|
||||
@@ -471,13 +469,7 @@ impl TryFrom<Request> for HttpRequest<Body> {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(req: Request) -> crate::Result<Self> {
|
||||
let Request {
|
||||
method,
|
||||
url,
|
||||
headers,
|
||||
body,
|
||||
..
|
||||
} = req;
|
||||
let Request { method, url, headers, body, .. } = req;
|
||||
|
||||
let mut req = HttpRequest::builder()
|
||||
.method(method)
|
||||
|
||||
@@ -18,7 +18,8 @@ fn test_response_text() {
|
||||
#[test]
|
||||
fn test_response_non_utf_8_text() {
|
||||
let server = server::http(move |_req| async {
|
||||
http::Response::builder()
|
||||
http::Response
|
||||
::builder()
|
||||
.header("content-type", "text/plain; charset=gbk")
|
||||
.body(b"\xc4\xe3\xba\xc3"[..].into())
|
||||
.unwrap()
|
||||
@@ -91,11 +92,7 @@ fn test_post() {
|
||||
});
|
||||
|
||||
let url = format!("http://{}/2", server.addr());
|
||||
let res = reqwest::blocking::Client::new()
|
||||
.post(&url)
|
||||
.body("Hello")
|
||||
.send()
|
||||
.unwrap();
|
||||
let 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);
|
||||
@@ -108,7 +105,7 @@ fn test_post_form() {
|
||||
assert_eq!(req.headers()["content-length"], "24");
|
||||
assert_eq!(
|
||||
req.headers()["content-type"],
|
||||
"application/x-www-form-urlencoded"
|
||||
"application/x-www-form-urlencoded; charset=UTF-8"
|
||||
);
|
||||
|
||||
let data = hyper::body::to_bytes(req.into_body()).await.unwrap();
|
||||
@@ -117,14 +114,13 @@ fn test_post_form() {
|
||||
http::Response::default()
|
||||
});
|
||||
|
||||
let form = &[("hello", "world"), ("sean", "monstar")];
|
||||
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");
|
||||
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);
|
||||
@@ -135,10 +131,7 @@ fn test_post_form() {
|
||||
#[test]
|
||||
fn test_error_for_status_4xx() {
|
||||
let server = server::http(move |_req| async {
|
||||
http::Response::builder()
|
||||
.status(400)
|
||||
.body(Default::default())
|
||||
.unwrap()
|
||||
http::Response::builder().status(400).body(Default::default()).unwrap()
|
||||
});
|
||||
|
||||
let url = format!("http://{}/1", server.addr());
|
||||
@@ -154,10 +147,7 @@ fn test_error_for_status_4xx() {
|
||||
#[test]
|
||||
fn test_error_for_status_5xx() {
|
||||
let server = server::http(move |_req| async {
|
||||
http::Response::builder()
|
||||
.status(500)
|
||||
.body(Default::default())
|
||||
.unwrap()
|
||||
http::Response::builder().status(500).body(Default::default()).unwrap()
|
||||
});
|
||||
|
||||
let url = format!("http://{}/1", server.addr());
|
||||
@@ -165,10 +155,7 @@ fn test_error_for_status_5xx() {
|
||||
|
||||
let err = res.error_for_status().unwrap_err();
|
||||
assert!(err.is_status());
|
||||
assert_eq!(
|
||||
err.status(),
|
||||
Some(reqwest::StatusCode::INTERNAL_SERVER_ERROR)
|
||||
);
|
||||
assert_eq!(err.status(), Some(reqwest::StatusCode::INTERNAL_SERVER_ERROR));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -180,10 +167,7 @@ fn test_default_headers() {
|
||||
|
||||
let mut headers = http::HeaderMap::with_capacity(1);
|
||||
headers.insert("reqwest-test", "orly".parse().unwrap());
|
||||
let client = reqwest::blocking::Client::builder()
|
||||
.default_headers(headers)
|
||||
.build()
|
||||
.unwrap();
|
||||
let client = reqwest::blocking::Client::builder().default_headers(headers).build().unwrap();
|
||||
|
||||
let url = format!("http://{}/1", server.addr());
|
||||
let res = client.get(&url).send().unwrap();
|
||||
@@ -205,20 +189,14 @@ fn test_override_default_headers() {
|
||||
let mut headers = http::HeaderMap::with_capacity(1);
|
||||
headers.insert(
|
||||
http::header::AUTHORIZATION,
|
||||
http::header::HeaderValue::from_static("iamatoken"),
|
||||
http::header::HeaderValue::from_static("iamatoken")
|
||||
);
|
||||
let client = reqwest::blocking::Client::builder()
|
||||
.default_headers(headers)
|
||||
.build()
|
||||
.unwrap();
|
||||
let client = reqwest::blocking::Client::builder().default_headers(headers).build().unwrap();
|
||||
|
||||
let url = format!("http://{}/3", server.addr());
|
||||
let res = client
|
||||
.get(&url)
|
||||
.header(
|
||||
http::header::AUTHORIZATION,
|
||||
http::header::HeaderValue::from_static("secret"),
|
||||
)
|
||||
.header(http::header::AUTHORIZATION, http::header::HeaderValue::from_static("secret"))
|
||||
.send()
|
||||
.unwrap();
|
||||
|
||||
@@ -253,14 +231,8 @@ fn test_appended_headers_not_overwritten() {
|
||||
// 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();
|
||||
headers.insert(header::ACCEPT, header::HeaderValue::from_static("text/html"));
|
||||
let client = reqwest::blocking::Client::builder().default_headers(headers).build().unwrap();
|
||||
|
||||
let url = format!("http://{}/4", server.addr());
|
||||
let res = client
|
||||
@@ -282,9 +254,7 @@ fn test_blocking_inside_a_runtime() {
|
||||
|
||||
let url = format!("http://{}/text", server.addr());
|
||||
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.expect("new rt");
|
||||
let rt = tokio::runtime::Builder::new_current_thread().build().expect("new rt");
|
||||
|
||||
rt.block_on(async move {
|
||||
let _should_panic = reqwest::blocking::get(&url);
|
||||
@@ -294,7 +264,8 @@ fn test_blocking_inside_a_runtime() {
|
||||
#[cfg(feature = "default-tls")]
|
||||
#[test]
|
||||
fn test_allowed_methods_blocking() {
|
||||
let resp = reqwest::blocking::Client::builder()
|
||||
let resp = reqwest::blocking::Client
|
||||
::builder()
|
||||
.https_only(true)
|
||||
.build()
|
||||
.expect("client builder")
|
||||
@@ -303,7 +274,8 @@ fn test_allowed_methods_blocking() {
|
||||
|
||||
assert_eq!(resp.is_err(), false);
|
||||
|
||||
let resp = reqwest::blocking::Client::builder()
|
||||
let resp = reqwest::blocking::Client
|
||||
::builder()
|
||||
.https_only(true)
|
||||
.build()
|
||||
.expect("client builder")
|
||||
@@ -318,7 +290,8 @@ fn test_allowed_methods_blocking() {
|
||||
fn test_body_from_bytes() {
|
||||
let body = "abc";
|
||||
// No external calls are needed. Only the request building is tested.
|
||||
let request = reqwest::blocking::Client::builder()
|
||||
let request = reqwest::blocking::Client
|
||||
::builder()
|
||||
.build()
|
||||
.expect("Could not build the client")
|
||||
.put("https://google.com")
|
||||
|
||||
Reference in New Issue
Block a user