chore(rpc): improve rpc server launch error diagnostic (#1979)

This commit is contained in:
jawilk
2023-03-31 15:38:40 +02:00
committed by GitHub
parent 3956e306f2
commit 6f0064f687
6 changed files with 135 additions and 11 deletions

View File

@ -2,15 +2,15 @@
use crate::dirs::{JwtSecretPath, PlatformPath};
use clap::Args;
use jsonrpsee::{core::Error as RpcError, server::ServerHandle};
use jsonrpsee::server::ServerHandle;
use reth_interfaces::events::ChainEventSubscriptions;
use reth_network_api::{NetworkInfo, Peers};
use reth_primitives::ChainSpec;
use reth_provider::{BlockProvider, EvmEnvProvider, HeaderProvider, StateProviderFactory};
use reth_rpc::{JwtError, JwtSecret};
use reth_rpc_builder::{
constants, IpcServerBuilder, RethRpcModule, RpcModuleSelection, RpcServerConfig,
RpcServerHandle, ServerBuilder, TransportRpcModuleConfig,
constants, error::RpcError, IpcServerBuilder, RethRpcModule, RpcModuleSelection,
RpcServerConfig, RpcServerHandle, ServerBuilder, TransportRpcModuleConfig,
};
use reth_rpc_engine_api::EngineApiHandle;
use reth_tasks::TaskSpawner;

View File

@ -1,5 +1,6 @@
use crate::error::{RpcError, ServerKind};
pub use jsonrpsee::server::ServerBuilder;
use jsonrpsee::{core::Error as RpcError, server::ServerHandle, RpcModule};
use jsonrpsee::server::{RpcModule, ServerHandle};
use reth_network_api::{NetworkInfo, Peers};
use reth_primitives::ChainSpec;
use reth_provider::{BlockProvider, EvmEnvProvider, HeaderProvider, StateProviderFactory};
@ -74,7 +75,11 @@ where
tower::ServiceBuilder::new().layer(AuthLayer::new(JwtAuthValidator::new(secret)));
// By default, both http and ws are enabled.
let server = ServerBuilder::new().set_middleware(middleware).build(socket_addr).await?;
let server = ServerBuilder::new()
.set_middleware(middleware)
.build(socket_addr)
.await
.map_err(|err| RpcError::from_jsonrpsee_error(err, ServerKind::Auth(socket_addr)))?;
server.start(module)
Ok(server.start(module)?)
}

View File

