UTF encoded form

This commit is contained in:
Matthew Ransley
2023-07-05 14:17:53 +01:00
parent d51035a0f2
commit 4ee6bdec9a
4 changed files with 211 additions and 317 deletions

View File

@@ -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");

View File

@@ -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");

View File

@@ -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)

View File

@@ -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")