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 6d7f10de22.

* Clone `Call` instead of creating a `String`

This might improve performance.

Co-authored-by: teor <teor@riseup.net>
This commit is contained in:
Janito Vaqueiro Ferreira Filho 2022-04-22 00:09:03 +00:00 committed by GitHub
parent c2430c6f45
commit 903eabdced
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 76 additions and 4 deletions

View File

@ -7,7 +7,7 @@
//! 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)
use jsonrpc_core;
use jsonrpc_core::{Compatibility, MetaIoHandler};
use jsonrpc_http_server::ServerBuilder;
use tokio::task::JoinHandle;
use tower::{buffer::Buffer, Service};
@ -20,10 +20,11 @@ use zebra_node_services::{mempool, BoxError};
use crate::{
config::Config,
methods::{Rpc, RpcImpl},
server::compatibility::FixHttpRequestMiddleware,
server::{compatibility::FixHttpRequestMiddleware, tracing_middleware::TracingMiddleware},
};
pub mod compatibility;
mod tracing_middleware;
/// Zebra RPC Server
#[derive(Clone, Debug)]
@ -63,8 +64,8 @@ impl RpcServer {
RpcImpl::new(app_version, mempool, state, latest_chain_tip, network);
// Create handler compatible with V1 and V2 RPC protocols
let mut io =
jsonrpc_core::IoHandler::with_compatibility(jsonrpc_core::Compatibility::Both);
let mut io: MetaIoHandler<(), _> =
MetaIoHandler::new(Compatibility::Both, TracingMiddleware);
io.extend_with(rpc_impl.to_delegate());
let server = ServerBuilder::new(io)

View File

@ -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
}
}