@ -0,0 +1,64 @@
use std::net::SocketAddr;
use jsonrpsee::core::Error as JsonRpseeError;
use std::{io, io::ErrorKind};
/// Rpc server kind.
#[derive(Debug, PartialEq)]
pub enum ServerKind {
/// Http.
Http(SocketAddr),
/// Websocket.
WS(SocketAddr),
/// Auth.
Auth(SocketAddr),
}
impl std::fmt::Display for ServerKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ServerKind::Http(addr) => write!(f, "{addr} (HTTP-RPC server)"),
ServerKind::WS(addr) => write!(f, "{addr} (WS-RPC server)"),
ServerKind::Auth(addr) => write!(f, "{addr} (AUTH server)"),
}
}
}
/// Rpc Errors.
#[derive(Debug, thiserror::Error)]
pub enum RpcError {
/// Wrapper for `jsonrpsee::core::Error`.
#[error(transparent)]
RpcError(#[from] JsonRpseeError),
/// Address already in use.
#[error("Address {kind} is already in use (os error 98)")]
AddressAlreadyInUse {
/// Server kind.
kind: ServerKind,
/// IO error.
error: io::Error,
},
/// Custom error.
#[error("{0}")]
Custom(String),
}
impl RpcError {
/// Converts a `jsonrpsee::core::Error` to a more descriptive `RpcError`.
pub fn from_jsonrpsee_error(err: JsonRpseeError, kind: ServerKind) -> RpcError {
match err {
JsonRpseeError::Transport(err) => {
if let Some(io_error) = err.downcast_ref::<io::Error>() {
if io_error.kind() == ErrorKind::AddrInUse {
return RpcError::AddressAlreadyInUse {
kind,
error: io::Error::from(io_error.kind()),
}
}
}
RpcError::RpcError(err.into())
}
_ => err.into(),
}
}
}

View File

@ -54,8 +54,9 @@
//! ```
use constants::*;
use error::{RpcError, ServerKind};
use jsonrpsee::{
core::{server::rpc_module::Methods, Error as RpcError},
core::server::rpc_module::Methods,
server::{IdProvider, Server, ServerHandle},
RpcModule,
};
@ -89,6 +90,9 @@ pub mod auth;
/// Cors utilities.
mod cors;
/// Rpc error utilities.
pub mod error;
/// Eth utils
mod eth;
@ -865,11 +869,17 @@ impl RpcServerConfig {
let cors = cors.map_err(|err| RpcError::Custom(err.to_string()))?;
let middleware = tower::ServiceBuilder::new().layer(cors);
let http_server =
builder.set_middleware(middleware).build(http_socket_addr).await?;
builder.set_middleware(middleware).build(http_socket_addr).await.map_err(
|err| {
RpcError::from_jsonrpsee_error(err, ServerKind::Http(http_socket_addr))
},
)?;
server.http_local_addr = http_server.local_addr().ok();
server.http = Some(WsHttpServer::WithCors(http_server));
} else {
let http_server = builder.build(http_socket_addr).await?;
let http_server = builder.build(http_socket_addr).await.map_err(|err| {
RpcError::from_jsonrpsee_error(err, ServerKind::Http(http_socket_addr))
})?;
server.http_local_addr = http_server.local_addr().ok();
server.http = Some(WsHttpServer::Plain(http_server));
}
@ -884,11 +894,16 @@ impl RpcServerConfig {
if let Some(cors) = self.ws_cors_domains.as_deref().map(cors::create_cors_layer) {
let cors = cors.map_err(|err| RpcError::Custom(err.to_string()))?;
let middleware = tower::ServiceBuilder::new().layer(cors);
let ws_server = builder.set_middleware(middleware).build(ws_socket_addr).await?;
let ws_server =
builder.set_middleware(middleware).build(ws_socket_addr).await.map_err(
|err| RpcError::from_jsonrpsee_error(err, ServerKind::WS(ws_socket_addr)),
)?;
server.http_local_addr = ws_server.local_addr().ok();
server.ws = Some(WsHttpServer::WithCors(ws_server));
} else {
let ws_server = builder.build(ws_socket_addr).await?;
let ws_server = builder.build(ws_socket_addr).await.map_err(|err| {
RpcError::from_jsonrpsee_error(err, ServerKind::WS(ws_socket_addr))
})?;
server.ws_local_addr = ws_server.local_addr().ok();
server.ws = Some(WsHttpServer::Plain(ws_server));
}

View File

@ -1,5 +1,6 @@
mod http;
mod serde;
mod startup;
pub mod utils;
fn main() {}

View File

@ -0,0 +1,39 @@
//! Startup tests
use crate::utils::{launch_http, launch_ws, test_rpc_builder};
use reth_rpc_builder::{
error::{RpcError, ServerKind},
RethRpcModule, RpcServerConfig, TransportRpcModuleConfig,
};
use std::io;
fn is_addr_in_use_kind(err: RpcError, kind: ServerKind) -> bool {
match err {
RpcError::AddressAlreadyInUse { kind: k, error } => {
k == kind && error.kind() == io::ErrorKind::AddrInUse
}
_ => false,
}
}
#[tokio::test(flavor = "multi_thread")]
async fn test_http_addr_in_use() {
let handle = launch_http(vec![RethRpcModule::Admin]).await;
let addr = handle.http_local_addr().unwrap();
let builder = test_rpc_builder();
let server = builder.build(TransportRpcModuleConfig::set_http(vec![RethRpcModule::Admin]));
let result = server
.start_server(RpcServerConfig::http(Default::default()).with_http_address(addr))
.await;
assert!(is_addr_in_use_kind(result.unwrap_err(), ServerKind::Http(addr)));
}
#[tokio::test(flavor = "multi_thread")]
async fn test_ws_addr_in_use() {
let handle = launch_ws(vec![RethRpcModule::Admin]).await;
let addr = handle.ws_local_addr().unwrap();
let builder = test_rpc_builder();
let server = builder.build(TransportRpcModuleConfig::set_ws(vec![RethRpcModule::Admin]));
let result =
server.start_server(RpcServerConfig::ws(Default::default()).with_ws_address(addr)).await;
assert!(is_addr_in_use_kind(result.unwrap_err(), ServerKind::WS(addr)));
}