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