feat(rpc): Log unrecognized RPC requests (#3860)
* Trace RPC calls by default Enable RPC call tracing by default. This is useful for development purposes, and should probably be removed in the future. * Create a `TracingMiddleware` for the RPC server A middleware that will print error messages if there are unrecognized RPC requests. * Use `TracingMiddleware` in the `Server` Enable logging of unrecognized RPC requests. * Warn user about unrecognized RPCs Instead of reporting it as an error. Co-authored-by: teor <teor@riseup.net> * Revert "Trace RPC calls by default" This reverts commit 6d7f10de2211b6d5ae581618e799069775717a25. * Clone `Call` instead of creating a `String` This might improve performance. Co-authored-by: teor <teor@riseup.net>
This commit is contained in:
parent
c2430c6f45
commit
903eabdced
|
|
@ -7,7 +7,7 @@
|
||||||
//! See the full list of
|
//! See the full list of
|
||||||
//! [Differences between JSON-RPC 1.0 and 2.0.](https://www.simple-is-better.org/rpc/#differences-between-1-0-and-2-0)
|
//! [Differences between JSON-RPC 1.0 and 2.0.](https://www.simple-is-better.org/rpc/#differences-between-1-0-and-2-0)
|
||||||
|
|
||||||
use jsonrpc_core;
|
use jsonrpc_core::{Compatibility, MetaIoHandler};
|
||||||
use jsonrpc_http_server::ServerBuilder;
|
use jsonrpc_http_server::ServerBuilder;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use tower::{buffer::Buffer, Service};
|
use tower::{buffer::Buffer, Service};
|
||||||
|
|
@ -20,10 +20,11 @@ use zebra_node_services::{mempool, BoxError};
|
||||||
use crate::{
|
use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
methods::{Rpc, RpcImpl},
|
methods::{Rpc, RpcImpl},
|
||||||
server::compatibility::FixHttpRequestMiddleware,
|
server::{compatibility::FixHttpRequestMiddleware, tracing_middleware::TracingMiddleware},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod compatibility;
|
pub mod compatibility;
|
||||||
|
mod tracing_middleware;
|
||||||
|
|
||||||
/// Zebra RPC Server
|
/// Zebra RPC Server
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -63,8 +64,8 @@ impl RpcServer {
|
||||||
RpcImpl::new(app_version, mempool, state, latest_chain_tip, network);
|
RpcImpl::new(app_version, mempool, state, latest_chain_tip, network);
|
||||||
|
|
||||||
// Create handler compatible with V1 and V2 RPC protocols
|
// Create handler compatible with V1 and V2 RPC protocols
|
||||||
let mut io =
|
let mut io: MetaIoHandler<(), _> =
|
||||||
jsonrpc_core::IoHandler::with_compatibility(jsonrpc_core::Compatibility::Both);
|
MetaIoHandler::new(Compatibility::Both, TracingMiddleware);
|
||||||
io.extend_with(rpc_impl.to_delegate());
|
io.extend_with(rpc_impl.to_delegate());
|
||||||
|
|
||||||
let server = ServerBuilder::new(io)
|
let server = ServerBuilder::new(io)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
//! A custom middleware to trace unrecognized RPC requests.
|
||||||
|
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
|
use futures::future::{Either, FutureExt};
|
||||||
|
use jsonrpc_core::{
|
||||||
|
middleware::Middleware,
|
||||||
|
types::{Call, Failure, Output, Response},
|
||||||
|
BoxFuture, Error, ErrorCode, Metadata, MethodCall, Notification,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A custom RPC middleware that logs unrecognized RPC requests.
|
||||||
|
pub struct TracingMiddleware;
|
||||||
|
|
||||||
|
impl<M: Metadata> Middleware<M> for TracingMiddleware {
|
||||||
|
type Future = BoxFuture<Option<Response>>;
|
||||||
|
type CallFuture = BoxFuture<Option<Output>>;
|
||||||
|
|
||||||
|
fn on_call<Next, NextFuture>(
|
||||||
|
&self,
|
||||||
|
call: Call,
|
||||||
|
meta: M,
|
||||||
|
next: Next,
|
||||||
|
) -> Either<Self::CallFuture, NextFuture>
|
||||||
|
where
|
||||||
|
Next: Fn(Call, M) -> NextFuture + Send + Sync,
|
||||||
|
NextFuture: Future<Output = Option<Output>> + Send + 'static,
|
||||||
|
{
|
||||||
|
Either::Left(
|
||||||
|
next(call.clone(), meta)
|
||||||
|
.then(move |output| Self::log_error_if_method_not_found(output, call))
|
||||||
|
.boxed(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TracingMiddleware {
|
||||||
|
/// Obtain a description string for a received request.
|
||||||
|
///
|
||||||
|
/// Prints out only the method name and the received parameters.
|
||||||
|
fn call_description(call: &Call) -> String {
|
||||||
|
match call {
|
||||||
|
Call::MethodCall(MethodCall { method, params, .. }) => {
|
||||||
|
format!(r#"method = {method:?}, params = {params:?}"#)
|
||||||
|
}
|
||||||
|
Call::Notification(Notification { method, params, .. }) => {
|
||||||
|
format!(r#"notification = {method:?}, params = {params:?}"#)
|
||||||
|
}
|
||||||
|
Call::Invalid { .. } => "invalid request".to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check an RPC output and log an error if it indicates the method was not found.
|
||||||
|
async fn log_error_if_method_not_found(output: Option<Output>, call: Call) -> Option<Output> {
|
||||||
|
let call_description = Self::call_description(&call);
|
||||||
|
|
||||||
|
if let Some(Output::Failure(Failure {
|
||||||
|
error:
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::MethodNotFound,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
})) = output
|
||||||
|
{
|
||||||
|
tracing::warn!("Received unrecognized RPC request: {call_description}");
|
||||||
|
}
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue