Compare commits
10 Commits
f522527aec
...
bb34f72c2f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb34f72c2f | ||
|
|
08d5048e5b | ||
|
|
4ee6bdec9a | ||
|
|
3bd1d5d1b3 | ||
|
|
d3f3a5fde3 | ||
|
|
0437b3fccf | ||
|
|
d51035a0f2 | ||
|
|
97d5b067c8 | ||
|
|
815e8695ad | ||
|
|
fa96a507f4 |
105
README.md
105
README.md
@@ -1,10 +1,6 @@
|
|||||||
# reqwest-impersonate
|
# reqwest-impersonate
|
||||||
|
|
||||||
A fork of reqwest used to impersonate the Chrome browser. Inspired by [curl-impersonate](https://github.com/lwthiker/curl-impersonate).
|
A fork of [reqwest-impersonate](https://github.com/4JX/reqwest-impersonate), designed to provide more functionality and stability
|
||||||
|
|
||||||
This crate was intended to be an experiment to learn more about TLS and HTTP2 fingerprinting. Some parts of reqwest may not have the code needed to work when used to copy Chrome.
|
|
||||||
|
|
||||||
It is currently missing HTTP/2 `PRIORITY` support. (PRs to [h2](https://github.com/hyperium/h2) are welcome)
|
|
||||||
|
|
||||||
**Notice:** This crate depends on patched dependencies. To use it, please add the following to your `Cargo.toml`.
|
**Notice:** This crate depends on patched dependencies. To use it, please add the following to your `Cargo.toml`.
|
||||||
|
|
||||||
@@ -14,14 +10,12 @@ hyper = { git = "https://github.com/4JX/hyper.git", branch = "v0.14.18-patched"
|
|||||||
h2 = { git = "https://github.com/4JX/h2.git", branch = "imp" }
|
h2 = { git = "https://github.com/4JX/h2.git", branch = "imp" }
|
||||||
```
|
```
|
||||||
|
|
||||||
These patches were made specifically for `reqwest-impersonate` to work, but I would appreciate if someone took the time to PR more "proper" versions to the parent projects.
|
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
`Cargo.toml`
|
`Cargo.toml`
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
reqwest-impersonate = { git = "https://github.com/4JX/reqwest-impersonate.git", default-features = false, features = [
|
reqwest-impersonate = { git = "https://github.com/epicmatthew23/reqwest-impersonate.git", default-features = false, features = [
|
||||||
"chrome",
|
"chrome",
|
||||||
"blocking",
|
"blocking",
|
||||||
] }
|
] }
|
||||||
@@ -33,9 +27,9 @@ reqwest-impersonate = { git = "https://github.com/4JX/reqwest-impersonate.git",
|
|||||||
use reqwest_impersonate::browser::ChromeVersion;
|
use reqwest_impersonate::browser::ChromeVersion;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Build a client to mimic Chrome 104
|
// Build a client to mimic Chrome 114
|
||||||
let client = reqwest_impersonate::blocking::Client::builder()
|
let client = reqwest_impersonate::blocking::Client::builder()
|
||||||
.chrome_builder(ChromeVersion::V104)
|
.chrome_builder(ChromeVersion::V114)
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -50,94 +44,3 @@ fn main() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Original readme
|
|
||||||
|
|
||||||
[](https://crates.io/crates/reqwest)
|
|
||||||
[](https://docs.rs/reqwest)
|
|
||||||
[](./LICENSE-APACHE)
|
|
||||||
[](https://github.com/seanmonstar/reqwest/actions?query=workflow%3ACI)
|
|
||||||
|
|
||||||
An ergonomic, batteries-included HTTP Client for Rust.
|
|
||||||
|
|
||||||
- Plain bodies, JSON, urlencoded, multipart
|
|
||||||
- Customizable redirect policy
|
|
||||||
- HTTP Proxies
|
|
||||||
- HTTPS via system-native TLS (or optionally, rustls)
|
|
||||||
- Cookie Store
|
|
||||||
- WASM
|
|
||||||
- [Changelog](CHANGELOG.md)
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
This asynchronous example uses [Tokio](https://tokio.rs) and enables some
|
|
||||||
optional features, so your `Cargo.toml` could look like this:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[dependencies]
|
|
||||||
reqwest = { version = "0.11", features = ["json"] }
|
|
||||||
tokio = { version = "1", features = ["full"] }
|
|
||||||
```
|
|
||||||
|
|
||||||
And then the code:
|
|
||||||
|
|
||||||
```rust,no_run
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let resp = reqwest::get("https://httpbin.org/ip")
|
|
||||||
.await?
|
|
||||||
.json::<HashMap<String, String>>()
|
|
||||||
.await?;
|
|
||||||
println!("{:#?}", resp);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Blocking Client
|
|
||||||
|
|
||||||
There is an optional "blocking" client API that can be enabled:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[dependencies]
|
|
||||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
|
||||||
```
|
|
||||||
|
|
||||||
```rust,no_run
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let resp = reqwest::blocking::get("https://httpbin.org/ip")?
|
|
||||||
.json::<HashMap<String, String>>()?;
|
|
||||||
println!("{:#?}", resp);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
On Linux:
|
|
||||||
|
|
||||||
- OpenSSL 1.0.1, 1.0.2, 1.1.0, or 1.1.1 with headers (see <https://github.com/sfackler/rust-openssl>)
|
|
||||||
|
|
||||||
On Windows and macOS:
|
|
||||||
|
|
||||||
- Nothing.
|
|
||||||
|
|
||||||
Reqwest uses [rust-native-tls](https://github.com/sfackler/rust-native-tls),
|
|
||||||
which will use the operating system TLS framework if available, meaning Windows
|
|
||||||
and macOS. On Linux, it will use OpenSSL 1.1.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Licensed under either of
|
|
||||||
|
|
||||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <http://apache.org/licenses/LICENSE-2.0>)
|
|
||||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
|
||||||
|
|
||||||
### Contribution
|
|
||||||
|
|
||||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
|
||||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall
|
|
||||||
be dual licensed as above, without any additional terms or conditions.
|
|
||||||
|
|||||||
37
examples/tls.rs
Normal file
37
examples/tls.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
//! `cargo run --example tls --features=blocking,chrome`
|
||||||
|
|
||||||
|
#![deny(warnings)]
|
||||||
|
|
||||||
|
use reqwest_impersonate::browser::ChromeVersion;
|
||||||
|
|
||||||
|
// This is using the `tokio` runtime. You'll need the following dependency:
|
||||||
|
//
|
||||||
|
// `tokio = { version = "1", features = ["full"] }`
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
|
||||||
|
fn main() -> Result<(), reqwest_impersonate::Error> {
|
||||||
|
// Build a client to mimic Chrome 104
|
||||||
|
let client = reqwest_impersonate::blocking::Client::builder()
|
||||||
|
.chrome_builder(ChromeVersion::V108)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Use the API you're already familiar with
|
||||||
|
match client.get("https://tls.peet.ws/api/all").send() {
|
||||||
|
Ok(res) => {
|
||||||
|
println!("{}", res.text().unwrap());
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
dbg!(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// The [cfg(not(target_arch = "wasm32"))] above prevent building the tokio::main function
|
||||||
|
// for wasm32 target, because tokio isn't compatible with wasm32.
|
||||||
|
// If you aren't building for wasm32, you don't need that line.
|
||||||
|
// The two lines below avoid the "'main' function not found" error when building for wasm32 target.
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
fn main() {}
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
|
|
||||||
# The main application derivation
|
# The main application derivation
|
||||||
reqwest-impersonate = craneLib.buildPackage
|
reqwest-impersonate = craneLib.buildPackage
|
||||||
({
|
(rec {
|
||||||
src = nixLib.cleanSourceWith
|
src = nixLib.cleanSourceWith
|
||||||
{
|
{
|
||||||
src = workspaceSrc;
|
src = workspaceSrc;
|
||||||
@@ -68,6 +68,7 @@
|
|||||||
buildInputs = with pkgs;
|
buildInputs = with pkgs;
|
||||||
[
|
[
|
||||||
openssl
|
openssl
|
||||||
|
boringssl
|
||||||
]
|
]
|
||||||
++ pkgs.lib.optionals pkgs.stdenv.isDarwin [ ];
|
++ pkgs.lib.optionals pkgs.stdenv.isDarwin [ ];
|
||||||
|
|
||||||
@@ -76,6 +77,8 @@
|
|||||||
clang
|
clang
|
||||||
pkg-config
|
pkg-config
|
||||||
] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [ ];
|
] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [ ];
|
||||||
|
|
||||||
|
LD_LIBRARY_PATH = nixLib.makeLibraryPath buildInputs;
|
||||||
} // envVars);
|
} // envVars);
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ use super::response::Response;
|
|||||||
use super::Body;
|
use super::Body;
|
||||||
#[cfg(feature = "__chrome")]
|
#[cfg(feature = "__chrome")]
|
||||||
use crate::browser::{configure_chrome, ChromeVersion};
|
use crate::browser::{configure_chrome, ChromeVersion};
|
||||||
use crate::connect::{Connector};
|
use crate::connect::Connector;
|
||||||
#[cfg(feature = "cookies")]
|
#[cfg(feature = "cookies")]
|
||||||
use crate::cookie;
|
use crate::cookie;
|
||||||
#[cfg(feature = "trust-dns")]
|
#[cfg(feature = "trust-dns")]
|
||||||
|
|||||||
@@ -10,15 +10,15 @@ use serde::Serialize;
|
|||||||
use serde_json;
|
use serde_json;
|
||||||
|
|
||||||
use super::body::Body;
|
use super::body::Body;
|
||||||
use super::client::{Client, Pending};
|
use super::client::{ Client, Pending };
|
||||||
#[cfg(feature = "multipart")]
|
#[cfg(feature = "multipart")]
|
||||||
use super::multipart;
|
use super::multipart;
|
||||||
use super::response::Response;
|
use super::response::Response;
|
||||||
#[cfg(feature = "multipart")]
|
#[cfg(feature = "multipart")]
|
||||||
use crate::header::CONTENT_LENGTH;
|
use crate::header::CONTENT_LENGTH;
|
||||||
use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE};
|
use crate::header::{ HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE };
|
||||||
use crate::{Method, Url};
|
use crate::{ Method, Url };
|
||||||
use http::{request::Parts, Request as HttpRequest, Version};
|
use http::{ request::Parts, Request as HttpRequest, Version };
|
||||||
|
|
||||||
/// A request which can be executed with `Client::execute()`.
|
/// A request which can be executed with `Client::execute()`.
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
@@ -142,23 +142,9 @@ impl Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn pieces(
|
pub(super) fn pieces(
|
||||||
self,
|
self
|
||||||
) -> (
|
) -> (Method, Url, HeaderMap, Option<Body>, Option<Duration>, Version) {
|
||||||
Method,
|
(self.method, self.url, self.headers, self.body, self.timeout, self.version)
|
||||||
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 {
|
pub(super) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder {
|
||||||
let mut builder = RequestBuilder { client, request };
|
let mut builder = RequestBuilder { client, request };
|
||||||
|
|
||||||
let auth = builder
|
let auth = builder.request
|
||||||
.request
|
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|req| extract_authority(&mut req.url));
|
.and_then(|req| extract_authority(&mut req.url));
|
||||||
@@ -181,39 +166,44 @@ impl RequestBuilder {
|
|||||||
|
|
||||||
/// Add a `Header` to this Request.
|
/// Add a `Header` to this Request.
|
||||||
pub fn header<K, V>(self, key: K, value: V) -> RequestBuilder
|
pub fn header<K, V>(self, key: K, value: V) -> RequestBuilder
|
||||||
where
|
where
|
||||||
HeaderName: TryFrom<K>,
|
HeaderName: TryFrom<K>,
|
||||||
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
||||||
HeaderValue: TryFrom<V>,
|
HeaderValue: TryFrom<V>,
|
||||||
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
|
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>
|
||||||
{
|
{
|
||||||
self.header_sensitive(key, value, false)
|
self.header_sensitive(key, value, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a `Header` to this Request with ability to define if header_value is sensitive.
|
/// 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
|
fn header_sensitive<K, V>(mut self, key: K, value: V, sensitive: bool) -> RequestBuilder
|
||||||
where
|
where
|
||||||
HeaderName: TryFrom<K>,
|
HeaderName: TryFrom<K>,
|
||||||
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
||||||
HeaderValue: TryFrom<V>,
|
HeaderValue: TryFrom<V>,
|
||||||
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
|
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>
|
||||||
{
|
{
|
||||||
let mut error = None;
|
let mut error = None;
|
||||||
if let Ok(ref mut req) = self.request {
|
if let Ok(ref mut req) = self.request {
|
||||||
match <HeaderName as TryFrom<K>>::try_from(key) {
|
match <HeaderName as TryFrom<K>>::try_from(key) {
|
||||||
Ok(key) => match <HeaderValue as TryFrom<V>>::try_from(value) {
|
Ok(key) =>
|
||||||
Ok(mut value) => {
|
match <HeaderValue as TryFrom<V>>::try_from(value) {
|
||||||
// We want to potentially make an unsensitive header
|
Ok(mut value) => {
|
||||||
// to be sensitive, not the reverse. So, don't turn off
|
// We want to potentially make an unsensitive header
|
||||||
// a previously sensitive header.
|
// to be sensitive, not the reverse. So, don't turn off
|
||||||
if sensitive {
|
// a previously sensitive header.
|
||||||
value.set_sensitive(true);
|
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 {
|
if let Some(err) = error {
|
||||||
@@ -247,14 +237,14 @@ impl RequestBuilder {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
|
pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
|
||||||
where
|
where U: fmt::Display, P: fmt::Display
|
||||||
U: fmt::Display,
|
|
||||||
P: fmt::Display,
|
|
||||||
{
|
{
|
||||||
let mut header_value = b"Basic ".to_vec();
|
let mut header_value = b"Basic ".to_vec();
|
||||||
{
|
{
|
||||||
let mut encoder =
|
let mut encoder = Base64Encoder::from(
|
||||||
Base64Encoder::from(&mut header_value, &base64::engine::DEFAULT_ENGINE);
|
&mut header_value,
|
||||||
|
&base64::engine::DEFAULT_ENGINE
|
||||||
|
);
|
||||||
// The unwraps here are fine because Vec::write* is infallible.
|
// The unwraps here are fine because Vec::write* is infallible.
|
||||||
write!(encoder, "{}:", username).unwrap();
|
write!(encoder, "{}:", username).unwrap();
|
||||||
if let Some(password) = password {
|
if let Some(password) = password {
|
||||||
@@ -266,10 +256,7 @@ impl RequestBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Enable HTTP bearer authentication.
|
/// Enable HTTP bearer authentication.
|
||||||
pub fn bearer_auth<T>(self, token: T) -> RequestBuilder
|
pub fn bearer_auth<T>(self, token: T) -> RequestBuilder where T: fmt::Display {
|
||||||
where
|
|
||||||
T: fmt::Display,
|
|
||||||
{
|
|
||||||
let header_value = format!("Bearer {}", token);
|
let header_value = format!("Bearer {}", token);
|
||||||
self.header_sensitive(crate::header::AUTHORIZATION, header_value, true)
|
self.header_sensitive(crate::header::AUTHORIZATION, header_value, true)
|
||||||
}
|
}
|
||||||
@@ -318,7 +305,7 @@ impl RequestBuilder {
|
|||||||
pub fn multipart(self, mut multipart: 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()
|
||||||
);
|
);
|
||||||
|
|
||||||
builder = match multipart.compute_length() {
|
builder = match multipart.compute_length() {
|
||||||
@@ -327,7 +314,7 @@ impl RequestBuilder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(ref mut req) = builder.request {
|
if let Ok(ref mut req) = builder.request {
|
||||||
*req.body_mut() = Some(multipart.stream())
|
*req.body_mut() = Some(multipart.stream());
|
||||||
}
|
}
|
||||||
builder
|
builder
|
||||||
}
|
}
|
||||||
@@ -414,11 +401,13 @@ impl RequestBuilder {
|
|||||||
Ok(body) => {
|
Ok(body) => {
|
||||||
req.headers_mut().insert(
|
req.headers_mut().insert(
|
||||||
CONTENT_TYPE,
|
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());
|
*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 {
|
if let Some(err) = error {
|
||||||
@@ -444,11 +433,15 @@ impl RequestBuilder {
|
|||||||
if let Ok(ref mut req) = self.request {
|
if let Ok(ref mut req) = self.request {
|
||||||
match serde_json::to_vec(json) {
|
match serde_json::to_vec(json) {
|
||||||
Ok(body) => {
|
Ok(body) => {
|
||||||
req.headers_mut()
|
req.headers_mut().insert(
|
||||||
.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
|
CONTENT_TYPE,
|
||||||
|
HeaderValue::from_static("application/json")
|
||||||
|
);
|
||||||
*req.body_mut() = Some(body.into());
|
*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 {
|
if let Some(err) = error {
|
||||||
@@ -553,11 +546,9 @@ impl fmt::Debug for RequestBuilder {
|
|||||||
|
|
||||||
fn fmt_request_fields<'a, 'b>(
|
fn fmt_request_fields<'a, 'b>(
|
||||||
f: &'a mut fmt::DebugStruct<'a, 'b>,
|
f: &'a mut fmt::DebugStruct<'a, 'b>,
|
||||||
req: &Request,
|
req: &Request
|
||||||
) -> &'a mut fmt::DebugStruct<'a, 'b> {
|
) -> &'a mut fmt::DebugStruct<'a, 'b> {
|
||||||
f.field("method", &req.method)
|
f.field("method", &req.method).field("url", &req.url).field("headers", &req.headers)
|
||||||
.field("url", &req.url)
|
|
||||||
.field("headers", &req.headers)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check the request URL for a "username:password" type authority, and if
|
/// 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;
|
use percent_encoding::percent_decode;
|
||||||
|
|
||||||
if url.has_authority() {
|
if url.has_authority() {
|
||||||
let username: String = percent_decode(url.username().as_bytes())
|
let username: String = percent_decode(url.username().as_bytes()).decode_utf8().ok()?.into();
|
||||||
.decode_utf8()
|
let password = url
|
||||||
.ok()?
|
.password()
|
||||||
.into();
|
.and_then(|pass| {
|
||||||
let password = url.password().and_then(|pass| {
|
percent_decode(pass.as_bytes()).decode_utf8().ok().map(String::from)
|
||||||
percent_decode(pass.as_bytes())
|
});
|
||||||
.decode_utf8()
|
|
||||||
.ok()
|
|
||||||
.map(String::from)
|
|
||||||
});
|
|
||||||
if !username.is_empty() || password.is_some() {
|
if !username.is_empty() || password.is_some() {
|
||||||
url.set_username("")
|
url.set_username("").expect("has_authority means set_username shouldn't fail");
|
||||||
.expect("has_authority means set_username shouldn't fail");
|
url.set_password(None).expect("has_authority means set_password shouldn't fail");
|
||||||
url.set_password(None)
|
|
||||||
.expect("has_authority means set_password shouldn't fail");
|
|
||||||
return Some((username, password));
|
return Some((username, password));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -588,21 +573,12 @@ pub(crate) fn extract_authority(url: &mut Url) -> Option<(String, Option<String>
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> TryFrom<HttpRequest<T>> for Request
|
impl<T> TryFrom<HttpRequest<T>> for Request where T: Into<Body> {
|
||||||
where
|
|
||||||
T: Into<Body>,
|
|
||||||
{
|
|
||||||
type Error = crate::Error;
|
type Error = crate::Error;
|
||||||
|
|
||||||
fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
|
fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
|
||||||
let (parts, body) = req.into_parts();
|
let (parts, body) = req.into_parts();
|
||||||
let Parts {
|
let Parts { method, uri, headers, version, .. } = parts;
|
||||||
method,
|
|
||||||
uri,
|
|
||||||
headers,
|
|
||||||
version,
|
|
||||||
..
|
|
||||||
} = parts;
|
|
||||||
let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
|
let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
|
||||||
Ok(Request {
|
Ok(Request {
|
||||||
method,
|
method,
|
||||||
@@ -619,14 +595,7 @@ impl TryFrom<Request> for HttpRequest<Body> {
|
|||||||
type Error = crate::Error;
|
type Error = crate::Error;
|
||||||
|
|
||||||
fn try_from(req: Request) -> crate::Result<Self> {
|
fn try_from(req: Request) -> crate::Result<Self> {
|
||||||
let Request {
|
let Request { method, url, headers, body, version, .. } = req;
|
||||||
method,
|
|
||||||
url,
|
|
||||||
headers,
|
|
||||||
body,
|
|
||||||
version,
|
|
||||||
..
|
|
||||||
} = req;
|
|
||||||
|
|
||||||
let mut req = HttpRequest::builder()
|
let mut req = HttpRequest::builder()
|
||||||
.version(version)
|
.version(version)
|
||||||
@@ -642,7 +611,7 @@ impl TryFrom<Request> for HttpRequest<Body> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{Client, HttpRequest, Request, Version};
|
use super::{ Client, HttpRequest, Request, Version };
|
||||||
use crate::Method;
|
use crate::Method;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
@@ -667,7 +636,12 @@ mod tests {
|
|||||||
let some_url = "https://google.com/";
|
let some_url = "https://google.com/";
|
||||||
let r = client.get(some_url);
|
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");
|
let req = r.build().expect("request is valid");
|
||||||
assert_eq!(req.url().query(), Some("foo=a&foo=b"));
|
assert_eq!(req.url().query(), Some("foo=a&foo=b"));
|
||||||
@@ -743,11 +717,7 @@ mod tests {
|
|||||||
let some_url = "https://google.com/";
|
let some_url = "https://google.com/";
|
||||||
let empty_query: &[(&str, &str)] = &[];
|
let empty_query: &[(&str, &str)] = &[];
|
||||||
|
|
||||||
let req = client
|
let req = client.get(some_url).query(empty_query).build().expect("request build");
|
||||||
.get(some_url)
|
|
||||||
.query(empty_query)
|
|
||||||
.build()
|
|
||||||
.expect("request build");
|
|
||||||
|
|
||||||
assert_eq!(req.url().query(), None);
|
assert_eq!(req.url().query(), None);
|
||||||
assert_eq!(req.url().as_str(), "https://google.com/");
|
assert_eq!(req.url().as_str(), "https://google.com/");
|
||||||
@@ -760,11 +730,7 @@ mod tests {
|
|||||||
.post("http://httpbin.org/post")
|
.post("http://httpbin.org/post")
|
||||||
.header("foo", "bar")
|
.header("foo", "bar")
|
||||||
.body("from a &str!");
|
.body("from a &str!");
|
||||||
let req = builder
|
let req = builder.try_clone().expect("clone successful").build().expect("request is valid");
|
||||||
.try_clone()
|
|
||||||
.expect("clone successful")
|
|
||||||
.build()
|
|
||||||
.expect("request is valid");
|
|
||||||
assert_eq!(req.url().as_str(), "http://httpbin.org/post");
|
assert_eq!(req.url().as_str(), "http://httpbin.org/post");
|
||||||
assert_eq!(req.method(), Method::POST);
|
assert_eq!(req.method(), Method::POST);
|
||||||
assert_eq!(req.headers()["foo"], "bar");
|
assert_eq!(req.headers()["foo"], "bar");
|
||||||
@@ -774,11 +740,7 @@ mod tests {
|
|||||||
fn try_clone_no_body() {
|
fn try_clone_no_body() {
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
let builder = client.get("http://httpbin.org/get");
|
let builder = client.get("http://httpbin.org/get");
|
||||||
let req = builder
|
let req = builder.try_clone().expect("clone successful").build().expect("request is valid");
|
||||||
.try_clone()
|
|
||||||
.expect("clone successful")
|
|
||||||
.build()
|
|
||||||
.expect("request is valid");
|
|
||||||
assert_eq!(req.url().as_str(), "http://httpbin.org/get");
|
assert_eq!(req.url().as_str(), "http://httpbin.org/get");
|
||||||
assert_eq!(req.method(), Method::GET);
|
assert_eq!(req.method(), Method::GET);
|
||||||
assert!(req.body().is_none());
|
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 chunks: Vec<Result<_, ::std::io::Error>> = vec![Ok("hello"), Ok(" "), Ok("world")];
|
||||||
let stream = futures_util::stream::iter(chunks);
|
let stream = futures_util::stream::iter(chunks);
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
let builder = client
|
let builder = client.get("http://httpbin.org/get").body(super::Body::wrap_stream(stream));
|
||||||
.get("http://httpbin.org/get")
|
|
||||||
.body(super::Body::wrap_stream(stream));
|
|
||||||
let clone = builder.try_clone();
|
let clone = builder.try_clone();
|
||||||
assert!(clone.is_none());
|
assert!(clone.is_none());
|
||||||
}
|
}
|
||||||
@@ -805,10 +765,7 @@ mod tests {
|
|||||||
let req = client.get(some_url).build().expect("request build");
|
let req = client.get(some_url).build().expect("request build");
|
||||||
|
|
||||||
assert_eq!(req.url().as_str(), "https://localhost/");
|
assert_eq!(req.url().as_str(), "https://localhost/");
|
||||||
assert_eq!(
|
assert_eq!(req.headers()["authorization"], "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
|
||||||
req.headers()["authorization"],
|
|
||||||
"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -823,10 +780,7 @@ mod tests {
|
|||||||
.expect("request build");
|
.expect("request build");
|
||||||
|
|
||||||
assert_eq!(req.url().as_str(), "https://localhost/");
|
assert_eq!(req.url().as_str(), "https://localhost/");
|
||||||
assert_eq!(
|
assert_eq!(req.headers()["authorization"], "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
|
||||||
req.headers()["authorization"],
|
|
||||||
"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
|
|
||||||
);
|
|
||||||
assert!(req.headers()["authorization"].is_sensitive());
|
assert!(req.headers()["authorization"].is_sensitive());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -835,11 +789,7 @@ mod tests {
|
|||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
let some_url = "https://localhost/";
|
let some_url = "https://localhost/";
|
||||||
|
|
||||||
let req = client
|
let req = client.get(some_url).bearer_auth("Hold my bear").build().expect("request build");
|
||||||
.get(some_url)
|
|
||||||
.bearer_auth("Hold my bear")
|
|
||||||
.build()
|
|
||||||
.expect("request build");
|
|
||||||
|
|
||||||
assert_eq!(req.url().as_str(), "https://localhost/");
|
assert_eq!(req.url().as_str(), "https://localhost/");
|
||||||
assert_eq!(req.headers()["authorization"], "Bearer Hold my bear");
|
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");
|
let mut header = http::HeaderValue::from_static("in plain sight");
|
||||||
header.set_sensitive(true);
|
header.set_sensitive(true);
|
||||||
|
|
||||||
let req = client
|
let req = client.get(some_url).header("hiding", header).build().expect("request build");
|
||||||
.get(some_url)
|
|
||||||
.header("hiding", header)
|
|
||||||
.build()
|
|
||||||
.expect("request build");
|
|
||||||
|
|
||||||
assert_eq!(req.url().as_str(), "https://localhost/");
|
assert_eq!(req.url().as_str(), "https://localhost/");
|
||||||
assert_eq!(req.headers()["hiding"], "in plain sight");
|
assert_eq!(req.headers()["hiding"], "in plain sight");
|
||||||
|
|||||||
@@ -3,18 +3,18 @@ use std::fmt;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use base64::encode;
|
use base64::encode;
|
||||||
use http::{request::Parts, Request as HttpRequest, Version};
|
use http::{ request::Parts, Request as HttpRequest, Version };
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
#[cfg(feature = "json")]
|
#[cfg(feature = "json")]
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use serde_urlencoded;
|
use serde_urlencoded;
|
||||||
|
|
||||||
use super::body::{self, Body};
|
use super::body::{ self, Body };
|
||||||
#[cfg(feature = "multipart")]
|
#[cfg(feature = "multipart")]
|
||||||
use super::multipart;
|
use super::multipart;
|
||||||
use super::Client;
|
use super::Client;
|
||||||
use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE};
|
use crate::header::{ HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE };
|
||||||
use crate::{async_impl, Method, Url};
|
use crate::{ async_impl, Method, Url };
|
||||||
|
|
||||||
/// A request which can be executed with `Client::execute()`.
|
/// A request which can be executed with `Client::execute()`.
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
@@ -155,8 +155,7 @@ impl RequestBuilder {
|
|||||||
pub(crate) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder {
|
pub(crate) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder {
|
||||||
let mut builder = RequestBuilder { client, request };
|
let mut builder = RequestBuilder { client, request };
|
||||||
|
|
||||||
let auth = builder
|
let auth = builder.request
|
||||||
.request
|
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|req| async_impl::request::extract_authority(req.url_mut()));
|
.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
|
pub fn header<K, V>(self, key: K, value: V) -> RequestBuilder
|
||||||
where
|
where
|
||||||
HeaderName: TryFrom<K>,
|
HeaderName: TryFrom<K>,
|
||||||
HeaderValue: TryFrom<V>,
|
HeaderValue: TryFrom<V>,
|
||||||
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
||||||
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
|
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>
|
||||||
{
|
{
|
||||||
self.header_sensitive(key, value, false)
|
self.header_sensitive(key, value, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a `Header` to this Request with ability to define if header_value is sensitive.
|
/// 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
|
fn header_sensitive<K, V>(mut self, key: K, value: V, sensitive: bool) -> RequestBuilder
|
||||||
where
|
where
|
||||||
HeaderName: TryFrom<K>,
|
HeaderName: TryFrom<K>,
|
||||||
HeaderValue: TryFrom<V>,
|
HeaderValue: TryFrom<V>,
|
||||||
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
||||||
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
|
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>
|
||||||
{
|
{
|
||||||
let mut error = None;
|
let mut error = None;
|
||||||
if let Ok(ref mut req) = self.request {
|
if let Ok(ref mut req) = self.request {
|
||||||
match <HeaderName as TryFrom<K>>::try_from(key) {
|
match <HeaderName as TryFrom<K>>::try_from(key) {
|
||||||
Ok(key) => match <HeaderValue as TryFrom<V>>::try_from(value) {
|
Ok(key) =>
|
||||||
Ok(mut value) => {
|
match <HeaderValue as TryFrom<V>>::try_from(value) {
|
||||||
value.set_sensitive(sensitive);
|
Ok(mut value) => {
|
||||||
req.headers_mut().append(key, 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 {
|
if let Some(err) = error {
|
||||||
@@ -262,9 +266,7 @@ impl RequestBuilder {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
|
pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
|
||||||
where
|
where U: fmt::Display, P: fmt::Display
|
||||||
U: fmt::Display,
|
|
||||||
P: fmt::Display,
|
|
||||||
{
|
{
|
||||||
let auth = match password {
|
let auth = match password {
|
||||||
Some(password) => format!("{}:{}", username, password),
|
Some(password) => format!("{}:{}", username, password),
|
||||||
@@ -285,10 +287,7 @@ impl RequestBuilder {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn bearer_auth<T>(self, token: T) -> RequestBuilder
|
pub fn bearer_auth<T>(self, token: T) -> RequestBuilder where T: fmt::Display {
|
||||||
where
|
|
||||||
T: fmt::Display,
|
|
||||||
{
|
|
||||||
let header_value = format!("Bearer {}", token);
|
let header_value = format!("Bearer {}", token);
|
||||||
self.header_sensitive(crate::header::AUTHORIZATION, &*header_value, true)
|
self.header_sensitive(crate::header::AUTHORIZATION, &*header_value, true)
|
||||||
}
|
}
|
||||||
@@ -448,11 +447,13 @@ impl RequestBuilder {
|
|||||||
Ok(body) => {
|
Ok(body) => {
|
||||||
req.headers_mut().insert(
|
req.headers_mut().insert(
|
||||||
CONTENT_TYPE,
|
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());
|
*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 {
|
if let Some(err) = error {
|
||||||
@@ -499,11 +500,15 @@ impl RequestBuilder {
|
|||||||
if let Ok(ref mut req) = self.request {
|
if let Ok(ref mut req) = self.request {
|
||||||
match serde_json::to_vec(json) {
|
match serde_json::to_vec(json) {
|
||||||
Ok(body) => {
|
Ok(body) => {
|
||||||
req.headers_mut()
|
req.headers_mut().insert(
|
||||||
.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
|
CONTENT_TYPE,
|
||||||
|
HeaderValue::from_static("application/json")
|
||||||
|
);
|
||||||
*req.body_mut() = Some(body.into());
|
*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 {
|
if let Some(err) = error {
|
||||||
@@ -536,13 +541,13 @@ impl RequestBuilder {
|
|||||||
pub fn multipart(self, mut multipart: 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()
|
||||||
);
|
);
|
||||||
if let Ok(ref mut req) = builder.request {
|
if let Ok(ref mut req) = builder.request {
|
||||||
*req.body_mut() = Some(match multipart.compute_length() {
|
*req.body_mut() = Some(match multipart.compute_length() {
|
||||||
Some(length) => Body::sized(multipart.reader(), length),
|
Some(length) => Body::sized(multipart.reader(), length),
|
||||||
None => Body::new(multipart.reader()),
|
None => Body::new(multipart.reader()),
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
builder
|
builder
|
||||||
}
|
}
|
||||||
@@ -619,20 +624,12 @@ impl RequestBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> TryFrom<HttpRequest<T>> for Request
|
impl<T> TryFrom<HttpRequest<T>> for Request where T: Into<Body> {
|
||||||
where
|
|
||||||
T: Into<Body>,
|
|
||||||
{
|
|
||||||
type Error = crate::Error;
|
type Error = crate::Error;
|
||||||
|
|
||||||
fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
|
fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
|
||||||
let (parts, body) = req.into_parts();
|
let (parts, body) = req.into_parts();
|
||||||
let Parts {
|
let Parts { method, uri, headers, .. } = parts;
|
||||||
method,
|
|
||||||
uri,
|
|
||||||
headers,
|
|
||||||
..
|
|
||||||
} = parts;
|
|
||||||
let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
|
let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
|
||||||
let mut inner = async_impl::Request::new(method, url);
|
let mut inner = async_impl::Request::new(method, url);
|
||||||
crate::util::replace_headers(inner.headers_mut(), headers);
|
crate::util::replace_headers(inner.headers_mut(), headers);
|
||||||
@@ -651,24 +648,22 @@ impl fmt::Debug for Request {
|
|||||||
|
|
||||||
fn fmt_request_fields<'a, 'b>(
|
fn fmt_request_fields<'a, 'b>(
|
||||||
f: &'a mut fmt::DebugStruct<'a, 'b>,
|
f: &'a mut fmt::DebugStruct<'a, 'b>,
|
||||||
req: &Request,
|
req: &Request
|
||||||
) -> &'a mut fmt::DebugStruct<'a, 'b> {
|
) -> &'a mut fmt::DebugStruct<'a, 'b> {
|
||||||
f.field("method", req.method())
|
f.field("method", req.method()).field("url", req.url()).field("headers", req.headers())
|
||||||
.field("url", req.url())
|
|
||||||
.field("headers", req.headers())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::super::{body, Client};
|
use super::super::{ body, Client };
|
||||||
use super::{HttpRequest, Request, Version};
|
use super::{ HttpRequest, Request, Version };
|
||||||
use crate::header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE, HOST};
|
use crate::header::{ HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE, HOST };
|
||||||
use crate::Method;
|
use crate::Method;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
#[cfg(feature = "json")]
|
#[cfg(feature = "json")]
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use serde_urlencoded;
|
use serde_urlencoded;
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{ BTreeMap, HashMap };
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -822,7 +817,12 @@ mod tests {
|
|||||||
let some_url = "https://google.com/";
|
let some_url = "https://google.com/";
|
||||||
let mut r = client.get(some_url);
|
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");
|
let req = r.build().expect("request is valid");
|
||||||
assert_eq!(req.url().query(), Some("foo=a&foo=b"));
|
assert_eq!(req.url().query(), Some("foo=a&foo=b"));
|
||||||
@@ -881,7 +881,7 @@ mod tests {
|
|||||||
// Make sure the content type was set
|
// Make sure the content type was set
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
r.headers().get(CONTENT_TYPE).unwrap(),
|
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();
|
let buf = body::read_to_string(r.body_mut().take().unwrap()).unwrap();
|
||||||
@@ -915,14 +915,11 @@ mod tests {
|
|||||||
#[cfg(feature = "json")]
|
#[cfg(feature = "json")]
|
||||||
fn add_json_fail() {
|
fn add_json_fail() {
|
||||||
use serde::ser::Error as _;
|
use serde::ser::Error as _;
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{ Serialize, Serializer };
|
||||||
use std::error::Error as _;
|
use std::error::Error as _;
|
||||||
struct MyStruct;
|
struct MyStruct;
|
||||||
impl Serialize for MyStruct {
|
impl Serialize for MyStruct {
|
||||||
fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
Err(S::Error::custom("nope"))
|
Err(S::Error::custom("nope"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -967,11 +964,7 @@ mod tests {
|
|||||||
let some_url = "https://google.com/";
|
let some_url = "https://google.com/";
|
||||||
let empty_query: &[(&str, &str)] = &[];
|
let empty_query: &[(&str, &str)] = &[];
|
||||||
|
|
||||||
let req = client
|
let req = client.get(some_url).query(empty_query).build().expect("request build");
|
||||||
.get(some_url)
|
|
||||||
.query(empty_query)
|
|
||||||
.build()
|
|
||||||
.expect("request build");
|
|
||||||
|
|
||||||
assert_eq!(req.url().query(), None);
|
assert_eq!(req.url().query(), None);
|
||||||
assert_eq!(req.url().as_str(), "https://google.com/");
|
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");
|
let req = client.get(some_url).build().expect("request build");
|
||||||
|
|
||||||
assert_eq!(req.url().as_str(), "https://localhost/");
|
assert_eq!(req.url().as_str(), "https://localhost/");
|
||||||
assert_eq!(
|
assert_eq!(req.headers()["authorization"], "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
|
||||||
req.headers()["authorization"],
|
|
||||||
"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1041,10 +1031,7 @@ mod tests {
|
|||||||
.expect("request build");
|
.expect("request build");
|
||||||
|
|
||||||
assert_eq!(req.url().as_str(), "https://localhost/");
|
assert_eq!(req.url().as_str(), "https://localhost/");
|
||||||
assert_eq!(
|
assert_eq!(req.headers()["authorization"], "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
|
||||||
req.headers()["authorization"],
|
|
||||||
"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
|
|
||||||
);
|
|
||||||
assert_eq!(req.headers()["authorization"].is_sensitive(), true);
|
assert_eq!(req.headers()["authorization"].is_sensitive(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1053,11 +1040,7 @@ mod tests {
|
|||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
let some_url = "https://localhost/";
|
let some_url = "https://localhost/";
|
||||||
|
|
||||||
let req = client
|
let req = client.get(some_url).bearer_auth("Hold my bear").build().expect("request build");
|
||||||
.get(some_url)
|
|
||||||
.bearer_auth("Hold my bear")
|
|
||||||
.build()
|
|
||||||
.expect("request build");
|
|
||||||
|
|
||||||
assert_eq!(req.url().as_str(), "https://localhost/");
|
assert_eq!(req.url().as_str(), "https://localhost/");
|
||||||
assert_eq!(req.headers()["authorization"], "Bearer Hold my bear");
|
assert_eq!(req.headers()["authorization"], "Bearer Hold my bear");
|
||||||
|
|||||||
@@ -27,4 +27,7 @@ pub enum ChromeVersion {
|
|||||||
V104,
|
V104,
|
||||||
V105,
|
V105,
|
||||||
V106,
|
V106,
|
||||||
|
V108,
|
||||||
|
V114,
|
||||||
|
V129
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,17 @@ use super::ChromeVersion;
|
|||||||
mod v104;
|
mod v104;
|
||||||
mod v105;
|
mod v105;
|
||||||
mod v106;
|
mod v106;
|
||||||
|
mod v108;
|
||||||
|
mod v114;
|
||||||
|
mod v129;
|
||||||
|
|
||||||
pub(super) fn get_config_from_ver(ver: ChromeVersion) -> BrowserSettings {
|
pub(super) fn get_config_from_ver(ver: ChromeVersion) -> BrowserSettings {
|
||||||
match ver {
|
match ver {
|
||||||
ChromeVersion::V104 => v104::get_settings(),
|
ChromeVersion::V104 => v104::get_settings(),
|
||||||
ChromeVersion::V105 => v105::get_settings(),
|
ChromeVersion::V105 => v105::get_settings(),
|
||||||
ChromeVersion::V106 => v106::get_settings(),
|
ChromeVersion::V106 => v106::get_settings(),
|
||||||
|
ChromeVersion::V108 => v108::get_settings(),
|
||||||
|
ChromeVersion::V114 => v114::get_settings(),
|
||||||
|
ChromeVersion::V129 => v129::get_settings(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
106
src/browser/chrome/ver/v108.rs
Normal file
106
src/browser/chrome/ver/v108.rs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
use boring::ssl::{
|
||||||
|
CertCompressionAlgorithm,
|
||||||
|
SslConnector,
|
||||||
|
SslConnectorBuilder,
|
||||||
|
SslMethod,
|
||||||
|
SslVersion,
|
||||||
|
};
|
||||||
|
use http::{
|
||||||
|
header::{ ACCEPT, ACCEPT_ENCODING, ACCEPT_LANGUAGE, UPGRADE_INSECURE_REQUESTS, USER_AGENT },
|
||||||
|
HeaderMap,
|
||||||
|
HeaderValue,
|
||||||
|
};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::browser::{ BrowserSettings, Http2Data };
|
||||||
|
|
||||||
|
pub(super) fn get_settings() -> BrowserSettings {
|
||||||
|
BrowserSettings {
|
||||||
|
tls_builder_func: Arc::new(create_ssl_connector),
|
||||||
|
http2: Http2Data {
|
||||||
|
initial_stream_window_size: 6291456,
|
||||||
|
initial_connection_window_size: 15728640,
|
||||||
|
max_concurrent_streams: 1000,
|
||||||
|
max_header_list_size: 262144,
|
||||||
|
header_table_size: 65536,
|
||||||
|
enable_push: Some(false),
|
||||||
|
},
|
||||||
|
headers: create_headers(),
|
||||||
|
gzip: true,
|
||||||
|
brotli: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_ssl_connector() -> SslConnectorBuilder {
|
||||||
|
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||||
|
|
||||||
|
builder.set_grease_enabled(true);
|
||||||
|
|
||||||
|
builder.enable_ocsp_stapling();
|
||||||
|
|
||||||
|
let cipher_list = [
|
||||||
|
"TLS_AES_128_GCM_SHA256",
|
||||||
|
"TLS_AES_256_GCM_SHA384",
|
||||||
|
"TLS_CHACHA20_POLY1305_SHA256",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||||
|
"TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||||
|
"TLS_RSA_WITH_AES_256_CBC_SHA",
|
||||||
|
];
|
||||||
|
|
||||||
|
builder.set_cipher_list(&cipher_list.join(":")).unwrap();
|
||||||
|
|
||||||
|
let sigalgs_list = [
|
||||||
|
"ecdsa_secp256r1_sha256",
|
||||||
|
"rsa_pss_rsae_sha256",
|
||||||
|
"rsa_pkcs1_sha256",
|
||||||
|
"ecdsa_secp384r1_sha384",
|
||||||
|
"rsa_pss_rsae_sha384",
|
||||||
|
"rsa_pkcs1_sha384",
|
||||||
|
"rsa_pss_rsae_sha512",
|
||||||
|
"rsa_pkcs1_sha512",
|
||||||
|
];
|
||||||
|
|
||||||
|
builder.set_sigalgs_list(&sigalgs_list.join(":")).unwrap();
|
||||||
|
|
||||||
|
builder.enable_signed_cert_timestamps();
|
||||||
|
|
||||||
|
builder.set_alpn_protos(b"\x02h2\x08http/1.1").unwrap();
|
||||||
|
|
||||||
|
builder.add_cert_compression_alg(CertCompressionAlgorithm::Brotli).unwrap();
|
||||||
|
|
||||||
|
builder.set_min_proto_version(Some(SslVersion::TLS1_2)).unwrap();
|
||||||
|
|
||||||
|
builder.set_max_proto_version(Some(SslVersion::TLS1_3)).unwrap();
|
||||||
|
|
||||||
|
builder
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_headers() -> HeaderMap {
|
||||||
|
let mut headers = HeaderMap::new();
|
||||||
|
|
||||||
|
headers.insert(
|
||||||
|
"sec-ch-ua",
|
||||||
|
HeaderValue::from_static(
|
||||||
|
"\"Not?A_Brand\";v=\"8\", \"Chromium\";v=\"108\", \"Google Chrome\";v=\"108\""
|
||||||
|
)
|
||||||
|
);
|
||||||
|
headers.insert(
|
||||||
|
USER_AGENT,
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"
|
||||||
|
.parse()
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
headers.insert(ACCEPT_ENCODING, "gzip, deflate, br".parse().unwrap());
|
||||||
|
headers.insert(ACCEPT_LANGUAGE, "en-US,en;q=0.9".parse().unwrap());
|
||||||
|
|
||||||
|
headers
|
||||||
|
}
|
||||||
109
src/browser/chrome/ver/v114.rs
Normal file
109
src/browser/chrome/ver/v114.rs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
use boring::ssl::{
|
||||||
|
CertCompressionAlgorithm,
|
||||||
|
SslConnector,
|
||||||
|
SslConnectorBuilder,
|
||||||
|
SslMethod,
|
||||||
|
SslVersion,
|
||||||
|
SslVerifyMode,
|
||||||
|
};
|
||||||
|
use http::{
|
||||||
|
header::{ ACCEPT, ACCEPT_ENCODING, ACCEPT_LANGUAGE, UPGRADE_INSECURE_REQUESTS, USER_AGENT },
|
||||||
|
HeaderMap,
|
||||||
|
HeaderValue,
|
||||||
|
};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::browser::{ BrowserSettings, Http2Data };
|
||||||
|
|
||||||
|
pub(super) fn get_settings() -> BrowserSettings {
|
||||||
|
BrowserSettings {
|
||||||
|
tls_builder_func: Arc::new(create_ssl_connector),
|
||||||
|
http2: Http2Data {
|
||||||
|
initial_stream_window_size: 6291456,
|
||||||
|
initial_connection_window_size: 15728640,
|
||||||
|
max_concurrent_streams: 1000,
|
||||||
|
max_header_list_size: 262144,
|
||||||
|
header_table_size: 65536,
|
||||||
|
enable_push: Some(false),
|
||||||
|
},
|
||||||
|
headers: create_headers(),
|
||||||
|
gzip: true,
|
||||||
|
brotli: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_ssl_connector() -> SslConnectorBuilder {
|
||||||
|
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||||
|
|
||||||
|
builder.set_grease_enabled(true);
|
||||||
|
|
||||||
|
builder.enable_ocsp_stapling();
|
||||||
|
|
||||||
|
builder.set_verify(SslVerifyMode::NONE);
|
||||||
|
|
||||||
|
let cipher_list = [
|
||||||
|
"TLS_AES_128_GCM_SHA256",
|
||||||
|
"TLS_AES_256_GCM_SHA384",
|
||||||
|
"TLS_CHACHA20_POLY1305_SHA256",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||||
|
"TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||||
|
"TLS_RSA_WITH_AES_256_CBC_SHA",
|
||||||
|
];
|
||||||
|
|
||||||
|
builder.set_cipher_list(&cipher_list.join(":")).unwrap();
|
||||||
|
|
||||||
|
let sigalgs_list = [
|
||||||
|
"ecdsa_secp256r1_sha256",
|
||||||
|
"rsa_pss_rsae_sha256",
|
||||||
|
"rsa_pkcs1_sha256",
|
||||||
|
"ecdsa_secp384r1_sha384",
|
||||||
|
"rsa_pss_rsae_sha384",
|
||||||
|
"rsa_pkcs1_sha384",
|
||||||
|
"rsa_pss_rsae_sha512",
|
||||||
|
"rsa_pkcs1_sha512",
|
||||||
|
];
|
||||||
|
|
||||||
|
builder.set_sigalgs_list(&sigalgs_list.join(":")).unwrap();
|
||||||
|
|
||||||
|
builder.enable_signed_cert_timestamps();
|
||||||
|
|
||||||
|
builder.set_alpn_protos(b"\x02h2\x08http/1.1").unwrap();
|
||||||
|
|
||||||
|
builder.add_cert_compression_alg(CertCompressionAlgorithm::Brotli).unwrap();
|
||||||
|
|
||||||
|
builder.set_min_proto_version(Some(SslVersion::TLS1_2)).unwrap();
|
||||||
|
|
||||||
|
builder.set_max_proto_version(Some(SslVersion::TLS1_3)).unwrap();
|
||||||
|
|
||||||
|
builder
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_headers() -> HeaderMap {
|
||||||
|
let mut headers = HeaderMap::new();
|
||||||
|
|
||||||
|
headers.insert(
|
||||||
|
"sec-ch-ua",
|
||||||
|
HeaderValue::from_static(
|
||||||
|
"\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Google Chrome\";v=\"114\""
|
||||||
|
)
|
||||||
|
);
|
||||||
|
headers.insert(
|
||||||
|
USER_AGENT,
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
|
||||||
|
.parse()
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
headers.insert(ACCEPT_ENCODING, "gzip, deflate, br".parse().unwrap());
|
||||||
|
headers.insert(ACCEPT_LANGUAGE, "en-US,en;q=0.9".parse().unwrap());
|
||||||
|
|
||||||
|
headers
|
||||||
|
}
|
||||||
85
src/browser/chrome/ver/v129.rs
Normal file
85
src/browser/chrome/ver/v129.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
use boring::ssl::{
|
||||||
|
CertCompressionAlgorithm, SslConnector, SslConnectorBuilder, SslMethod, SslVersion,
|
||||||
|
SslVerifyMode,
|
||||||
|
};
|
||||||
|
use http::{
|
||||||
|
header::{ACCEPT, ACCEPT_ENCODING, ACCEPT_LANGUAGE, UPGRADE_INSECURE_REQUESTS, USER_AGENT},
|
||||||
|
HeaderMap, HeaderValue,
|
||||||
|
};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::browser::{BrowserSettings, Http2Data};
|
||||||
|
|
||||||
|
pub(super) fn get_settings() -> BrowserSettings {
|
||||||
|
BrowserSettings {
|
||||||
|
tls_builder_func: Arc::new(create_ssl_connector),
|
||||||
|
http2: Http2Data {
|
||||||
|
initial_stream_window_size: 6291456,
|
||||||
|
initial_connection_window_size: 15728640,
|
||||||
|
max_concurrent_streams: 1000,
|
||||||
|
max_header_list_size: 262144,
|
||||||
|
header_table_size: 65536,
|
||||||
|
enable_push: Some(false),
|
||||||
|
},
|
||||||
|
headers: create_headers(),
|
||||||
|
gzip: true,
|
||||||
|
brotli: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_ssl_connector() -> SslConnectorBuilder {
|
||||||
|
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||||
|
|
||||||
|
builder.set_grease_enabled(true);
|
||||||
|
builder.enable_ocsp_stapling();
|
||||||
|
builder.set_verify(SslVerifyMode::NONE);
|
||||||
|
|
||||||
|
let cipher_list = [
|
||||||
|
"TLS_AES_128_GCM_SHA256",
|
||||||
|
"TLS_AES_256_GCM_SHA384",
|
||||||
|
"TLS_CHACHA20_POLY1305_SHA256",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||||
|
"TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||||
|
"TLS_RSA_WITH_AES_256_CBC_SHA",
|
||||||
|
];
|
||||||
|
|
||||||
|
builder.set_cipher_list(&cipher_list.join(":")).unwrap();
|
||||||
|
|
||||||
|
let sigalgs_list = [
|
||||||
|
"ecdsa_secp256r1_sha256",
|
||||||
|
"rsa_pss_rsae_sha256",
|
||||||
|
"rsa_pkcs1_sha256",
|
||||||
|
"ecdsa_secp384r1_sha384",
|
||||||
|
"rsa_pss_rsae_sha384",
|
||||||
|
"rsa_pkcs1_sha384",
|
||||||
|
"rsa_pss_rsae_sha512",
|
||||||
|
"rsa_pkcs1_sha512",
|
||||||
|
];
|
||||||
|
|
||||||
|
builder.set_sigalgs_list(&sigalgs_list.join(":")).unwrap();
|
||||||
|
|
||||||
|
builder.set_alpn_protos(b"\x02h2\x08http/1.1").unwrap();
|
||||||
|
|
||||||
|
builder.add_cert_compression_alg(CertCompressionAlgorithm::Brotli).unwrap();
|
||||||
|
|
||||||
|
builder.enable_signed_cert_timestamps();
|
||||||
|
|
||||||
|
builder.set_min_proto_version(Some(SslVersion::TLS1_2)).unwrap();
|
||||||
|
builder.set_max_proto_version(Some(SslVersion::TLS1_3)).unwrap();
|
||||||
|
|
||||||
|
builder
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_headers() -> HeaderMap {
|
||||||
|
let mut headers = HeaderMap::new();
|
||||||
|
headers
|
||||||
|
}
|
||||||
12
src/tls.rs
12
src/tls.rs
@@ -72,7 +72,11 @@ impl Certificate {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(any(not(feature = "__boring"), feature = "native-tls-crate", feature = "__rustls"))]
|
#[cfg(any(
|
||||||
|
not(feature = "__boring"),
|
||||||
|
feature = "native-tls-crate",
|
||||||
|
feature = "__rustls"
|
||||||
|
))]
|
||||||
pub fn from_der(der: &[u8]) -> crate::Result<Certificate> {
|
pub fn from_der(der: &[u8]) -> crate::Result<Certificate> {
|
||||||
Ok(Certificate {
|
Ok(Certificate {
|
||||||
#[cfg(feature = "native-tls-crate")]
|
#[cfg(feature = "native-tls-crate")]
|
||||||
@@ -98,7 +102,11 @@ impl Certificate {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(any(not(feature = "__boring"), feature = "native-tls-crate", feature = "__rustls"))]
|
#[cfg(any(
|
||||||
|
not(feature = "__boring"),
|
||||||
|
feature = "native-tls-crate",
|
||||||
|
feature = "__rustls"
|
||||||
|
))]
|
||||||
pub fn from_pem(pem: &[u8]) -> crate::Result<Certificate> {
|
pub fn from_pem(pem: &[u8]) -> crate::Result<Certificate> {
|
||||||
Ok(Certificate {
|
Ok(Certificate {
|
||||||
#[cfg(feature = "native-tls-crate")]
|
#[cfg(feature = "native-tls-crate")]
|
||||||
|
|||||||
@@ -4,15 +4,15 @@ use std::io::Write;
|
|||||||
|
|
||||||
use base64::write::EncoderWriter as Base64Encoder;
|
use base64::write::EncoderWriter as Base64Encoder;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use http::{request::Parts, Method, Request as HttpRequest};
|
use http::{ request::Parts, Method, Request as HttpRequest };
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
#[cfg(feature = "json")]
|
#[cfg(feature = "json")]
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use web_sys::RequestCredentials;
|
use web_sys::RequestCredentials;
|
||||||
|
|
||||||
use super::{Body, Client, Response};
|
use super::{ Body, Client, Response };
|
||||||
use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE};
|
use crate::header::{ HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE };
|
||||||
|
|
||||||
/// A request which can be executed with `Client::execute()`.
|
/// A request which can be executed with `Client::execute()`.
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
@@ -174,11 +174,13 @@ impl RequestBuilder {
|
|||||||
Ok(body) => {
|
Ok(body) => {
|
||||||
req.headers_mut().insert(
|
req.headers_mut().insert(
|
||||||
CONTENT_TYPE,
|
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());
|
*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 {
|
if let Some(err) = error {
|
||||||
@@ -195,11 +197,15 @@ impl RequestBuilder {
|
|||||||
if let Ok(ref mut req) = self.request {
|
if let Ok(ref mut req) = self.request {
|
||||||
match serde_json::to_vec(json) {
|
match serde_json::to_vec(json) {
|
||||||
Ok(body) => {
|
Ok(body) => {
|
||||||
req.headers_mut()
|
req.headers_mut().insert(
|
||||||
.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
|
CONTENT_TYPE,
|
||||||
|
HeaderValue::from_static("application/json")
|
||||||
|
);
|
||||||
*req.body_mut() = Some(body.into());
|
*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 {
|
if let Some(err) = error {
|
||||||
@@ -210,14 +216,14 @@ impl RequestBuilder {
|
|||||||
|
|
||||||
/// Enable HTTP basic authentication.
|
/// Enable HTTP basic authentication.
|
||||||
pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
|
pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> RequestBuilder
|
||||||
where
|
where U: fmt::Display, P: fmt::Display
|
||||||
U: fmt::Display,
|
|
||||||
P: fmt::Display,
|
|
||||||
{
|
{
|
||||||
let mut header_value = b"Basic ".to_vec();
|
let mut header_value = b"Basic ".to_vec();
|
||||||
{
|
{
|
||||||
let mut encoder =
|
let mut encoder = Base64Encoder::from(
|
||||||
Base64Encoder::from(&mut header_value, &base64::engine::DEFAULT_ENGINE);
|
&mut header_value,
|
||||||
|
&base64::engine::DEFAULT_ENGINE
|
||||||
|
);
|
||||||
// The unwraps here are fine because Vec::write* is infallible.
|
// The unwraps here are fine because Vec::write* is infallible.
|
||||||
write!(encoder, "{}:", username).unwrap();
|
write!(encoder, "{}:", username).unwrap();
|
||||||
if let Some(password) = password {
|
if let Some(password) = password {
|
||||||
@@ -229,10 +235,7 @@ impl RequestBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Enable HTTP bearer authentication.
|
/// Enable HTTP bearer authentication.
|
||||||
pub fn bearer_auth<T>(self, token: T) -> RequestBuilder
|
pub fn bearer_auth<T>(self, token: T) -> RequestBuilder where T: fmt::Display {
|
||||||
where
|
|
||||||
T: fmt::Display,
|
|
||||||
{
|
|
||||||
let header_value = format!("Bearer {}", token);
|
let header_value = format!("Bearer {}", token);
|
||||||
self.header(crate::header::AUTHORIZATION, header_value)
|
self.header(crate::header::AUTHORIZATION, header_value)
|
||||||
}
|
}
|
||||||
@@ -250,29 +253,34 @@ impl RequestBuilder {
|
|||||||
#[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
|
||||||
pub fn multipart(mut self, multipart: super::multipart::Form) -> RequestBuilder {
|
pub fn multipart(mut self, multipart: super::multipart::Form) -> RequestBuilder {
|
||||||
if let Ok(ref mut req) = self.request {
|
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
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a `Header` to this Request.
|
/// Add a `Header` to this Request.
|
||||||
pub fn header<K, V>(mut self, key: K, value: V) -> RequestBuilder
|
pub fn header<K, V>(mut self, key: K, value: V) -> RequestBuilder
|
||||||
where
|
where
|
||||||
HeaderName: TryFrom<K>,
|
HeaderName: TryFrom<K>,
|
||||||
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
|
||||||
HeaderValue: TryFrom<V>,
|
HeaderValue: TryFrom<V>,
|
||||||
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
|
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>
|
||||||
{
|
{
|
||||||
let mut error = None;
|
let mut error = None;
|
||||||
if let Ok(ref mut req) = self.request {
|
if let Ok(ref mut req) = self.request {
|
||||||
match <HeaderName as TryFrom<K>>::try_from(key) {
|
match <HeaderName as TryFrom<K>>::try_from(key) {
|
||||||
Ok(key) => match <HeaderValue as TryFrom<V>>::try_from(value) {
|
Ok(key) =>
|
||||||
Ok(value) => {
|
match <HeaderValue as TryFrom<V>>::try_from(value) {
|
||||||
req.headers_mut().append(key, 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 {
|
if let Some(err) = error {
|
||||||
@@ -434,27 +442,17 @@ impl fmt::Debug for RequestBuilder {
|
|||||||
|
|
||||||
fn fmt_request_fields<'a, 'b>(
|
fn fmt_request_fields<'a, 'b>(
|
||||||
f: &'a mut fmt::DebugStruct<'a, 'b>,
|
f: &'a mut fmt::DebugStruct<'a, 'b>,
|
||||||
req: &Request,
|
req: &Request
|
||||||
) -> &'a mut fmt::DebugStruct<'a, 'b> {
|
) -> &'a mut fmt::DebugStruct<'a, 'b> {
|
||||||
f.field("method", &req.method)
|
f.field("method", &req.method).field("url", &req.url).field("headers", &req.headers)
|
||||||
.field("url", &req.url)
|
|
||||||
.field("headers", &req.headers)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> TryFrom<HttpRequest<T>> for Request
|
impl<T> TryFrom<HttpRequest<T>> for Request where T: Into<Body> {
|
||||||
where
|
|
||||||
T: Into<Body>,
|
|
||||||
{
|
|
||||||
type Error = crate::Error;
|
type Error = crate::Error;
|
||||||
|
|
||||||
fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
|
fn try_from(req: HttpRequest<T>) -> crate::Result<Self> {
|
||||||
let (parts, body) = req.into_parts();
|
let (parts, body) = req.into_parts();
|
||||||
let Parts {
|
let Parts { method, uri, headers, .. } = parts;
|
||||||
method,
|
|
||||||
uri,
|
|
||||||
headers,
|
|
||||||
..
|
|
||||||
} = parts;
|
|
||||||
let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
|
let url = Url::parse(&uri.to_string()).map_err(crate::error::builder)?;
|
||||||
Ok(Request {
|
Ok(Request {
|
||||||
method,
|
method,
|
||||||
@@ -471,13 +469,7 @@ impl TryFrom<Request> for HttpRequest<Body> {
|
|||||||
type Error = crate::Error;
|
type Error = crate::Error;
|
||||||
|
|
||||||
fn try_from(req: Request) -> crate::Result<Self> {
|
fn try_from(req: Request) -> crate::Result<Self> {
|
||||||
let Request {
|
let Request { method, url, headers, body, .. } = req;
|
||||||
method,
|
|
||||||
url,
|
|
||||||
headers,
|
|
||||||
body,
|
|
||||||
..
|
|
||||||
} = req;
|
|
||||||
|
|
||||||
let mut req = HttpRequest::builder()
|
let mut req = HttpRequest::builder()
|
||||||
.method(method)
|
.method(method)
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ fn test_response_text() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_response_non_utf_8_text() {
|
fn test_response_non_utf_8_text() {
|
||||||
let server = server::http(move |_req| async {
|
let server = server::http(move |_req| async {
|
||||||
http::Response::builder()
|
http::Response
|
||||||
|
::builder()
|
||||||
.header("content-type", "text/plain; charset=gbk")
|
.header("content-type", "text/plain; charset=gbk")
|
||||||
.body(b"\xc4\xe3\xba\xc3"[..].into())
|
.body(b"\xc4\xe3\xba\xc3"[..].into())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -91,11 +92,7 @@ fn test_post() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let url = format!("http://{}/2", server.addr());
|
let url = format!("http://{}/2", server.addr());
|
||||||
let res = reqwest::blocking::Client::new()
|
let res = reqwest::blocking::Client::new().post(&url).body("Hello").send().unwrap();
|
||||||
.post(&url)
|
|
||||||
.body("Hello")
|
|
||||||
.send()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(res.url().as_str(), &url);
|
assert_eq!(res.url().as_str(), &url);
|
||||||
assert_eq!(res.status(), reqwest::StatusCode::OK);
|
assert_eq!(res.status(), reqwest::StatusCode::OK);
|
||||||
@@ -108,7 +105,7 @@ fn test_post_form() {
|
|||||||
assert_eq!(req.headers()["content-length"], "24");
|
assert_eq!(req.headers()["content-length"], "24");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
req.headers()["content-type"],
|
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();
|
let data = hyper::body::to_bytes(req.into_body()).await.unwrap();
|
||||||
@@ -117,14 +114,13 @@ fn test_post_form() {
|
|||||||
http::Response::default()
|
http::Response::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
let form = &[("hello", "world"), ("sean", "monstar")];
|
let form = &[
|
||||||
|
("hello", "world"),
|
||||||
|
("sean", "monstar"),
|
||||||
|
];
|
||||||
|
|
||||||
let url = format!("http://{}/form", server.addr());
|
let url = format!("http://{}/form", server.addr());
|
||||||
let res = reqwest::blocking::Client::new()
|
let res = reqwest::blocking::Client::new().post(&url).form(form).send().expect("request send");
|
||||||
.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);
|
||||||
@@ -135,10 +131,7 @@ fn test_post_form() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_error_for_status_4xx() {
|
fn test_error_for_status_4xx() {
|
||||||
let server = server::http(move |_req| async {
|
let server = server::http(move |_req| async {
|
||||||
http::Response::builder()
|
http::Response::builder().status(400).body(Default::default()).unwrap()
|
||||||
.status(400)
|
|
||||||
.body(Default::default())
|
|
||||||
.unwrap()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let url = format!("http://{}/1", server.addr());
|
let url = format!("http://{}/1", server.addr());
|
||||||
@@ -154,10 +147,7 @@ fn test_error_for_status_4xx() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_error_for_status_5xx() {
|
fn test_error_for_status_5xx() {
|
||||||
let server = server::http(move |_req| async {
|
let server = server::http(move |_req| async {
|
||||||
http::Response::builder()
|
http::Response::builder().status(500).body(Default::default()).unwrap()
|
||||||
.status(500)
|
|
||||||
.body(Default::default())
|
|
||||||
.unwrap()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let url = format!("http://{}/1", server.addr());
|
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();
|
let err = res.error_for_status().unwrap_err();
|
||||||
assert!(err.is_status());
|
assert!(err.is_status());
|
||||||
assert_eq!(
|
assert_eq!(err.status(), Some(reqwest::StatusCode::INTERNAL_SERVER_ERROR));
|
||||||
err.status(),
|
|
||||||
Some(reqwest::StatusCode::INTERNAL_SERVER_ERROR)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -180,10 +167,7 @@ fn test_default_headers() {
|
|||||||
|
|
||||||
let mut headers = http::HeaderMap::with_capacity(1);
|
let mut headers = http::HeaderMap::with_capacity(1);
|
||||||
headers.insert("reqwest-test", "orly".parse().unwrap());
|
headers.insert("reqwest-test", "orly".parse().unwrap());
|
||||||
let client = reqwest::blocking::Client::builder()
|
let client = reqwest::blocking::Client::builder().default_headers(headers).build().unwrap();
|
||||||
.default_headers(headers)
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let url = format!("http://{}/1", server.addr());
|
let url = format!("http://{}/1", server.addr());
|
||||||
let res = client.get(&url).send().unwrap();
|
let res = client.get(&url).send().unwrap();
|
||||||
@@ -205,20 +189,14 @@ fn test_override_default_headers() {
|
|||||||
let mut headers = http::HeaderMap::with_capacity(1);
|
let mut headers = http::HeaderMap::with_capacity(1);
|
||||||
headers.insert(
|
headers.insert(
|
||||||
http::header::AUTHORIZATION,
|
http::header::AUTHORIZATION,
|
||||||
http::header::HeaderValue::from_static("iamatoken"),
|
http::header::HeaderValue::from_static("iamatoken")
|
||||||
);
|
);
|
||||||
let client = reqwest::blocking::Client::builder()
|
let client = reqwest::blocking::Client::builder().default_headers(headers).build().unwrap();
|
||||||
.default_headers(headers)
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let url = format!("http://{}/3", server.addr());
|
let url = format!("http://{}/3", server.addr());
|
||||||
let res = client
|
let res = client
|
||||||
.get(&url)
|
.get(&url)
|
||||||
.header(
|
.header(http::header::AUTHORIZATION, http::header::HeaderValue::from_static("secret"))
|
||||||
http::header::AUTHORIZATION,
|
|
||||||
http::header::HeaderValue::from_static("secret"),
|
|
||||||
)
|
|
||||||
.send()
|
.send()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -253,14 +231,8 @@ fn test_appended_headers_not_overwritten() {
|
|||||||
// make sure this also works with default headers
|
// make sure this also works with default headers
|
||||||
use reqwest::header;
|
use reqwest::header;
|
||||||
let mut headers = header::HeaderMap::with_capacity(1);
|
let mut headers = header::HeaderMap::with_capacity(1);
|
||||||
headers.insert(
|
headers.insert(header::ACCEPT, header::HeaderValue::from_static("text/html"));
|
||||||
header::ACCEPT,
|
let client = reqwest::blocking::Client::builder().default_headers(headers).build().unwrap();
|
||||||
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 url = format!("http://{}/4", server.addr());
|
||||||
let res = client
|
let res = client
|
||||||
@@ -282,9 +254,7 @@ fn test_blocking_inside_a_runtime() {
|
|||||||
|
|
||||||
let url = format!("http://{}/text", server.addr());
|
let url = format!("http://{}/text", server.addr());
|
||||||
|
|
||||||
let rt = tokio::runtime::Builder::new_current_thread()
|
let rt = tokio::runtime::Builder::new_current_thread().build().expect("new rt");
|
||||||
.build()
|
|
||||||
.expect("new rt");
|
|
||||||
|
|
||||||
rt.block_on(async move {
|
rt.block_on(async move {
|
||||||
let _should_panic = reqwest::blocking::get(&url);
|
let _should_panic = reqwest::blocking::get(&url);
|
||||||
@@ -294,7 +264,8 @@ fn test_blocking_inside_a_runtime() {
|
|||||||
#[cfg(feature = "default-tls")]
|
#[cfg(feature = "default-tls")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_allowed_methods_blocking() {
|
fn test_allowed_methods_blocking() {
|
||||||
let resp = reqwest::blocking::Client::builder()
|
let resp = reqwest::blocking::Client
|
||||||
|
::builder()
|
||||||
.https_only(true)
|
.https_only(true)
|
||||||
.build()
|
.build()
|
||||||
.expect("client builder")
|
.expect("client builder")
|
||||||
@@ -303,7 +274,8 @@ fn test_allowed_methods_blocking() {
|
|||||||
|
|
||||||
assert_eq!(resp.is_err(), false);
|
assert_eq!(resp.is_err(), false);
|
||||||
|
|
||||||
let resp = reqwest::blocking::Client::builder()
|
let resp = reqwest::blocking::Client
|
||||||
|
::builder()
|
||||||
.https_only(true)
|
.https_only(true)
|
||||||
.build()
|
.build()
|
||||||
.expect("client builder")
|
.expect("client builder")
|
||||||
@@ -318,7 +290,8 @@ fn test_allowed_methods_blocking() {
|
|||||||
fn test_body_from_bytes() {
|
fn test_body_from_bytes() {
|
||||||
let body = "abc";
|
let body = "abc";
|
||||||
// No external calls are needed. Only the request building is tested.
|
// No external calls are needed. Only the request building is tested.
|
||||||
let request = reqwest::blocking::Client::builder()
|
let request = reqwest::blocking::Client
|
||||||
|
::builder()
|
||||||
.build()
|
.build()
|
||||||
.expect("Could not build the client")
|
.expect("Could not build the client")
|
||||||
.put("https://google.com")
|
.put("https://google.com")
|
||||||
|
|||||||
Reference in New Issue
Block a user