Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(transport-http): layer client #1227

Merged
merged 33 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
71a14b5
feat(transport): bare minimal reqwest-tower integration
yash-atreya Aug 30, 2024
7eea874
feat(transport-http): reqwest-tower layer client
yash-atreya Sep 2, 2024
98110e6
feat(transport-http): LayerClient
yash-atreya Sep 2, 2024
f4dbb7e
fix: feature gate layer transport
yash-atreya Sep 3, 2024
0d77863
rm logging layer
yash-atreya Sep 3, 2024
386d8cd
feat(transport-http): hyper layer transport
yash-atreya Sep 4, 2024
849a7ff
hyper layer transport test
yash-atreya Sep 9, 2024
f44eacd
test with tower-http layers
yash-atreya Sep 9, 2024
69641a6
rm reqwest layer transport
yash-atreya Sep 9, 2024
1c437de
Merge branch 'main' into yash/reqwest-tower-integration
yash-atreya Sep 10, 2024
1821332
rm trait bounds for new
yash-atreya Sep 11, 2024
5b42d91
nit
yash-atreya Sep 11, 2024
5f65b93
unify hyper transports
yash-atreya Sep 16, 2024
814bd60
rm TransportConnect for HyperLayerTransport
yash-atreya Sep 16, 2024
d9e14c0
make request generic
yash-atreya Sep 16, 2024
0b201f2
unify hyper transports
yash-atreya Sep 16, 2024
3c3d593
Merge branch 'main' into yash/reqwest-tower-integration
yash-atreya Sep 16, 2024
8e65d0f
nit
yash-atreya Sep 16, 2024
3310d05
nit
yash-atreya Sep 16, 2024
1f0c99b
nit
yash-atreya Sep 16, 2024
1971f60
rm unintended reqwest default
yash-atreya Sep 16, 2024
a029906
rename HyperLayerTransport to HyperTransport
yash-atreya Sep 16, 2024
d18e0bd
rename file
yash-atreya Sep 16, 2024
4d12209
Merge branch 'main' into yash/reqwest-tower-integration
yash-atreya Sep 17, 2024
c2f1432
nit
yash-atreya Sep 17, 2024
9f880db
fix: rm transport from HttpConnect
yash-atreya Sep 18, 2024
939ac7e
fix: rm url from HyperTransport, infer it from Http
yash-atreya Sep 18, 2024
563ecbb
clippy
yash-atreya Sep 18, 2024
cdfcd10
fix
yash-atreya Sep 18, 2024
edc580e
impl Http<Hyper>
yash-atreya Sep 18, 2024
8a38603
fix
yash-atreya Sep 18, 2024
9af4cae
rename
yash-atreya Sep 18, 2024
e16447f
nit
yash-atreya Sep 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
4 changes: 2 additions & 2 deletions crates/provider/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,13 +356,13 @@ impl<L, F, N> ProviderBuilder<L, F, N> {
where
L: ProviderLayer<
crate::HyperProvider<N>,
alloy_transport_http::Http<alloy_transport_http::HyperClient>,
alloy_transport_http::Http<alloy_transport_http::HyperTransport>,
N,
>,
F: TxFiller<N>
+ ProviderLayer<
L::Provider,
alloy_transport_http::Http<alloy_transport_http::HyperClient>,
alloy_transport_http::Http<alloy_transport_http::HyperTransport>,
N,
>,
N: Network,
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::Http<alloy_transport_http::HyperTransport>, N>;

#[macro_use]
extern crate tracing;
Expand Down
155 changes: 155 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,146 @@ mod tests {
assert_eq!(0, num);
}

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

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);
}

#[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::HyperTransport::with_service(anvil.endpoint_url(), 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
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);
}

#[cfg(feature = "hyper")]
#[tokio::test]
async fn test_layer_transport_with_tower_http() {
use http::header::{self, HeaderValue};
use tower_http::set_header::SetRequestHeaderLayer;
init_tracing();
let anvil = Anvil::new().spawn();
let hyper_client = Client::builder(TokioExecutor::new()).build_http::<Full<HyperBytes>>();
let service = tower::ServiceBuilder::new()
.layer(SetRequestHeaderLayer::if_not_present(
header::USER_AGENT,
HeaderValue::from_static("alloy app"),
))
.service(hyper_client);
let layer_transport =
alloy_transport_http::HyperTransport::with_service(anvil.endpoint_url(), 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);
}

#[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"]
7 changes: 3 additions & 4 deletions crates/rpc-client/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,11 @@ 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::Http<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 hyper = alloy_transport_http::HyperTransport::new(url.clone());
let transport = alloy_transport_http::Http::with_client(hyper, url);
let is_local = transport.guess_local();

self.transport(transport, is_local)
Expand Down
Loading