Skip to content

Commit

Permalink
switch to reqwest (#491)
Browse files Browse the repository at this point in the history
* switch to reqwest

* rust fmt

* fix build for wasm

* bump version
  • Loading branch information
clouds56 committed May 17, 2021
1 parent 509b3bb commit b5bcaa2
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 121 deletions.
16 changes: 14 additions & 2 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ jobs:
command: check
toolchain: stable
args: --target wasm32-unknown-unknown --no-default-features --features eip-1193
- name: Checking wasm32 with http
uses: actions-rs/cargo@master
with:
command: check
toolchain: stable
args: --target wasm32-unknown-unknown --no-default-features --features http,wasm
- name: Testing wasm32
uses: actions-rs/cargo@master
with:
Expand Down Expand Up @@ -122,12 +128,18 @@ jobs:
command: check
toolchain: stable
args: --no-default-features --features http-tls
- name: Checking http-rustls
- name: Checking http-native-tls
uses: actions-rs/cargo@master
with:
command: check
toolchain: stable
args: --no-default-features --features http-native-tls
- name: Checking http-rustls-tls
uses: actions-rs/cargo@master
with:
command: check
toolchain: stable
args: --no-default-features --features http-rustls
args: --no-default-features --features http-rustls-tls
- name: Checking ws-tokio
uses: actions-rs/cargo@master
with:
Expand Down
20 changes: 10 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "web3"
version = "0.15.0"
version = "0.16.0"
description = "Ethereum JSON-RPC client."
homepage = "https://github.com/tomusdrw/rust-web3"
repository = "https://github.com/tomusdrw/rust-web3"
Expand Down Expand Up @@ -31,10 +31,8 @@ pin-project = "1.0"
# Optional deps
## HTTP
base64 = { version = "0.13", optional = true }
hyper = { version = "0.14", optional = true, default-features = false, features = ["client", "http1", "stream", "tcp"] }
hyper-tls = { version = "0.5", optional = true }
hyper-proxy = {version = "0.9.0", default-features = false, optional = true }
hyper-rustls = { version = "0.22.1", default-features = false, features = ["webpki-tokio"], optional = true }
bytes = { version = "1.0", optional = true }
reqwest = { version = "0.11", optional = true, default-features = false }
headers = { version = "0.3", optional = true }
## WS
async-native-tls = { git = "https://github.com/async-email/async-native-tls.git", rev = "b5b5562d6cea77f913d4cbe448058c031833bf17", optional = true, default-features = false }
Expand Down Expand Up @@ -66,11 +64,13 @@ tokio-stream = { version = "0.1", features = ["net"] }

[features]
default = ["http-tls", "signing", "ws-tls-tokio", "ipc-tokio"]
eip-1193 = ["js-sys", "wasm-bindgen", "wasm-bindgen-futures", "futures-timer/wasm-bindgen", "rand", "getrandom"]
_http_base = ["hyper", "url", "base64", "headers"]
http = ["_http_base", "hyper-proxy/tls"]
http-tls = ["hyper-tls", "http"]
http-rustls = ["hyper-rustls", "_http_base", "hyper-proxy/rustls-webpki"]
wasm = ["js-sys", "wasm-bindgen", "wasm-bindgen-futures", "futures-timer/wasm-bindgen", "rand", "getrandom"]
eip-1193 = ["wasm"]
_http_base = ["reqwest", "bytes", "url", "base64", "headers"]
http = ["_http_base"]
http-tls = ["http", "reqwest/default-tls"]
http-native-tls = ["http", "reqwest/native-tls"]
http-rustls-tls = ["http", "reqwest/rustls-tls"]
signing = ["secp256k1"]
ws-tokio = ["soketto", "url", "tokio", "tokio-util"]
ws-async-std = ["soketto", "url", "async-std"]
Expand Down
172 changes: 63 additions & 109 deletions src/transports/http.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
//! HTTP Transport

use crate::{error, helpers, rpc, BatchTransport, Error, RequestId, Transport};
#[cfg(not(feature = "wasm"))]
use futures::future::BoxFuture;
#[cfg(feature = "wasm")]
use futures::future::LocalBoxFuture as BoxFuture;
use futures::{
self,
task::{Context, Poll},
Future, FutureExt, StreamExt,
Future, FutureExt,
};
use hyper::header::HeaderValue;
use reqwest::header::HeaderValue;
use std::{
env, fmt,
fmt,
ops::Deref,
pin::Pin,
sync::{
Expand All @@ -18,105 +22,59 @@ use std::{
};
use url::Url;

impl From<hyper::Error> for Error {
fn from(err: hyper::Error) -> Self {
impl From<reqwest::Error> for Error {
fn from(err: reqwest::Error) -> Self {
Error::Transport(format!("{:?}", err))
}
}

impl From<hyper::http::uri::InvalidUri> for Error {
fn from(err: hyper::http::uri::InvalidUri) -> Self {
Error::Transport(format!("{:?}", err))
}
}

impl From<hyper::header::InvalidHeaderValue> for Error {
fn from(err: hyper::header::InvalidHeaderValue) -> Self {
impl From<reqwest::header::InvalidHeaderValue> for Error {
fn from(err: reqwest::header::InvalidHeaderValue) -> Self {
Error::Transport(format!("{}", err))
}
}

// The max string length of a request without transfer-encoding: chunked.
const MAX_SINGLE_CHUNK: usize = 256;

#[cfg(feature = "http-tls")]
#[derive(Debug, Clone)]
enum Client {
Proxy(hyper::Client<hyper_proxy::ProxyConnector<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>>),
NoProxy(hyper::Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>),
}

#[cfg(feature = "http-rustls")]
#[derive(Debug, Clone)]
enum Client {
Proxy(hyper::Client<hyper_proxy::ProxyConnector<hyper_rustls::HttpsConnector<hyper::client::HttpConnector>>>),
NoProxy(hyper::Client<hyper_rustls::HttpsConnector<hyper::client::HttpConnector>>),
}

#[cfg(not(any(feature = "http-tls", feature = "http-rustls")))]
#[derive(Debug, Clone)]
enum Client {
Proxy(hyper::Client<hyper_proxy::ProxyConnector<hyper::client::HttpConnector>>),
NoProxy(hyper::Client<hyper::client::HttpConnector>),
}

impl Client {
pub fn request(&self, req: hyper::Request<hyper::Body>) -> hyper::client::ResponseFuture {
match self {
Client::Proxy(client) => client.request(req),
Client::NoProxy(client) => client.request(req),
}
}
}

/// HTTP Transport (synchronous)
#[derive(Debug, Clone)]
pub struct Http {
id: Arc<AtomicUsize>,
url: hyper::Uri,
url: reqwest::Url,
basic_auth: Option<HeaderValue>,
client: Client,
client: reqwest::Client,
}

impl Http {
/// Create new HTTP transport connecting to given URL.
pub fn new(url: &str) -> error::Result<Self> {
#[cfg(feature = "http-tls")]
let (proxy_env, connector) = { (env::var("HTTPS_PROXY"), hyper_tls::HttpsConnector::new()) };
#[cfg(feature = "http-rustls")]
let (proxy_env, connector) = {
(
env::var("HTTPS_PROXY"),
hyper_rustls::HttpsConnector::with_webpki_roots(),
)
};
#[cfg(not(any(feature = "http-tls", feature = "http-rustls")))]
let (proxy_env, connector) = { (env::var("HTTP_PROXY"), hyper::client::HttpConnector::new()) };
#[allow(unused_mut)]
let mut client_builder = reqwest::Client::builder();

let client = match proxy_env {
Ok(proxy) => {
let mut url = url::Url::parse(&proxy)?;
let username = String::from(url.username());
let password = String::from(url.password().unwrap_or_default());

url.set_username("").map_err(|_| Error::Internal)?;
url.set_password(None).map_err(|_| Error::Internal)?;

let uri = url.to_string().parse()?;
#[cfg(feature = "http-native-tls")]
{
client_builder = client_builder.use_native_tls();
}

let mut proxy = hyper_proxy::Proxy::new(hyper_proxy::Intercept::All, uri);
#[cfg(feature = "http-rustls-tls")]
{
client_builder = client_builder.use_rustls_tls();
}

if username != "" {
let credentials = headers::Authorization::basic(&username, &password);
proxy.set_authorization(credentials);
#[cfg(not(feature = "wasm"))]
{
let proxy_env = std::env::var("HTTPS_PROXY");
client_builder = match proxy_env {
Ok(proxy_scheme) => {
let proxy = reqwest::Proxy::all(proxy_scheme.as_str())?;
client_builder.proxy(proxy)
}
Err(_) => client_builder.no_proxy(),
};
}

let proxy_connector = hyper_proxy::ProxyConnector::from_proxy(connector, proxy)?;

Client::Proxy(hyper::Client::builder().build(proxy_connector))
}
Err(_) => Client::NoProxy(hyper::Client::builder().build(connector)),
};
let client = client_builder.build()?;

let basic_auth = {
let url = Url::parse(url)?;
Expand Down Expand Up @@ -144,29 +102,29 @@ impl Http {
let request = helpers::to_string(&request);
log::debug!("[{}] Sending: {} to {}", id, request, self.url);
let len = request.len();
let mut req = hyper::Request::new(hyper::Body::from(request));
*req.method_mut() = hyper::Method::POST;
*req.uri_mut() = self.url.clone();
req.headers_mut().insert(
hyper::header::CONTENT_TYPE,
HeaderValue::from_static("application/json"),
);
req.headers_mut()
.insert(hyper::header::USER_AGENT, HeaderValue::from_static("web3.rs"));

let mut request_builder = self
.client
.post(self.url.clone())
.header(
reqwest::header::CONTENT_TYPE,
HeaderValue::from_static("application/json"),
)
.header(reqwest::header::USER_AGENT, HeaderValue::from_static("web3.rs"))
.body(request);

// Don't send chunked request
if len < MAX_SINGLE_CHUNK {
req.headers_mut().insert(hyper::header::CONTENT_LENGTH, len.into());
request_builder = request_builder.header(reqwest::header::CONTENT_LENGTH, len.to_string());
}

// Send basic auth header
if let Some(ref basic_auth) = self.basic_auth {
req.headers_mut()
.insert(hyper::header::AUTHORIZATION, basic_auth.clone());
request_builder = request_builder.header(reqwest::header::AUTHORIZATION, basic_auth.clone());
}
let result = self.client.request(req);

Response::new(id, result, extract)
let result = request_builder.send();
Response::new(id, Box::pin(result), extract)
}
}

Expand Down Expand Up @@ -220,9 +178,11 @@ fn batch_response<T: Deref<Target = [u8]>>(response: T) -> error::Result<Vec<err
}
}

type ResponseFuture = BoxFuture<'static, reqwest::Result<reqwest::Response>>;
type BodyFuture = BoxFuture<'static, reqwest::Result<bytes::Bytes>>;
enum ResponseState {
Waiting(hyper::client::ResponseFuture),
Reading(Vec<u8>, hyper::Body),
Waiting(ResponseFuture),
Reading(BodyFuture),
}

/// A future representing a response to a pending request.
Expand All @@ -234,7 +194,7 @@ pub struct Response<T> {

impl<T> Response<T> {
/// Creates a new `Response`
pub fn new(id: RequestId, response: hyper::client::ResponseFuture, extract: T) -> Self {
pub fn new(id: RequestId, response: ResponseFuture, extract: T) -> Self {
log::trace!("[{}] Request pending.", id);
Response {
id,
Expand Down Expand Up @@ -267,24 +227,18 @@ where
response.status()
))));
}
self.state = ResponseState::Reading(Default::default(), response.into_body());
self.state = ResponseState::Reading(Box::pin(response.bytes()));
}
ResponseState::Reading(ref mut content, ref mut body) => {
ResponseState::Reading(ref mut body) => {
log::trace!("[{}] Reading body.", id);
match ready!(body.poll_next_unpin(ctx)) {
Some(chunk) => {
content.extend(&*chunk?);
}
None => {
let response = std::mem::take(content);
log::trace!(
"[{}] Extracting result from:\n{}",
self.id,
std::str::from_utf8(&response).unwrap_or("<invalid utf8>")
);
return Poll::Ready((self.extract)(response));
}
}
let chunk = ready!(body.poll_unpin(ctx))?;
let response = chunk.to_vec();
log::trace!(
"[{}] Extracting result from:\n{}",
self.id,
std::str::from_utf8(&response).unwrap_or("<invalid utf8>")
);
return Poll::Ready((self.extract)(response));
}
}
}
Expand Down

0 comments on commit b5bcaa2

Please sign in to comment.