Skip to content

Commit

Permalink
feat(transport-http): layer client (#1227)
Browse files Browse the repository at this point in the history
* feat(transport): bare minimal reqwest-tower integration

* feat(transport-http): reqwest-tower layer client

* feat(transport-http): LayerClient

* fix: feature gate layer transport

* rm logging layer

* feat(transport-http): hyper layer transport

* hyper layer transport test

* test with tower-http layers

* rm reqwest layer transport

* rm trait bounds for new

* nit

* unify hyper transports

* rm TransportConnect for HyperLayerTransport

* make request generic

* unify hyper transports

* nit

* nit

* nit

* rm unintended reqwest default

* rename HyperLayerTransport to HyperTransport

* rename file

* nit

* fix: rm transport from HttpConnect

* fix: rm url from HyperTransport, infer it from Http

* clippy

* fix

* impl Http<Hyper>

* fix

* rename

* nit
  • Loading branch information
yash-atreya authored Sep 18, 2024
1 parent 347626b commit 68115f5
Show file tree
Hide file tree
Showing 10 changed files with 247 additions and 70 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ thiserror = "1.0"
thiserror-no-std = "2.0.2"
url = "2.5"
derive_more = { version = "1.0.0", default-features = false }
http = "1.1.0"

## serde
serde = { version = "1.0", default-features = false, features = [
Expand All @@ -153,3 +154,4 @@ assert_matches = "1.5"
serial_test = "3.0"
similar-asserts = "1.5"
tempfile = "3.10"
tower-http = "0.5.2"
9 changes: 8 additions & 1 deletion crates/alloy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,14 @@ network = ["dep:alloy-network"]
node-bindings = ["dep:alloy-node-bindings", "alloy-provider?/anvil-node"]

# providers
providers = ["dep:alloy-provider", "rpc-client", "transports", "eips", "consensus", "network"]
providers = [
"dep:alloy-provider",
"rpc-client",
"transports",
"eips",
"consensus",
"network",
]
provider-http = ["providers", "transport-http"]
provider-ws = ["providers", "alloy-provider?/ws", "transport-ws"]
provider-ipc = ["providers", "alloy-provider?/ipc", "transport-ipc"]
Expand Down
11 changes: 10 additions & 1 deletion crates/provider/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ alloy-rpc-types-anvil = { workspace = true, optional = true }
alloy-rpc-types-eth = { workspace = true, features = ["serde"] }
alloy-rpc-types-trace = { workspace = true, optional = true }
alloy-rpc-types-txpool = { workspace = true, optional = true }
alloy-rpc-types-engine = { workspace = true, optional = true, features = ["serde"] }
alloy-rpc-types-engine = { workspace = true, optional = true, features = [
"serde",
] }
alloy-rpc-types = { workspace = true, optional = true }
alloy-transport-http = { workspace = true, optional = true }
alloy-transport-ipc = { workspace = true, optional = true }
Expand Down Expand Up @@ -73,6 +75,13 @@ reqwest.workspace = true
tokio = { workspace = true, features = ["macros"] }
tracing-subscriber = { workspace = true, features = ["fmt"] }
tempfile.workspace = true
tower.workspace = true
tower-http = { workspace = true, features = [
"set-header",
"sensitive-headers",
] }
http-body-util.workspace = true
http.workspace = true

[features]
default = ["reqwest", "reqwest-default-tls"]
Expand Down
13 changes: 2 additions & 11 deletions crates/provider/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,17 +354,8 @@ impl<L, F, N> ProviderBuilder<L, F, N> {
#[cfg(feature = "hyper")]
pub fn on_hyper_http(self, url: url::Url) -> F::Provider
where
L: ProviderLayer<
crate::HyperProvider<N>,
alloy_transport_http::Http<alloy_transport_http::HyperClient>,
N,
>,
F: TxFiller<N>
+ ProviderLayer<
L::Provider,
alloy_transport_http::Http<alloy_transport_http::HyperClient>,
N,
>,
L: ProviderLayer<crate::HyperProvider<N>, alloy_transport_http::HyperTransport, N>,
F: TxFiller<N> + ProviderLayer<L::Provider, alloy_transport_http::HyperTransport, N>,
N: Network,
{
let client = ClientBuilder::default().hyper_http(url);
Expand Down
2 changes: 1 addition & 1 deletion crates/provider/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub type ReqwestProvider<N = alloy_network::Ethereum> =
/// [`Http`]: alloy_transport_http::Http
#[cfg(feature = "hyper")]
pub type HyperProvider<N = alloy_network::Ethereum> =
crate::RootProvider<alloy_transport_http::Http<alloy_transport_http::HyperClient>, N>;
crate::RootProvider<alloy_transport_http::HyperTransport, N>;

#[macro_use]
extern crate tracing;
Expand Down
124 changes: 124 additions & 0 deletions crates/provider/src/provider/trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,21 @@ mod tests {
use alloy_node_bindings::Anvil;
use alloy_primitives::{address, b256, bytes, keccak256};
use alloy_rpc_types_eth::{request::TransactionRequest, Block};
// For layer transport tests
#[cfg(feature = "hyper")]
use alloy_transport_http::{
hyper,
hyper::body::Bytes as HyperBytes,
hyper_util::{
client::legacy::{Client, Error},
rt::TokioExecutor,
},
HyperResponse, HyperResponseFut,
};
#[cfg(feature = "hyper")]
use http_body_util::Full;
#[cfg(feature = "hyper")]
use tower::{Layer, Service};

fn init_tracing() {
let _ = tracing_subscriber::fmt::try_init();
Expand All @@ -1035,6 +1050,115 @@ mod tests {
assert_eq!(0, num);
}

#[cfg(feature = "hyper")]
#[tokio::test]
async fn test_default_hyper_transport() {
init_tracing();
let anvil = Anvil::new().spawn();
let hyper_t = alloy_transport_http::HyperTransport::new_hyper(anvil.endpoint_url());

let rpc_client = alloy_rpc_client::RpcClient::new(hyper_t, true);

let provider = RootProvider::<_, Ethereum>::new(rpc_client);
let num = provider.get_block_number().await.unwrap();
assert_eq!(0, num);
}

#[cfg(feature = "hyper")]
#[tokio::test]
async fn test_hyper_layer_transport() {
struct LoggingLayer;

impl<S> Layer<S> for LoggingLayer {
type Service = LoggingService<S>;

fn layer(&self, inner: S) -> Self::Service {
LoggingService { inner }
}
}

#[derive(Clone)] // required
struct LoggingService<S> {
inner: S,
}

impl<S, B> Service<hyper::Request<B>> for LoggingService<S>
where
S: Service<hyper::Request<B>, Response = HyperResponse, Error = Error>
+ Clone
+ Send
+ Sync
+ 'static,
S::Future: Send,
S::Error: std::error::Error + Send + Sync + 'static,
B: From<Vec<u8>> + Send + 'static + Clone + Sync + std::fmt::Debug,
{
type Response = HyperResponse;
type Error = Error;
type Future = HyperResponseFut;

fn poll_ready(
&mut self,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}

fn call(&mut self, req: hyper::Request<B>) -> Self::Future {
println!("Logging Layer - HyperRequest {req:?}");

let fut = self.inner.call(req);

Box::pin(fut)
}
}
use http::header::{self, HeaderValue};
use tower_http::{
sensitive_headers::SetSensitiveRequestHeadersLayer, set_header::SetRequestHeaderLayer,
};
init_tracing();
let anvil = Anvil::new().spawn();
let hyper_client = Client::builder(TokioExecutor::new()).build_http::<Full<HyperBytes>>();

// Setup tower serive with multiple layers modifying request headers
let service = tower::ServiceBuilder::new()
.layer(SetRequestHeaderLayer::if_not_present(
header::USER_AGENT,
HeaderValue::from_static("alloy app"),
))
.layer(SetRequestHeaderLayer::overriding(
header::AUTHORIZATION,
HeaderValue::from_static("some-jwt-token"),
))
.layer(SetRequestHeaderLayer::appending(
header::SET_COOKIE,
HeaderValue::from_static("cookie-value"),
))
.layer(SetSensitiveRequestHeadersLayer::new([header::AUTHORIZATION])) // Hides the jwt token as sensitive.
.layer(LoggingLayer)
.service(hyper_client);

let layer_transport = alloy_transport_http::HyperClient::with_service(service);

let http_hyper =
alloy_transport_http::Http::with_client(layer_transport, anvil.endpoint_url());

let rpc_client = alloy_rpc_client::RpcClient::new(http_hyper, true);

let provider = RootProvider::<_, Ethereum>::new(rpc_client);
let num = provider.get_block_number().await.unwrap();
assert_eq!(0, num);

// Test Cloning with service
let cloned_t = provider.client().transport().clone();

let rpc_client = alloy_rpc_client::RpcClient::new(cloned_t, true);

let provider = RootProvider::<_, Ethereum>::new(rpc_client);
let num = provider.get_block_number().await.unwrap();
assert_eq!(0, num);
}

#[tokio::test]
async fn test_builder_helper_fn_any_network() {
init_tracing();
Expand Down
4 changes: 1 addition & 3 deletions crates/rpc-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ alloy-transport-ws = { workspace = true, optional = true }

reqwest = { workspace = true, optional = true }

hyper-util = { workspace = true, optional = true }

url = { workspace = true, optional = true }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
Expand All @@ -57,7 +55,7 @@ futures-util.workspace = true
[features]
default = ["reqwest"]
reqwest = ["dep:url", "dep:reqwest", "alloy-transport-http/reqwest"]
hyper = ["dep:url", "dep:hyper-util", "alloy-transport-http/hyper"]
hyper = ["dep:url", "alloy-transport-http/hyper"]
pubsub = ["dep:alloy-pubsub", "dep:alloy-primitives"]
ws = ["pubsub", "dep:alloy-transport-ws", "dep:url"]
ipc = ["pubsub", "dep:alloy-transport-ipc"]
6 changes: 2 additions & 4 deletions crates/rpc-client/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,10 @@ impl<L> ClientBuilder<L> {
#[cfg(all(not(target_arch = "wasm32"), feature = "hyper"))]
pub fn hyper_http(self, url: url::Url) -> RpcClient<L::Service>
where
L: Layer<alloy_transport_http::Http<alloy_transport_http::HyperClient>>,
L: Layer<alloy_transport_http::HyperTransport>,
L::Service: Transport,
{
let executor = hyper_util::rt::TokioExecutor::new();
let client = hyper_util::client::legacy::Client::builder(executor).build_http();
let transport = alloy_transport_http::Http::with_client(client, url);
let transport = alloy_transport_http::HyperTransport::new_hyper(url);
let is_local = transport.guess_local();

self.transport(transport, is_local)
Expand Down
Loading

0 comments on commit 68115f5

Please sign in to comment.