Example: hyper_http_layer
Example
To run this example:
- Clone the examples repository:
git clone git@github.com:alloy-rs/examples.git
- Run:
cargo run --example hyper_http_layer
//! 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)
}
}
Find the source code on Github here.