Skip to content

Commit

Permalink
Merge pull request #160 from alloy-rs/yash/hyper-layer
Browse files Browse the repository at this point in the history
feat(`layers`): hyper layer
  • Loading branch information
mattsse authored Nov 18, 2024
2 parents bc7aa07 + f3ac4a5 commit bbcada7
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ alloy = { version = "0.6.2", features = [
"signer-trezor",
"signer-yubihsm",
"eips",
"hyper",
] }

foundry-fork-db = "0.7"
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ This repository contains the following examples:
- [x] [Recommended fillers](./examples/fillers/examples/recommended_fillers.rs)
- [x] [Wallet management filler](./examples/fillers/examples/wallet_filler.rs)
- [x] Layers
- [x] [Hyper layer transport](./examples/layers/examples/hyper_http_layer.rs)
- [x] [Request / response logging layer](./examples/layers/examples/logging_layer.rs)
- [x] [Retry-backoff layer](./examples/layers/examples/retry_layer.rs)
- [x] Node Bindings
Expand Down
1 change: 1 addition & 0 deletions examples/layers/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ alloy.workspace = true
eyre.workspace = true
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
tower = { version = "0.5", features = ["retry"] }
http-body-util = "0.1"
101 changes: 101 additions & 0 deletions examples/layers/examples/hyper_http_layer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//! This example demonstrates how to write a custom layer for the `hyper` HTTP client that can
//! modify the underlying http request before it is sent.
use alloy::{
node_bindings::Anvil,
providers::{Provider, ProviderBuilder},
rpc::client::RpcClient,
transports::http::{
hyper,
hyper_util::{
client::legacy::{Client, Error},
rt::TokioExecutor,
},
Http, HyperClient, HyperResponse, HyperResponseFut,
},
};
use eyre::Result;
use http_body_util::Full;
use tower::{Layer, Service};

#[tokio::main]
async fn main() -> Result<()> {
// Start an Anvil node.
let anvil = Anvil::new().spawn();

// Create a new Hyper client.
let hyper_client =
Client::builder(TokioExecutor::new()).build_http::<Full<hyper::body::Bytes>>();

// Use tower::ServiceBuilder to stack layers on top of the Hyper client.
let service = tower::ServiceBuilder::new().layer(RequestModifyingLayer).service(hyper_client);

// Instantiate the HyperClient with the stacked layers.
let layer_transport = HyperClient::<Full<hyper::body::Bytes>, _>::with_service(service);
let http = Http::with_client(layer_transport, anvil.endpoint_url());

// Create a new RPC client with the Hyper transport.
let rpc_client = RpcClient::new(http, true);

let provider = ProviderBuilder::new().on_client(rpc_client);

let num = provider.get_block_number().await.unwrap();

assert_eq!(num, 0);

Ok(())
}

// Layer that will be stacked on top of the Hyper client.
struct RequestModifyingLayer;

// Implement the `Layer` trait for the custom layer.
impl<S> Layer<S> for RequestModifyingLayer {
type Service = RequestModifyingService<S>;

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

// Service that will modify the request before it is sent.
#[derive(Clone)] // Service must be Cloneable.
struct RequestModifyingService<S> {
inner: S,
}

impl<S, B> Service<hyper::Request<B>> for RequestModifyingService<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 Error = Error;
type Future = HyperResponseFut;
type Response = HyperResponse;

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, mut req: hyper::Request<B>) -> Self::Future {
// Modify the request here.

// Example: Add a custom header to the request.
let header = req.headers_mut();
header.insert("x-alloy", "hyper".parse().unwrap());

println!("Request: {:?}", req);

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

Box::pin(fut)
}
}

0 comments on commit bbcada7

Please sign in to comment.