|
| 1 | +//! Example of how to create a node with custom middleware that alters a returned error message from |
| 2 | +//! the RPC |
| 3 | +//! |
| 4 | +//! Run with |
| 5 | +//! |
| 6 | +//! ```sh |
| 7 | +//! cargo run -p example-custom-rpc-middleware node --http --dev --dev.block-time 12s --http.api=debug,eth |
| 8 | +//! ``` |
| 9 | +//! |
| 10 | +//! Then make an RPC request that will result in an error |
| 11 | +//! |
| 12 | +//! ```sh |
| 13 | +//! curl -s -X POST http://localhost:8545 \ |
| 14 | +//! -H "Content-Type: application/json" \ |
| 15 | +//! -d '{ |
| 16 | +//! "jsonrpc": "2.0", |
| 17 | +//! "method": "debug_getRawBlock", |
| 18 | +//! "params": ["2"], |
| 19 | +//! "id": 1 |
| 20 | +//! }' | jq |
| 21 | +//! ``` |
| 22 | +
|
| 23 | +use clap::Parser; |
| 24 | +use jsonrpsee::{ |
| 25 | + core::{ |
| 26 | + middleware::{Batch, Notification, RpcServiceT}, |
| 27 | + server::MethodResponse, |
| 28 | + }, |
| 29 | + types::{ErrorObjectOwned, Id, Request}, |
| 30 | +}; |
| 31 | +use reth_ethereum::{ |
| 32 | + cli::{chainspec::EthereumChainSpecParser, interface::Cli}, |
| 33 | + node::{EthereumAddOns, EthereumNode}, |
| 34 | +}; |
| 35 | +use tower::Layer; |
| 36 | + |
| 37 | +fn main() { |
| 38 | + Cli::<EthereumChainSpecParser>::parse() |
| 39 | + .run(|builder, _| async move { |
| 40 | + let handle = builder |
| 41 | + .with_types::<EthereumNode>() |
| 42 | + .with_components(EthereumNode::components()) |
| 43 | + .with_add_ons( |
| 44 | + //create ethereum addons with our custom rpc middleware |
| 45 | + EthereumAddOns::default().with_rpc_middleware(ResponseMutationLayer), |
| 46 | + ) |
| 47 | + .launch_with_debug_capabilities() |
| 48 | + .await?; |
| 49 | + |
| 50 | + handle.wait_for_node_exit().await |
| 51 | + }) |
| 52 | + .unwrap(); |
| 53 | +} |
| 54 | + |
| 55 | +#[derive(Clone)] |
| 56 | +pub struct ResponseMutationLayer; |
| 57 | + |
| 58 | +impl<S> Layer<S> for ResponseMutationLayer { |
| 59 | + type Service = ResponseMutationService<S>; |
| 60 | + |
| 61 | + fn layer(&self, inner: S) -> Self::Service { |
| 62 | + ResponseMutationService { service: inner } |
| 63 | + } |
| 64 | +} |
| 65 | + |
| 66 | +#[derive(Clone)] |
| 67 | +pub struct ResponseMutationService<S> { |
| 68 | + service: S, |
| 69 | +} |
| 70 | + |
| 71 | +impl<S> RpcServiceT for ResponseMutationService<S> |
| 72 | +where |
| 73 | + S: RpcServiceT< |
| 74 | + MethodResponse = jsonrpsee::MethodResponse, |
| 75 | + BatchResponse = jsonrpsee::MethodResponse, |
| 76 | + NotificationResponse = jsonrpsee::MethodResponse, |
| 77 | + > + Send |
| 78 | + + Sync |
| 79 | + + Clone |
| 80 | + + 'static, |
| 81 | +{ |
| 82 | + type MethodResponse = S::MethodResponse; |
| 83 | + type NotificationResponse = S::NotificationResponse; |
| 84 | + type BatchResponse = S::BatchResponse; |
| 85 | + |
| 86 | + fn call<'a>(&self, req: Request<'a>) -> impl Future<Output = Self::MethodResponse> + Send + 'a { |
| 87 | + tracing::info!("processed call {:?}", req); |
| 88 | + let service = self.service.clone(); |
| 89 | + Box::pin(async move { |
| 90 | + let resp = service.call(req).await; |
| 91 | + |
| 92 | + //we can modify the response with our own custom error |
| 93 | + if resp.is_error() { |
| 94 | + let err = ErrorObjectOwned::owned( |
| 95 | + -31404, |
| 96 | + "CustomError", |
| 97 | + Some("Our very own custom error message"), |
| 98 | + ); |
| 99 | + return MethodResponse::error(Id::Number(1), err); |
| 100 | + } |
| 101 | + |
| 102 | + //otherwise just return the original response |
| 103 | + resp |
| 104 | + }) |
| 105 | + } |
| 106 | + |
| 107 | + fn batch<'a>(&self, req: Batch<'a>) -> impl Future<Output = Self::BatchResponse> + Send + 'a { |
| 108 | + self.service.batch(req) |
| 109 | + } |
| 110 | + |
| 111 | + fn notification<'a>( |
| 112 | + &self, |
| 113 | + n: Notification<'a>, |
| 114 | + ) -> impl Future<Output = Self::NotificationResponse> + Send + 'a { |
| 115 | + self.service.notification(n) |
| 116 | + } |
| 117 | +} |
0 commit comments