diff --git a/client/http-client/src/client.rs b/client/http-client/src/client.rs index 212d54abbb..6ee886b884 100644 --- a/client/http-client/src/client.rs +++ b/client/http-client/src/client.rs @@ -37,12 +37,12 @@ use hyper::body::HttpBody; use hyper::http::HeaderMap; use hyper::Body; use jsonrpsee_core::client::{ - generate_batch_id_range, BatchResponse, CertificateStore, ClientT, IdKind, RequestIdManager, Subscription, + generate_batch_id_range, BatchResponse, CertificateStore, ClientT, Error, IdKind, RequestIdManager, Subscription, SubscriptionClientT, }; use jsonrpsee_core::params::BatchRequestBuilder; use jsonrpsee_core::traits::ToRpcParams; -use jsonrpsee_core::{Error, JsonRawValue, TEN_MB_SIZE_BYTES}; +use jsonrpsee_core::{JsonRawValue, TEN_MB_SIZE_BYTES}; use jsonrpsee_types::{ErrorObject, InvalidRequestId, ResponseSuccess, TwoPointZero}; use serde::de::DeserializeOwned; use tower::layer::util::Identity; diff --git a/client/http-client/src/tests.rs b/client/http-client/src/tests.rs index da3e871886..e2a0cc0936 100644 --- a/client/http-client/src/tests.rs +++ b/client/http-client/src/tests.rs @@ -29,7 +29,7 @@ use crate::types::error::{ErrorCode, ErrorObject}; use crate::HttpClientBuilder; use jsonrpsee_core::client::{BatchResponse, ClientT, IdKind}; use jsonrpsee_core::params::BatchRequestBuilder; -use jsonrpsee_core::Error; +use jsonrpsee_core::ClientError; use jsonrpsee_core::{rpc_params, DeserializeOwned}; use jsonrpsee_test_utils::helpers::*; use jsonrpsee_test_utils::mocks::Id; @@ -59,8 +59,8 @@ async fn method_call_with_wrong_id_kind() { http_server_with_hardcoded_response(ok_response(exp.into(), Id::Num(0))).with_default_timeout().await.unwrap(); let uri = format!("http://{server_addr}"); let client = HttpClientBuilder::default().id_format(IdKind::String).build(&uri).unwrap(); - let res: Result = client.request("o", rpc_params![]).with_default_timeout().await.unwrap(); - assert!(matches!(res, Err(Error::InvalidRequestId(_)))); + let res: Result = client.request("o", rpc_params![]).with_default_timeout().await.unwrap(); + assert!(matches!(res, Err(ClientError::InvalidRequestId(_)))); } #[tokio::test] @@ -96,7 +96,7 @@ async fn response_with_wrong_id() { .await .unwrap() .unwrap_err(); - assert!(matches!(err, Error::InvalidRequestId(_))); + assert!(matches!(err, ClientError::InvalidRequestId(_))); } #[tokio::test] @@ -137,7 +137,7 @@ async fn internal_error_works() { async fn subscription_response_to_request() { let req = r#"{"jsonrpc":"2.0","method":"subscribe_hello","params":{"subscription":"3px4FrtxSYQ1zBKW154NoVnrDhrq764yQNCXEgZyM6Mu","result":"hello my friend"}}"#.to_string(); let err = run_request_with_response(req).with_default_timeout().await.unwrap().unwrap_err(); - assert!(matches!(err, Error::ParseError(_))); + assert!(matches!(err, ClientError::ParseError(_))); } #[tokio::test] @@ -261,23 +261,23 @@ async fn batch_request_out_of_order_response() { async fn run_batch_request_with_response( batch: BatchRequestBuilder<'_>, response: String, -) -> Result, Error> { +) -> Result, ClientError> { let server_addr = http_server_with_hardcoded_response(response).with_default_timeout().await.unwrap(); let uri = format!("http://{server_addr}"); let client = HttpClientBuilder::default().build(&uri).unwrap(); client.batch_request(batch).with_default_timeout().await.unwrap() } -async fn run_request_with_response(response: String) -> Result { +async fn run_request_with_response(response: String) -> Result { let server_addr = http_server_with_hardcoded_response(response).with_default_timeout().await.unwrap(); let uri = format!("http://{server_addr}"); let client = HttpClientBuilder::default().build(&uri).unwrap(); client.request("say_hello", rpc_params![]).with_default_timeout().await.unwrap() } -fn assert_jsonrpc_error_response(err: Error, exp: ErrorObjectOwned) { +fn assert_jsonrpc_error_response(err: ClientError, exp: ErrorObjectOwned) { match &err { - Error::Call(err) => { + ClientError::Call(err) => { assert_eq!(err, &exp); } e => panic!("Expected error: \"{err}\", got: {e:?}"), diff --git a/client/http-client/src/transport.rs b/client/http-client/src/transport.rs index 852248f434..741f53e3d1 100644 --- a/client/http-client/src/transport.rs +++ b/client/http-client/src/transport.rs @@ -10,9 +10,8 @@ use hyper::body::{Body, HttpBody}; use hyper::client::{Client, HttpConnector}; use hyper::http::{HeaderMap, HeaderValue}; use jsonrpsee_core::client::CertificateStore; -use jsonrpsee_core::error::GenericTransportError; -use jsonrpsee_core::http_helpers; use jsonrpsee_core::tracing::client::{rx_log_from_bytes, tx_log_from_str}; +use jsonrpsee_core::{http_helpers, GenericTransportError}; use std::error::Error as StdError; use std::future::Future; use std::pin::Pin; diff --git a/client/wasm-client/src/lib.rs b/client/wasm-client/src/lib.rs index 5d4f4d6a1e..943adf51ae 100644 --- a/client/wasm-client/src/lib.rs +++ b/client/wasm-client/src/lib.rs @@ -36,8 +36,7 @@ pub use jsonrpsee_types as types; use std::time::Duration; use jsonrpsee_client_transport::web; -use jsonrpsee_core::client::{ClientBuilder, IdKind}; -use jsonrpsee_core::Error; +use jsonrpsee_core::client::{ClientBuilder, Error, IdKind}; /// Builder for [`Client`]. /// diff --git a/client/ws-client/Cargo.toml b/client/ws-client/Cargo.toml index f4efd862ef..f0947af370 100644 --- a/client/ws-client/Cargo.toml +++ b/client/ws-client/Cargo.toml @@ -14,10 +14,10 @@ readme.workspace = true publish = true [dependencies] +http = "0.2.0" jsonrpsee-types = { workspace = true } jsonrpsee-client-transport = { workspace = true, features = ["ws"] } jsonrpsee-core = { workspace = true, features = ["async-client"] } -http = "0.2.0" url = "2.4.0" [dev-dependencies] diff --git a/client/ws-client/src/lib.rs b/client/ws-client/src/lib.rs index cb0b81d843..c354ff7992 100644 --- a/client/ws-client/src/lib.rs +++ b/client/ws-client/src/lib.rs @@ -38,18 +38,17 @@ #[cfg(test)] mod tests; +pub use http::{HeaderMap, HeaderValue}; pub use jsonrpsee_core::client::Client as WsClient; pub use jsonrpsee_types as types; -pub use http::{HeaderMap, HeaderValue}; -use std::time::Duration; -use url::Url; - use jsonrpsee_client_transport::ws::{AsyncRead, AsyncWrite, WsTransportClientBuilder}; use jsonrpsee_core::client::{ - CertificateStore, ClientBuilder, IdKind, MaybeSend, TransportReceiverT, TransportSenderT, + CertificateStore, ClientBuilder, Error, IdKind, MaybeSend, TransportReceiverT, TransportSenderT, }; -use jsonrpsee_core::{Error, TEN_MB_SIZE_BYTES}; +use jsonrpsee_core::TEN_MB_SIZE_BYTES; +use std::time::Duration; +use url::Url; /// Builder for [`WsClient`]. /// @@ -220,7 +219,7 @@ impl WsClientBuilder { /// ## Panics /// /// Panics if being called outside of `tokio` runtime context. - pub async fn build_with_transport(self, sender: S, receiver: R) -> Result + pub fn build_with_transport(self, sender: S, receiver: R) -> WsClient where S: TransportSenderT + Send, R: TransportReceiverT + Send, @@ -246,7 +245,7 @@ impl WsClientBuilder { client = client.ping_interval(interval); } - Ok(client.build_with_tokio(sender, receiver)) + client.build_with_tokio(sender, receiver) } /// Build the [`WsClient`] with specified data stream, using [`WsTransportClientBuilder::build_with_stream`]. @@ -271,7 +270,7 @@ impl WsClientBuilder { let (sender, receiver) = transport_builder.build_with_stream(uri, data_stream).await.map_err(|e| Error::Transport(e.into()))?; - let ws_client = self.build_with_transport(sender, receiver).await?; + let ws_client = self.build_with_transport(sender, receiver); Ok(ws_client) } @@ -294,7 +293,7 @@ impl WsClientBuilder { let uri = Url::parse(url.as_ref()).map_err(|e| Error::Transport(e.into()))?; let (sender, receiver) = transport_builder.build(uri).await.map_err(|e| Error::Transport(e.into()))?; - let ws_client = self.build_with_transport(sender, receiver).await?; + let ws_client = self.build_with_transport(sender, receiver); Ok(ws_client) } } diff --git a/client/ws-client/src/tests.rs b/client/ws-client/src/tests.rs index faaf8c46b7..565e650bf0 100644 --- a/client/ws-client/src/tests.rs +++ b/client/ws-client/src/tests.rs @@ -29,10 +29,9 @@ use crate::types::error::{ErrorCode, ErrorObject}; use crate::WsClientBuilder; -use jsonrpsee_core::client::{BatchResponse, ClientT, SubscriptionClientT}; -use jsonrpsee_core::client::{IdKind, Subscription}; +use jsonrpsee_core::client::{BatchResponse, ClientT, Error, IdKind, Subscription, SubscriptionClientT}; use jsonrpsee_core::params::BatchRequestBuilder; -use jsonrpsee_core::{rpc_params, DeserializeOwned, Error}; +use jsonrpsee_core::{rpc_params, DeserializeOwned}; use jsonrpsee_test_utils::helpers::*; use jsonrpsee_test_utils::mocks::{Id, WebSocketTestServer}; use jsonrpsee_test_utils::TimeoutFutureExt; diff --git a/core/Cargo.toml b/core/Cargo.toml index 84ee12c693..edb3a5aa3e 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -29,13 +29,11 @@ futures-util = { version = "0.3.14", default-features = false, optional = true } hyper = { version = "0.14.10", default-features = false, features = ["stream"], optional = true } rustc-hash = { version = "1", optional = true } rand = { version = "0.8", optional = true } -soketto = { version = "0.7.1", optional = true } parking_lot = { version = "0.12", optional = true } tokio = { version = "1.16", optional = true } wasm-bindgen-futures = { version = "0.4.19", optional = true } futures-timer = { version = "3", optional = true } - [features] default = [] http-helpers = ["hyper", "futures-util"] diff --git a/core/src/client/async_client/helpers.rs b/core/src/client/async_client/helpers.rs index ceb2527071..eb42e851e2 100644 --- a/core/src/client/async_client/helpers.rs +++ b/core/src/client/async_client/helpers.rs @@ -26,10 +26,9 @@ use crate::client::async_client::LOG_TARGET; use crate::client::async_client::manager::{RequestManager, RequestStatus}; -use crate::client::{RequestMessage, TransportSenderT}; +use crate::client::{RequestMessage, TransportSenderT, Error}; use crate::params::ArrayParams; use crate::traits::ToRpcParams; -use crate::Error; use futures_timer::Delay; use futures_util::future::{self, Either}; @@ -56,7 +55,7 @@ pub(crate) fn process_batch_response( manager: &mut RequestManager, rps: Vec, range: Range, -) -> Result<(), Error> { +) -> Result<(), InvalidRequestId> { let mut responses = Vec::with_capacity(rps.len()); let start_idx = range.start; @@ -65,7 +64,7 @@ pub(crate) fn process_batch_response( Some(state) => state, None => { tracing::warn!(target: LOG_TARGET, "Received unknown batch response"); - return Err(InvalidRequestId::NotPendingRequest(format!("{:?}", range)).into()); + return Err(InvalidRequestId::NotPendingRequest(format!("{:?}", range))); } }; @@ -81,7 +80,7 @@ pub(crate) fn process_batch_response( if let Some(elem) = maybe_elem { *elem = rp.result; } else { - return Err(InvalidRequestId::NotPendingRequest(rp.id.to_string()).into()); + return Err(InvalidRequestId::NotPendingRequest(rp.id.to_string())); } } @@ -155,7 +154,7 @@ pub(crate) fn process_notification(manager: &mut RequestManager, notif: Notifica Ok(()) => (), Err(err) => { tracing::warn!(target: LOG_TARGET, "Could not send notification, dropping handler for {:?} error: {:?}", notif.method, err); - let _ = manager.remove_notification_handler(notif.method.into_owned()); + let _ = manager.remove_notification_handler(¬if.method); } }, None => { @@ -173,7 +172,7 @@ pub(crate) fn process_single_response( manager: &mut RequestManager, response: Response, max_capacity_per_subscription: usize, -) -> Result, Error> { +) -> Result, InvalidRequestId> { let response_id = response.id.clone().into_owned(); let result = ResponseSuccess::try_from(response).map(|s| s.result).map_err(Error::Call); @@ -182,7 +181,7 @@ pub(crate) fn process_single_response( let send_back_oneshot = match manager.complete_pending_call(response_id.clone()) { Some(Some(send)) => send, Some(None) => return Ok(None), - None => return Err(InvalidRequestId::NotPendingRequest(response_id.to_string()).into()), + None => return Err(InvalidRequestId::NotPendingRequest(response_id.to_string())), }; let _ = send_back_oneshot.send(result); @@ -223,7 +222,7 @@ pub(crate) fn process_single_response( } RequestStatus::Subscription | RequestStatus::Invalid => { - Err(InvalidRequestId::NotPendingRequest(response_id.to_string()).into()) + Err(InvalidRequestId::NotPendingRequest(response_id.to_string())) } } } diff --git a/core/src/client/async_client/manager.rs b/core/src/client/async_client/manager.rs index f5a7840071..3ac657cbcf 100644 --- a/core/src/client/async_client/manager.rs +++ b/core/src/client/async_client/manager.rs @@ -37,7 +37,10 @@ use std::{ ops::Range, }; -use crate::{client::BatchEntry, Error}; +use crate::{ + client::BatchEntry, + client::Error, error::RegisterMethodError, +}; use jsonrpsee_types::{Id, SubscriptionId}; use rustc_hash::FxHashMap; use serde_json::value::Value as JsonValue; @@ -183,22 +186,18 @@ impl RequestManager { &mut self, method: &str, send_back: SubscriptionSink, - ) -> Result<(), Error> { + ) -> Result<(), RegisterMethodError> { if let Entry::Vacant(handle) = self.notification_handlers.entry(method.to_owned()) { handle.insert(send_back); Ok(()) } else { - Err(Error::MethodAlreadyRegistered(method.to_owned())) + Err(RegisterMethodError::AlreadyRegistered(method.to_owned())) } } /// Removes a notification handler. - pub(crate) fn remove_notification_handler(&mut self, method: String) -> Result<(), Error> { - if self.notification_handlers.remove(&method).is_some() { - Ok(()) - } else { - Err(Error::UnregisteredNotification(method)) - } + pub(crate) fn remove_notification_handler(&mut self, method: &str) -> Option { + self.notification_handlers.remove(method) } /// Tries to complete a pending subscription. diff --git a/core/src/client/async_client/mod.rs b/core/src/client/async_client/mod.rs index 61274e28b3..769ed99db5 100644 --- a/core/src/client/async_client/mod.rs +++ b/core/src/client/async_client/mod.rs @@ -5,11 +5,11 @@ mod manager; use crate::client::async_client::helpers::{process_subscription_close_response, InnerBatchResponse}; use crate::client::{ - BatchMessage, BatchResponse, ClientT, ReceivedMessage, RegisterNotificationMessage, RequestMessage, Subscription, - SubscriptionClientT, SubscriptionKind, SubscriptionMessage, TransportReceiverT, TransportSenderT, + BatchMessage, BatchResponse, ClientT, ReceivedMessage, RegisterNotificationMessage, RequestMessage, + Subscription, SubscriptionClientT, SubscriptionKind, SubscriptionMessage, TransportReceiverT, TransportSenderT, Error }; -use crate::error::Error; -use crate::params::BatchRequestBuilder; +use crate::error::RegisterMethodError; +use crate::params::{BatchRequestBuilder, EmptyBatchRequest}; use crate::tracing::client::{rx_log_from_json, tx_log_from_str}; use crate::traits::ToRpcParams; use crate::JsonRawValue; @@ -506,7 +506,9 @@ impl SubscriptionClientT for Client { Notif: DeserializeOwned, { if subscribe_method == unsubscribe_method { - return Err(Error::SubscriptionNameConflict(unsubscribe_method.to_owned())); + return Err(RegisterMethodError::SubscriptionNameConflict( + unsubscribe_method.to_owned(), + ).into()); } let guard = self.id_manager.next_request_two_ids()?; @@ -654,7 +656,7 @@ fn handle_backend_messages( range.end += 1; process_batch_response(&mut manager.lock(), batch, range)?; } else { - return Err(Error::EmptyBatchRequest); + return Err(EmptyBatchRequest.into()); } } else { return Err(unparse_error(raw)); @@ -762,12 +764,13 @@ async fn handle_frontend_messages( if manager.lock().insert_notification_handler(®.method, subscribe_tx).is_ok() { let _ = reg.send_back.send(Ok((subscribe_rx, reg.method))); } else { - let _ = reg.send_back.send(Err(Error::MethodAlreadyRegistered(reg.method))); + let _ = + reg.send_back.send(Err(RegisterMethodError::AlreadyRegistered(reg.method).into())); } } // User dropped the NotificationHandler for this method FrontToBack::UnregisterNotification(method) => { - let _ = manager.lock().remove_notification_handler(method); + let _ = manager.lock().remove_notification_handler(&method); } }; diff --git a/core/src/client/error.rs b/core/src/client/error.rs new file mode 100644 index 0000000000..13a435a998 --- /dev/null +++ b/core/src/client/error.rs @@ -0,0 +1,72 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! Error type for client(s). + +use std::sync::Arc; +use crate::{params::EmptyBatchRequest, RegisterMethodError}; +use jsonrpsee_types::{ErrorObjectOwned, InvalidRequestId}; + +/// Error type. +#[derive(Debug, thiserror::Error)] +pub enum Error { + /// JSON-RPC error which can occur when a JSON-RPC call fails. + #[error("{0}")] + Call(#[from] ErrorObjectOwned), + /// Networking error or error on the low-level protocol layer. + #[error("Networking or low-level protocol error: {0}")] + Transport(#[source] anyhow::Error), + /// The background task has been terminated. + #[error("The background task been terminated because: {0}; restart required")] + RestartNeeded(Arc), + /// Failed to parse the data. + #[error("Parse error: {0}")] + ParseError(#[from] serde_json::Error), + /// Invalid subscription ID. + #[error("Invalid subscription ID")] + InvalidSubscriptionId, + /// Invalid request ID. + #[error("{0}")] + InvalidRequestId(#[from] InvalidRequestId), + /// Request timeout + #[error("Request timeout")] + RequestTimeout, + /// Max number of request slots exceeded. + #[error("Configured max number of request slots exceeded")] + MaxSlotsExceeded, + /// Custom error. + #[error("Custom error: {0}")] + Custom(String), + /// Not implemented for HTTP clients. + #[error("Not implemented")] + HttpNotImplemented, + /// Empty batch request. + #[error("{0}")] + EmptyBatchRequest(#[from] EmptyBatchRequest), + /// The error returned when registering a method or subscription failed. + #[error("{0}")] + RegisterMethod(#[from] RegisterMethodError), +} \ No newline at end of file diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index bfcdbba2b7..6b91774fe9 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -26,6 +26,14 @@ //! Shared utilities for `jsonrpsee` clients. +cfg_async_client! { + pub mod async_client; + pub use async_client::{Client, ClientBuilder}; +} + +pub mod error; +pub use error::Error; + use std::fmt; use std::ops::Range; use std::pin::Pin; @@ -33,7 +41,6 @@ use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; use std::task; -use crate::error::Error; use crate::params::BatchRequestBuilder; use crate::traits::ToRpcParams; use async_trait::async_trait; @@ -53,11 +60,6 @@ pub mod __reexports { pub use crate::params::ArrayParams; } -cfg_async_client! { - pub mod async_client; - pub use async_client::{Client, ClientBuilder}; -} - /// [JSON-RPC](https://www.jsonrpc.org/specification) client interface that can make requests and notifications. #[async_trait] pub trait ClientT { diff --git a/core/src/error.rs b/core/src/error.rs index 4255e10415..93c526ff63 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -24,90 +24,6 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use std::{fmt, sync::Arc}; - -use jsonrpsee_types::{params::InvalidRequestId, ErrorObjectOwned}; - -/// Convenience type for displaying errors. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Mismatch { - /// Expected value. - pub expected: T, - /// Actual value. - pub got: T, -} - -impl fmt::Display for Mismatch { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_fmt(format_args!("Expected: {}, Got: {}", self.expected, self.got)) - } -} - -/// Error type. -#[derive(Debug, thiserror::Error)] -pub enum Error { - /// JSON-RPC error which can occur when a JSON-RPC call fails. - #[error("{0}")] - Call(#[from] ErrorObjectOwned), - /// Networking error or error on the low-level protocol layer. - #[error("Networking or low-level protocol error: {0}")] - Transport(#[source] anyhow::Error), - /// Invalid response, - #[error("Invalid response: {0}")] - InvalidResponse(Mismatch), - /// The background task has been terminated. - #[error("The background task been terminated because: {0}; restart required")] - RestartNeeded(Arc), - /// Failed to parse the data. - #[error("Parse error: {0}")] - ParseError(#[from] serde_json::Error), - /// Invalid subscription ID. - #[error("Invalid subscription ID")] - InvalidSubscriptionId, - /// Invalid request ID. - #[error("{0}")] - InvalidRequestId(#[from] InvalidRequestId), - /// Client received a notification with an unregistered method - #[error("Unregistered notification method")] - UnregisteredNotification(String), - /// A request with the same request ID has already been registered. - #[error("A request with the same request ID has already been registered")] - DuplicateRequestId, - /// Method was already registered. - #[error("Method: {0} was already registered")] - MethodAlreadyRegistered(String), - /// Method with that name has not yet been registered. - #[error("Method: {0} has not yet been registered")] - MethodNotFound(String), - /// Subscribe and unsubscribe method names are the same. - #[error("Cannot use the same method name for subscribe and unsubscribe, used: {0}")] - SubscriptionNameConflict(String), - /// Request timeout - #[error("Request timeout")] - RequestTimeout, - /// Configured max number of request slots exceeded. - #[error("Configured max number of request slots exceeded")] - MaxSlotsExceeded, - /// Attempted to stop server that is already stopped. - #[error("Attempted to stop server that is already stopped")] - AlreadyStopped, - /// List passed into access control based on HTTP header verification. - #[error("Must set at least one allowed value for the {0} header")] - EmptyAllowList(&'static str), - /// Access control verification of HTTP headers failed. - #[error("HTTP header: `{0}` value: `{1}` verification failed")] - HttpHeaderRejected(&'static str, String), - /// Custom error. - #[error("Custom error: {0}")] - Custom(String), - /// Not implemented for HTTP clients. - #[error("Not implemented")] - HttpNotImplemented, - /// Empty batch request. - #[error("Empty batch request is not allowed")] - EmptyBatchRequest, -} - /// Generic transport error. #[derive(Debug, thiserror::Error)] pub enum GenericTransportError { @@ -132,29 +48,16 @@ impl From for StringError { } } -impl From for Error { - fn from(io_err: std::io::Error) -> Error { - Error::Transport(io_err.into()) - } -} - -#[cfg(feature = "soketto")] -impl From for Error { - fn from(handshake_err: soketto::handshake::Error) -> Error { - Error::Transport(handshake_err.into()) - } -} - -#[cfg(feature = "soketto")] -impl From for Error { - fn from(conn_err: soketto::connection::Error) -> Error { - Error::Transport(conn_err.into()) - } -} - -#[cfg(feature = "hyper")] -impl From for Error { - fn from(hyper_err: hyper::Error) -> Error { - Error::Transport(hyper_err.into()) - } +/// The error returned when registering a method or subscription failed. +#[derive(Debug, Clone, thiserror::Error)] +pub enum RegisterMethodError { + /// Method was already registered. + #[error("Method: {0} was already registered")] + AlreadyRegistered(String), + /// Subscribe and unsubscribe method names are the same. + #[error("Cannot use the same method name for subscribe and unsubscribe, used: {0}")] + SubscriptionNameConflict(String), + /// Method with that name has not yet been registered. + #[error("Method: {0} has not yet been registered")] + MethodNotFound(String), } diff --git a/core/src/lib.rs b/core/src/lib.rs index bff45dfec0..a4cf37bcef 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -54,12 +54,13 @@ cfg_server! { cfg_client! { pub mod client; + pub use client::Error as ClientError; } /// Shared tracing helpers to trace RPC calls. pub mod tracing; pub use async_trait::async_trait; -pub use error::{Error, GenericTransportError, StringError}; +pub use error::{GenericTransportError, RegisterMethodError, StringError}; /// JSON-RPC result. pub type RpcResult = std::result::Result; diff --git a/core/src/params.rs b/core/src/params.rs index a9f4600e4b..3521097037 100644 --- a/core/src/params.rs +++ b/core/src/params.rs @@ -27,7 +27,6 @@ //! RPC parameters. use crate::traits::ToRpcParams; -use crate::Error; use serde::Serialize; use serde_json::value::RawValue; @@ -164,9 +163,9 @@ impl Default for ObjectParams { } impl ToRpcParams for ObjectParams { - fn to_rpc_params(self) -> Result>, Error> { + fn to_rpc_params(self) -> Result>, serde_json::Error> { if let Some(json) = self.0.build() { - RawValue::from_string(json).map(Some).map_err(Error::ParseError) + RawValue::from_string(json).map(Some) } else { Ok(None) } @@ -210,9 +209,9 @@ impl Default for ArrayParams { } impl ToRpcParams for ArrayParams { - fn to_rpc_params(self) -> Result>, Error> { + fn to_rpc_params(self) -> Result>, serde_json::Error> { if let Some(json) = self.0.build() { - RawValue::from_string(json).map(Some).map_err(Error::ParseError) + RawValue::from_string(json).map(Some) } else { Ok(None) } @@ -222,6 +221,11 @@ impl ToRpcParams for ArrayParams { /// Initial number of parameters in a batch request. const BATCH_PARAMS_NUM_CAPACITY: usize = 4; +/// Error representing an empty batch request. +#[derive(Debug, Clone, Copy, thiserror::Error)] +#[error("Empty batch request is not allowed")] +pub struct EmptyBatchRequest; + /// Request builder that serializes RPC parameters to construct a valid batch parameter. /// This is the equivalent of chaining multiple RPC requests. #[derive(Clone, Debug, Default)] @@ -234,16 +238,16 @@ impl<'a> BatchRequestBuilder<'a> { } /// Inserts the RPC method with provided parameters into the builder. - pub fn insert(&mut self, method: &'a str, value: Params) -> Result<(), Error> { + pub fn insert(&mut self, method: &'a str, value: Params) -> Result<(), serde_json::Error> { self.0.push((method, value.to_rpc_params()?)); Ok(()) } /// Finish the building process and return a valid batch parameter. #[allow(clippy::type_complexity)] - pub fn build(self) -> Result>)>, Error> { + pub fn build(self) -> Result>)>, EmptyBatchRequest> { if self.0.is_empty() { - Err(Error::EmptyBatchRequest) + Err(EmptyBatchRequest) } else { Ok(self.0) } diff --git a/core/src/server/rpc_module.rs b/core/src/server/rpc_module.rs index 442285b676..abceec27d8 100644 --- a/core/src/server/rpc_module.rs +++ b/core/src/server/rpc_module.rs @@ -30,7 +30,7 @@ use std::future::Future; use std::ops::{Deref, DerefMut}; use std::sync::Arc; -use crate::error::Error; +use crate::error::RegisterMethodError; use crate::id_providers::RandomIntegerIdProvider; use crate::server::LOG_TARGET; use crate::server::helpers::{MethodResponse, MethodSink}; @@ -43,7 +43,7 @@ use crate::traits::ToRpcParams; use futures_util::{future::BoxFuture, FutureExt}; use jsonrpsee_types::error::{ErrorCode, ErrorObject}; use jsonrpsee_types::{ - Id, Params, Request, Response, ResponsePayload, ResponseSuccess, SubscriptionId as RpcSubscriptionId, + Id, Params, Request, Response, ResponsePayload, ResponseSuccess, SubscriptionId as RpcSubscriptionId, ErrorObjectOwned, }; use rustc_hash::FxHashMap; use serde::de::DeserializeOwned; @@ -78,6 +78,21 @@ pub type MaxResponseSize = usize; /// - a [`mpsc::UnboundedReceiver`] to receive future subscription results pub type RawRpcResponse = (MethodResponse, mpsc::Receiver); + +/// The error that can occur when [`Methods::call`] or [`Methods::subscribe`] is invoked. +#[derive(thiserror::Error, Debug)] +pub enum MethodsError { + /// Failed to parse the call as valid JSON-RPC. + #[error("{0}")] + Parse(#[from] serde_json::Error), + /// Specific JSON-RPC error. + #[error("{0}")] + JsonRpc(#[from] ErrorObjectOwned), + #[error("Invalid subscription ID: `{0}`")] + /// Invalid subscription ID. + InvalidSubscriptionId(String), +} + /// This represent a response to a RPC call /// and `Subscribe` calls are handled differently /// because we want to prevent subscriptions to start @@ -190,9 +205,9 @@ impl Methods { } /// Verifies that the method name is not already taken, and returns an error if it is. - pub fn verify_method_name(&self, name: &'static str) -> Result<(), Error> { + pub fn verify_method_name(&mut self, name: &'static str) -> Result<(), RegisterMethodError> { if self.callbacks.contains_key(name) { - return Err(Error::MethodAlreadyRegistered(name.into())); + return Err(RegisterMethodError::AlreadyRegistered(name.into())); } Ok(()) @@ -204,9 +219,9 @@ impl Methods { &mut self, name: &'static str, callback: MethodCallback, - ) -> Result<&mut MethodCallback, Error> { + ) -> Result<&mut MethodCallback, RegisterMethodError> { match self.mut_callbacks().entry(name) { - Entry::Occupied(_) => Err(Error::MethodAlreadyRegistered(name.into())), + Entry::Occupied(_) => Err(RegisterMethodError::AlreadyRegistered(name.into())), Entry::Vacant(vacant) => Ok(vacant.insert(callback)), } } @@ -218,7 +233,7 @@ impl Methods { /// Merge two [`Methods`]'s by adding all [`MethodCallback`]s from `other` into `self`. /// Fails if any of the methods in `other` is present already. - pub fn merge(&mut self, other: impl Into) -> Result<(), Error> { + pub fn merge(&mut self, other: impl Into) -> Result<(), RegisterMethodError> { let mut other = other.into(); for name in other.callbacks.keys() { @@ -272,13 +287,13 @@ impl Methods { &self, method: &str, params: Params, - ) -> Result { + ) -> Result { let params = params.to_rpc_params()?; let req = Request::new(method.into(), params.as_ref().map(|p| p.as_ref()), Id::Number(0)); tracing::trace!(target: LOG_TARGET, "[Methods::call] Method: {:?}, params: {:?}", method, params); let (resp, _) = self.inner_call(req, 1, mock_subscription_permit()).await; let rp = serde_json::from_str::>(&resp.result)?; - ResponseSuccess::try_from(rp).map(|s| s.result).map_err(Error::Call) + ResponseSuccess::try_from(rp).map(|s| s.result).map_err(|e| MethodsError::JsonRpc(e.into_owned())) } /// Make a request (JSON-RPC method call or subscription) by using raw JSON. @@ -316,8 +331,8 @@ impl Methods { &self, request: &str, buf_size: usize, - ) -> Result<(MethodResponse, mpsc::Receiver), Error> { - tracing::trace!(target: LOG_TARGET, "[Methods::raw_json_request] Request: {:?}", request); + ) -> Result<(MethodResponse, mpsc::Receiver), serde_json::Error> { + tracing::trace!("[Methods::raw_json_request] Request: {:?}", request); let req: Request = serde_json::from_str(request)?; let (resp, rx) = self.inner_call(req, buf_size, mock_subscription_permit()).await; @@ -372,7 +387,7 @@ impl Methods { /// #[tokio::main] /// async fn main() { /// use jsonrpsee::{RpcModule, SubscriptionMessage}; - /// use jsonrpsee::core::{Error, EmptyServerParams, RpcResult}; + /// use jsonrpsee::core::{EmptyServerParams, RpcResult}; /// /// let mut module = RpcModule::new(()); /// module.register_subscription("hi", "hi", "goodbye", |_, pending, _| async move { @@ -387,18 +402,23 @@ impl Methods { /// assert_eq!(&sub_resp, "one answer"); /// } /// ``` - pub async fn subscribe_unbounded(&self, sub_method: &str, params: impl ToRpcParams) -> Result { + pub async fn subscribe_unbounded( + &self, + sub_method: &str, + params: impl ToRpcParams, + ) -> Result { self.subscribe(sub_method, params, u32::MAX as usize).await } /// Similar to [`Methods::subscribe_unbounded`] but it's using a bounded channel and the buffer capacity must be /// provided. + /// pub async fn subscribe( &self, sub_method: &str, params: impl ToRpcParams, buf_size: usize, - ) -> Result { + ) -> Result { let params = params.to_rpc_params()?; let req = Request::new(sub_method.into(), params.as_ref().map(|p| p.as_ref()), Id::Number(0)); @@ -410,7 +430,7 @@ impl Methods { let as_success: ResponseSuccess = serde_json::from_str::>(&resp.result)?.try_into()?; - let sub_id = as_success.result.try_into().map_err(|_| Error::InvalidSubscriptionId)?; + let sub_id = as_success.result.try_into().map_err(|_| MethodsError::InvalidSubscriptionId(resp.result.clone()))?; Ok(Subscription { sub_id, rx }) } @@ -470,7 +490,7 @@ impl RpcModule { &mut self, method_name: &'static str, callback: F, - ) -> Result<&mut MethodCallback, Error> + ) -> Result<&mut MethodCallback, RegisterMethodError> where Context: Send + Sync + 'static, R: IntoResponse + 'static, @@ -491,7 +511,7 @@ impl RpcModule { &mut self, method_name: &'static str, callback: Fun, - ) -> Result<&mut MethodCallback, Error> + ) -> Result<&mut MethodCallback, RegisterMethodError> where R: IntoResponse + 'static, Fut: Future + Send, @@ -520,7 +540,7 @@ impl RpcModule { &mut self, method_name: &'static str, callback: F, - ) -> Result<&mut MethodCallback, Error> + ) -> Result<&mut MethodCallback, RegisterMethodError> where Context: Send + Sync + 'static, R: IntoResponse + 'static, @@ -651,7 +671,7 @@ impl RpcModule { notif_method_name: &'static str, unsubscribe_method_name: &'static str, callback: F, - ) -> Result<&mut MethodCallback, Error> + ) -> Result<&mut MethodCallback, RegisterMethodError> where Context: Send + Sync + 'static, F: (Fn(Params<'static>, PendingSubscriptionSink, Arc) -> Fut) + Send + Sync + Clone + 'static, @@ -785,7 +805,7 @@ impl RpcModule { notif_method_name: &'static str, unsubscribe_method_name: &'static str, callback: F, - ) -> Result<&mut MethodCallback, Error> + ) -> Result<&mut MethodCallback, RegisterMethodError> where Context: Send + Sync + 'static, F: (Fn(Params, PendingSubscriptionSink, Arc) -> R) + Send + Sync + Clone + 'static, @@ -837,9 +857,9 @@ impl RpcModule { &mut self, subscribe_method_name: &'static str, unsubscribe_method_name: &'static str, - ) -> Result { + ) -> Result { if subscribe_method_name == unsubscribe_method_name { - return Err(Error::SubscriptionNameConflict(subscribe_method_name.into())); + return Err(RegisterMethodError::SubscriptionNameConflict(subscribe_method_name.into())); } self.methods.verify_method_name(subscribe_method_name)?; @@ -889,12 +909,16 @@ impl RpcModule { } /// Register an alias for an existing_method. Alias uniqueness is enforced. - pub fn register_alias(&mut self, alias: &'static str, existing_method: &'static str) -> Result<(), Error> { + pub fn register_alias( + &mut self, + alias: &'static str, + existing_method: &'static str, + ) -> Result<(), RegisterMethodError> { self.methods.verify_method_name(alias)?; let callback = match self.methods.callbacks.get(existing_method) { Some(callback) => callback.clone(), - None => return Err(Error::MethodNotFound(existing_method.into())), + None => return Err(RegisterMethodError::MethodNotFound(existing_method.into())), }; self.methods.mut_callbacks().insert(alias, callback); diff --git a/core/src/server/subscription.rs b/core/src/server/subscription.rs index 7ba9753bd2..a13d668fe2 100644 --- a/core/src/server/subscription.rs +++ b/core/src/server/subscription.rs @@ -26,11 +26,12 @@ //! Subscription related types and traits for server implementations. +use super::MethodsError; use super::helpers::{MethodResponse, MethodSink}; use crate::server::LOG_TARGET; use crate::server::error::{DisconnectError, PendingSubscriptionAcceptError, SendTimeoutError, TrySendError}; use crate::server::rpc_module::ConnectionId; -use crate::{traits::IdProvider, Error, StringError}; +use crate::{traits::IdProvider, error::StringError}; use jsonrpsee_types::SubscriptionPayload; use jsonrpsee_types::{ response::SubscriptionError, ErrorObjectOwned, Id, ResponsePayload, SubscriptionId, SubscriptionResponse, @@ -434,8 +435,8 @@ impl Subscription { &self.sub_id } - /// Receives the next value on the subscription if value could be decoded as T. - pub async fn next(&mut self) -> Option), Error>> { + /// Receives the next value on the subscription if the value could be decoded as T. + pub async fn next(&mut self) -> Option), MethodsError>> { let raw = self.rx.recv().await?; tracing::debug!(target: LOG_TARGET, "[Subscription::next]: rx {}", raw); diff --git a/core/src/traits.rs b/core/src/traits.rs index bf95913f31..64e158c913 100644 --- a/core/src/traits.rs +++ b/core/src/traits.rs @@ -24,7 +24,6 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::Error; use jsonrpsee_types::SubscriptionId; use serde::Serialize; use serde_json::value::RawValue; @@ -45,14 +44,13 @@ use serde_json::value::RawValue; /// ```rust /// use jsonrpsee_core::traits::ToRpcParams; /// use serde_json::value::RawValue; -/// use jsonrpsee_core::Error; /// /// struct ManualParam; /// /// impl ToRpcParams for ManualParam { -/// fn to_rpc_params(self) -> Result>, Error> { +/// fn to_rpc_params(self) -> Result>, serde_json::Error> { /// // Manually define a valid JSONRPC parameter. -/// RawValue::from_string("[1, \"2\", 3]".to_string()).map(Some).map_err(Error::ParseError) +/// RawValue::from_string("[1, \"2\", 3]".to_string()).map(Some) /// } /// } /// ``` @@ -63,7 +61,6 @@ use serde_json::value::RawValue; /// use jsonrpsee_core::traits::ToRpcParams; /// use serde_json::value::RawValue; /// use serde::Serialize; -/// use jsonrpsee_core::Error; /// /// #[derive(Serialize)] /// struct SerParam { @@ -72,24 +69,24 @@ use serde_json::value::RawValue; /// }; /// /// impl ToRpcParams for SerParam { -/// fn to_rpc_params(self) -> Result>, Error> { +/// fn to_rpc_params(self) -> Result>, serde_json::Error> { /// let s = String::from_utf8(serde_json::to_vec(&self)?).expect("Valid UTF8 format"); -/// RawValue::from_string(s).map(Some).map_err(Error::ParseError) +/// RawValue::from_string(s).map(Some) /// } /// } /// ``` pub trait ToRpcParams { /// Consume and serialize the type as a JSON raw value. - fn to_rpc_params(self) -> Result>, Error>; + fn to_rpc_params(self) -> Result>, serde_json::Error>; } // To not bound the `ToRpcParams: Serialize` define a custom implementation // for types which are serializable. macro_rules! to_rpc_params_impl { () => { - fn to_rpc_params(self) -> Result>, Error> { - let json = serde_json::to_string(&self).map_err(Error::ParseError)?; - RawValue::from_string(json).map(Some).map_err(Error::ParseError) + fn to_rpc_params(self) -> Result>, serde_json::Error> { + let json = serde_json::to_string(&self)?; + RawValue::from_string(json).map(Some) } }; } diff --git a/proc-macros/src/render_client.rs b/proc-macros/src/render_client.rs index 1cdb4c1029..4f2f6c39d5 100644 --- a/proc-macros/src/render_client.rs +++ b/proc-macros/src/render_client.rs @@ -93,9 +93,10 @@ impl RpcDescription { return quote_spanned!(args.span() => compile_error!("Result must be have two arguments")); } - // Force the last argument to be `jsonrpsee::core::Error`: + // Force the last argument to be `jsonrpsee::core::ClientError`: let error_arg = args.last_mut().unwrap(); - *error_arg = syn::GenericArgument::Type(syn::Type::Verbatim(self.jrps_client_item(quote! { core::Error }))); + *error_arg = + syn::GenericArgument::Type(syn::Type::Verbatim(self.jrps_client_item(quote! { core::client::Error }))); quote!(#ty) } else if type_name.ident == "RpcResult" { @@ -106,7 +107,7 @@ impl RpcDescription { // The type alias `RpcResult` is modified to `Result`. let ret_ty = args.last_mut().unwrap(); - let err_ty = self.jrps_client_item(quote! { core::Error }); + let err_ty = self.jrps_client_item(quote! { core::client::Error }); quote! { core::result::Result<#ret_ty, #err_ty> } } else { @@ -117,7 +118,7 @@ impl RpcDescription { fn render_method(&self, method: &RpcMethod) -> Result { // `jsonrpsee::Error` - let jrps_error = self.jrps_client_item(quote! { core::Error }); + let jrps_error = self.jrps_client_item(quote! { core::client::Error }); // Rust method to invoke (e.g. `self.(...)`). let rust_method_name = &method.signature.sig.ident; // List of inputs to put into `Params` (e.g. `self.foo(<12, "baz">)`). @@ -127,7 +128,7 @@ impl RpcDescription { let rpc_method_name = self.rpc_identifier(&method.name); // Called method is either `request` or `notification`. - // `returns` represent the return type of the *rust method* (`Result< <..>, jsonrpsee::core::Error`). + // `returns` represent the return type of the *rust method* (`Result`). let (called_method, returns) = if let Some(returns) = &method.returns { let called_method = quote::format_ident!("request"); let returns = self.return_result_type(returns.clone()); @@ -160,8 +161,8 @@ impl RpcDescription { } fn render_sub(&self, sub: &RpcSubscription) -> Result { - // `jsonrpsee::core::Error` - let jrps_error = self.jrps_client_item(quote! { core::Error }); + // `jsonrpsee::core::ClientError` + let jrps_error = self.jrps_client_item(quote! { core::client::Error }); // Rust method to invoke (e.g. `self.(...)`). let rust_method_name = &sub.signature.sig.ident; // List of inputs to put into `Params` (e.g. `self.foo(<12, "baz">)`). diff --git a/proc-macros/tests/ui/correct/custom_ret_types.rs b/proc-macros/tests/ui/correct/custom_ret_types.rs index 011fe9f845..8f89b55392 100644 --- a/proc-macros/tests/ui/correct/custom_ret_types.rs +++ b/proc-macros/tests/ui/correct/custom_ret_types.rs @@ -2,7 +2,7 @@ use std::net::SocketAddr; -use jsonrpsee::core::{async_trait, Error, Serialize}; +use jsonrpsee::core::{async_trait, ClientError, Serialize}; use jsonrpsee::proc_macros::rpc; use jsonrpsee::server::{IntoResponse, ServerBuilder}; use jsonrpsee::types::ResponsePayload; @@ -87,7 +87,7 @@ impl RpcServer for RpcServerImpl { // TODO: https://github.com/paritytech/jsonrpsee/issues/1067 // -// The client accepts only return types that are `Result`. +// The client accepts only return types that are `Result`. #[rpc(client, namespace = "foo")] pub trait RpcClient { #[method(name = "async_method1")] @@ -144,9 +144,9 @@ async fn main() { assert_method2(error); } -fn assert_method1(error: Error) { +fn assert_method1(error: ClientError) { let get_error_object = |err| match err { - jsonrpsee::core::Error::Call(object) => object, + ClientError::Call(object) => object, _ => panic!("wrong error kind: {:?}", err), }; @@ -156,9 +156,9 @@ fn assert_method1(error: Error) { assert!(error_object.data().is_none()); } -fn assert_method2(error: Error) { +fn assert_method2(error: ClientError) { let get_error_object = |err| match err { - jsonrpsee::core::Error::Call(object) => object, + ClientError::Call(object) => object, _ => panic!("wrong error kind: {:?}", err), }; diff --git a/server/Cargo.toml b/server/Cargo.toml index 3996a01285..266497c840 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -16,7 +16,7 @@ publish = true [dependencies] futures-util = { version = "0.3.14", default-features = false, features = ["io", "async-await-macro"] } jsonrpsee-types = { workspace = true } -jsonrpsee-core = { workspace = true, features = ["server", "soketto", "http-helpers"] } +jsonrpsee-core = { workspace = true, features = ["server", "http-helpers"] } tracing = "0.1.34" serde = "1" serde_json = { version = "1", features = ["raw_value"] } @@ -25,10 +25,10 @@ tokio = { version = "1.16", features = ["net", "rt-multi-thread", "macros", "tim tokio-util = { version = "0.7", features = ["compat"] } tokio-stream = "0.1.7" hyper = { version = "0.14", features = ["server", "http1", "http2"] } -tower = { version = "0.4.13", features = ["util"] } +tower = "0.4.13" +thiserror = "1" route-recognizer = "0.3.1" http = "0.2.9" -thiserror = "1.0.44" pin-project = "1.1.3" [dev-dependencies] diff --git a/server/src/future.rs b/server/src/future.rs index f4f529653e..d187ef58e2 100644 --- a/server/src/future.rs +++ b/server/src/future.rs @@ -31,7 +31,6 @@ use std::sync::Arc; use std::task::{Context, Poll}; use futures_util::{Stream, StreamExt}; -use jsonrpsee_core::Error; use pin_project::pin_project; use tokio::sync::{watch, OwnedSemaphorePermit, Semaphore, TryAcquireError}; use tokio::time::Interval; @@ -61,6 +60,10 @@ impl StopHandle { } } +#[derive(Debug, Copy, Clone, thiserror::Error)] +#[error("The server is already stopped")] +pub struct AlreadyStoppedError; + /// Server handle. /// /// When all [`StopHandle`]'s have been `dropped` or `stop` has been called @@ -75,8 +78,8 @@ impl ServerHandle { } /// Tell the server to stop without waiting for the server to stop. - pub fn stop(&self) -> Result<(), Error> { - self.0.send(()).map_err(|_| Error::AlreadyStopped) + pub fn stop(&self) -> Result<(), AlreadyStoppedError> { + self.0.send(()).map_err(|_| AlreadyStoppedError) } /// Wait for the server to stop. diff --git a/server/src/lib.rs b/server/src/lib.rs index cf615fa28c..fe4029de11 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -41,6 +41,7 @@ pub mod middleware; mod tests; pub use future::{stop_channel, ConnectionGuard, ConnectionPermit, ServerHandle, StopHandle}; +pub use jsonrpsee_core::error::RegisterMethodError; pub use jsonrpsee_core::server::*; pub use jsonrpsee_core::{id_providers::*, traits::IdProvider}; pub use jsonrpsee_types as types; diff --git a/server/src/middleware/http/proxy_get_request.rs b/server/src/middleware/http/proxy_get_request.rs index 40acd11f5d..4964d1b0b3 100644 --- a/server/src/middleware/http/proxy_get_request.rs +++ b/server/src/middleware/http/proxy_get_request.rs @@ -31,7 +31,6 @@ use crate::transport::http; use hyper::header::{ACCEPT, CONTENT_TYPE}; use hyper::http::HeaderValue; use hyper::{Body, Method, Request, Response, Uri}; -use jsonrpsee_core::error::Error as RpcError; use jsonrpsee_types::{Id, RequestSer}; use std::error::Error; use std::future::Future; @@ -40,6 +39,11 @@ use std::sync::Arc; use std::task::{Context, Poll}; use tower::{Layer, Service}; +/// Error that occur if the specified path doesn't start with `/` +#[derive(Debug, thiserror::Error)] +#[error("ProxyGetRequestLayer path must start with `/`, got `{0}`")] +pub struct InvalidPath(String); + /// Layer that applies [`ProxyGetRequest`] which proxies the `GET /path` requests to /// specific RPC method calls and that strips the response. /// @@ -54,10 +58,10 @@ impl ProxyGetRequestLayer { /// Creates a new [`ProxyGetRequestLayer`]. /// /// See [`ProxyGetRequest`] for more details. - pub fn new(path: impl Into, method: impl Into) -> Result { + pub fn new(path: impl Into, method: impl Into) -> Result { let path = path.into(); if !path.starts_with('/') { - return Err(RpcError::Custom("ProxyGetRequestLayer path must start with `/`".to_string())); + return Err(InvalidPath(path)); } Ok(Self { path, method: method.into() }) @@ -96,9 +100,9 @@ impl ProxyGetRequest { /// /// The request `GET /path` is redirected to the provided method. /// Fails if the path does not start with `/`. - pub fn new(inner: S, path: &str, method: &str) -> Result { + pub fn new(inner: S, path: &str, method: &str) -> Result { if !path.starts_with('/') { - return Err(RpcError::Custom(format!("ProxyGetRequest path must start with `/`, got: {path}"))); + return Err(InvalidPath(path.to_string())); } Ok(Self { inner, path: Arc::from(path), method: Arc::from(method) }) diff --git a/server/src/server.rs b/server/src/server.rs index ed3a7cedf8..9cdf57ac84 100644 --- a/server/src/server.rs +++ b/server/src/server.rs @@ -49,7 +49,7 @@ use jsonrpsee_core::id_providers::RandomIntegerIdProvider; use jsonrpsee_core::server::helpers::{prepare_error, MethodResponseResult}; use jsonrpsee_core::server::{BatchResponseBuilder, BoundedSubscriptions, MethodResponse, MethodSink, Methods}; use jsonrpsee_core::traits::IdProvider; -use jsonrpsee_core::{Error, JsonRawValue, TEN_MB_SIZE_BYTES}; +use jsonrpsee_core::{JsonRawValue, TEN_MB_SIZE_BYTES}; use jsonrpsee_types::error::{ reject_too_big_batch_request, ErrorCode, BATCHES_NOT_SUPPORTED_CODE, BATCHES_NOT_SUPPORTED_MSG, @@ -91,8 +91,8 @@ impl std::fmt::Debug for Server Server { /// Returns socket address to which the server is bound. - pub fn local_addr(&self) -> Result { - self.listener.local_addr().map_err(Into::into) + pub fn local_addr(&self) -> std::io::Result { + self.listener.local_addr() } } @@ -857,7 +857,7 @@ impl Builder { /// } /// ``` /// - pub async fn build(self, addrs: impl ToSocketAddrs) -> Result, Error> { + pub async fn build(self, addrs: impl ToSocketAddrs) -> std::io::Result> { let listener = TcpListener::bind(addrs).await?; Ok(Server { @@ -894,7 +894,7 @@ impl Builder { pub fn build_from_tcp( self, listener: impl Into, - ) -> Result, Error> { + ) -> std::io::Result> { let listener = TcpListener::from_std(listener.into())?; Ok(Server { diff --git a/server/src/tests/http.rs b/server/src/tests/http.rs index 1f4d54dbd4..34b5e5ee00 100644 --- a/server/src/tests/http.rs +++ b/server/src/tests/http.rs @@ -26,9 +26,8 @@ use std::net::SocketAddr; -use crate::server::BatchRequestConfig; -use crate::{RpcModule, ServerBuilder, ServerHandle}; -use jsonrpsee_core::{Error, RpcResult}; +use crate::{BatchRequestConfig, RegisterMethodError, RpcModule, ServerBuilder, ServerHandle}; +use jsonrpsee_core::RpcResult; use jsonrpsee_test_utils::helpers::*; use jsonrpsee_test_utils::mocks::{Id, StatusCode}; use jsonrpsee_test_utils::TimeoutFutureExt; @@ -447,7 +446,7 @@ async fn can_register_modules() { let err = mod1.merge(mod2).unwrap_err(); - let expected_err = Error::MethodAlreadyRegistered(String::from("bla")); + let expected_err = RegisterMethodError::AlreadyRegistered(String::from("bla")); assert_eq!(err.to_string(), expected_err.to_string()); assert_eq!(mod1.method_names().count(), 2); } diff --git a/server/src/tests/shared.rs b/server/src/tests/shared.rs index 03d5b54268..9e37f3a8fb 100644 --- a/server/src/tests/shared.rs +++ b/server/src/tests/shared.rs @@ -1,6 +1,5 @@ use crate::tests::helpers::{init_logger, server_with_handles}; use hyper::StatusCode; -use jsonrpsee_core::Error; use jsonrpsee_test_utils::helpers::{http_request, ok_response, to_http_uri}; use jsonrpsee_test_utils::mocks::{Id, WebSocketTestClient, WebSocketTestError}; use jsonrpsee_test_utils::TimeoutFutureExt; @@ -19,7 +18,7 @@ async fn stop_works() { // First `unwrap` is timeout, second is `JoinHandle`'s one. // After server was stopped, attempt to stop it again should result in an error. - assert!(matches!(server_handle.stop(), Err(Error::AlreadyStopped))); + assert!(matches!(server_handle.stop(), Err(_))); } #[tokio::test] diff --git a/server/src/tests/ws.rs b/server/src/tests/ws.rs index 14d7324acb..3fbafed690 100644 --- a/server/src/tests/ws.rs +++ b/server/src/tests/ws.rs @@ -27,12 +27,12 @@ use std::num::NonZeroUsize; use std::time::Duration; -use crate::server::BatchRequestConfig; use crate::tests::helpers::{deser_call, init_logger, server_with_context}; use crate::types::SubscriptionId; +use crate::{BatchRequestConfig, RegisterMethodError}; use crate::{RpcModule, ServerBuilder}; use jsonrpsee_core::server::{SendTimeoutError, SubscriptionMessage}; -use jsonrpsee_core::{traits::IdProvider, Error}; +use jsonrpsee_core::traits::IdProvider; use jsonrpsee_test_utils::helpers::*; use jsonrpsee_test_utils::mocks::{Id, WebSocketTestClient, WebSocketTestError}; use jsonrpsee_test_utils::TimeoutFutureExt; @@ -439,7 +439,7 @@ async fn register_same_subscribe_unsubscribe_is_err() { assert!(matches!( module .register_subscription("subscribe_hello", "subscribe_hello", "subscribe_hello", |_, _, _| async { Ok(()) }), - Err(Error::SubscriptionNameConflict(_)) + Err(RegisterMethodError::SubscriptionNameConflict(_)) )); } @@ -509,7 +509,7 @@ async fn can_register_modules() { assert_eq!(mod1.method_names().count(), 2); let err = mod1.merge(mod2).unwrap_err(); - assert!(matches!(err, Error::MethodAlreadyRegistered(err) if err == "bla")); + assert!(matches!(err, RegisterMethodError::AlreadyRegistered(err) if err == "bla")); assert_eq!(mod1.method_names().count(), 2); } diff --git a/server/src/transport/ws.rs b/server/src/transport/ws.rs index e48488192f..09fd6d1a54 100644 --- a/server/src/transport/ws.rs +++ b/server/src/transport/ws.rs @@ -12,7 +12,6 @@ use futures_util::{Future, StreamExt, TryStreamExt}; use hyper::upgrade::Upgraded; use jsonrpsee_core::server::helpers::MethodSink; use jsonrpsee_core::server::{BoundedSubscriptions, Methods}; -use jsonrpsee_core::Error; use jsonrpsee_types::error::{reject_too_big_request, ErrorCode}; use jsonrpsee_types::Id; use soketto::connection::Error as SokettoError; @@ -33,12 +32,12 @@ enum Incoming { Pong, } -pub(crate) async fn send_message(sender: &mut Sender, response: String) -> Result<(), Error> { +pub(crate) async fn send_message(sender: &mut Sender, response: String) -> Result<(), SokettoError> { sender.send_text_owned(response).await?; sender.flush().await.map_err(Into::into) } -pub(crate) async fn send_ping(sender: &mut Sender) -> Result<(), Error> { +pub(crate) async fn send_ping(sender: &mut Sender) -> Result<(), SokettoError> { tracing::debug!(target: LOG_TARGET, "Send ping"); // Submit empty slice as "optional" parameter. let slice: &[u8] = &[]; diff --git a/tests/tests/helpers.rs b/tests/tests/helpers.rs index 0398d3ae86..71765b9c76 100644 --- a/tests/tests/helpers.rs +++ b/tests/tests/helpers.rs @@ -33,7 +33,6 @@ use std::time::Duration; use fast_socks5::client::Socks5Stream; use fast_socks5::server; use futures::{SinkExt, Stream, StreamExt}; -use jsonrpsee::core::Error; use jsonrpsee::server::middleware::http::ProxyGetRequestLayer; use jsonrpsee::server::{ PendingSubscriptionSink, RpcModule, Server, ServerBuilder, ServerHandle, SubscriptionMessage, TrySendError, @@ -94,7 +93,7 @@ pub async fn server_with_subscription_and_handle() -> (SocketAddr, ServerHandle) .register_subscription("subscribe_noop", "subscribe_noop", "unsubscribe_noop", |_, pending, _| async { let _sink = pending.accept().await?; tokio::time::sleep(Duration::from_secs(1)).await; - Err(Error::Custom("Server closed the stream because it was lazy".to_string()).into()) + Err("Server closed the stream because it was lazy".to_string().into()) }) .unwrap(); diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs index 38851a07e1..c40ecacf2d 100644 --- a/tests/tests/integration_tests.rs +++ b/tests/tests/integration_tests.rs @@ -40,10 +40,10 @@ use helpers::{ server_with_health_api, server_with_subscription, server_with_subscription_and_handle, }; use hyper::http::HeaderValue; -use jsonrpsee::core::client::{ClientT, IdKind, Subscription, SubscriptionClientT}; +use jsonrpsee::core::client::{ClientT, Error, IdKind, Subscription, SubscriptionClientT}; use jsonrpsee::core::params::{ArrayParams, BatchRequestBuilder}; use jsonrpsee::core::server::SubscriptionMessage; -use jsonrpsee::core::{Error, JsonValue}; +use jsonrpsee::core::JsonValue; use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::server::middleware::http::HostFilterLayer; use jsonrpsee::server::{ServerBuilder, ServerHandle}; diff --git a/tests/tests/metrics.rs b/tests/tests/metrics.rs index 788431f2d5..2a308f3578 100644 --- a/tests/tests/metrics.rs +++ b/tests/tests/metrics.rs @@ -29,6 +29,7 @@ mod helpers; use std::collections::HashMap; +use std::io; use std::net::SocketAddr; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -36,7 +37,7 @@ use std::time::Duration; use futures::future::BoxFuture; use futures::FutureExt; use helpers::init_logger; -use jsonrpsee::core::{async_trait, client::ClientT, Error}; +use jsonrpsee::core::{async_trait, client::ClientT, ClientError}; use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::proc_macros::rpc; use jsonrpsee::server::middleware::rpc::{RpcServiceBuilder, RpcServiceT}; @@ -122,7 +123,7 @@ fn test_module() -> RpcModule<()> { async fn websocket_server( module: RpcModule<()>, counter: Arc>, -) -> Result<(SocketAddr, ServerHandle), Error> { +) -> io::Result<(SocketAddr, ServerHandle)> { let rpc_middleware = RpcServiceBuilder::new().layer_fn(move |service| CounterMiddleware { service, counter: counter.clone() }); let server = Server::builder().set_rpc_middleware(rpc_middleware).build("127.0.0.1:0").await?; @@ -133,7 +134,7 @@ async fn websocket_server( Ok((addr, handle)) } -async fn http_server(module: RpcModule<()>, counter: Arc>) -> Result<(SocketAddr, ServerHandle), Error> { +async fn http_server(module: RpcModule<()>, counter: Arc>) -> io::Result<(SocketAddr, ServerHandle)> { let rpc_middleware = RpcServiceBuilder::new().layer_fn(move |service| CounterMiddleware { service, counter: counter.clone() }); let server = Server::builder().set_rpc_middleware(rpc_middleware).build("127.0.0.1:0").await?; @@ -157,7 +158,7 @@ async fn ws_server_logger() { let res: String = client.request("say_hello", rpc_params![]).await.unwrap(); assert_eq!(res, "hello"); - let res: Result = client.request("unknown_method", rpc_params![]).await; + let res: Result = client.request("unknown_method", rpc_params![]).await; assert!(res.is_err()); let res: String = client.request("say_hello", rpc_params![]).await.unwrap(); @@ -165,10 +166,10 @@ async fn ws_server_logger() { let res: String = client.request("say_hello", rpc_params![]).await.unwrap(); assert_eq!(res, "hello"); - let res: Result = client.request("unknown_method", rpc_params![]).await; + let res: Result = client.request("unknown_method", rpc_params![]).await; assert!(res.is_err()); - let res: Result = client.request("err", rpc_params![]).await; + let res: Result = client.request("err", rpc_params![]).await; assert!(res.is_err()); { @@ -197,7 +198,7 @@ async fn http_server_logger() { let res: String = client.request("say_hello", rpc_params![]).await.unwrap(); assert_eq!(res, "hello"); - let res: Result = client.request("unknown_method", rpc_params![]).await; + let res: Result = client.request("unknown_method", rpc_params![]).await; assert!(res.is_err()); let res: String = client.request("say_hello", rpc_params![]).await.unwrap(); @@ -205,10 +206,10 @@ async fn http_server_logger() { let res: String = client.request("say_hello", rpc_params![]).await.unwrap(); assert_eq!(res, "hello"); - let res: Result = client.request("unknown_method", rpc_params![]).await; + let res: Result = client.request("unknown_method", rpc_params![]).await; assert!(res.is_err()); - let res: Result = client.request("err", rpc_params![]).await; + let res: Result = client.request("err", rpc_params![]).await; assert!(res.is_err()); { diff --git a/tests/tests/proc_macros.rs b/tests/tests/proc_macros.rs index 2cda9d4da2..721b010e2f 100644 --- a/tests/tests/proc_macros.rs +++ b/tests/tests/proc_macros.rs @@ -33,8 +33,7 @@ mod helpers; use std::net::SocketAddr; use helpers::init_logger; -use jsonrpsee::core::client::ClientT; -use jsonrpsee::core::{client::SubscriptionClientT, Error}; +use jsonrpsee::core::client::{ClientT, Error, SubscriptionClientT}; use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::rpc_params; use jsonrpsee::server::ServerBuilder; diff --git a/tests/tests/rpc_module.rs b/tests/tests/rpc_module.rs index 58b859827a..65ba98424b 100644 --- a/tests/tests/rpc_module.rs +++ b/tests/tests/rpc_module.rs @@ -33,7 +33,6 @@ use std::time::Duration; use futures::StreamExt; use helpers::{init_logger, pipe_from_stream_and_drop}; -use jsonrpsee::core::error::Error; use jsonrpsee::core::EmptyServerParams; use jsonrpsee::core::{server::*, RpcResult}; use jsonrpsee::types::error::{ErrorCode, ErrorObject, INVALID_PARAMS_MSG, PARSE_ERROR_CODE}; @@ -117,7 +116,7 @@ async fn calling_method_without_server() { let err = module.call::<_, EmptyServerParams>("foo", (false,)).await.unwrap_err(); assert!(matches!( err, - Error::Call(err) if err.code() == ErrorCode::InvalidParams.code() && err.message() == INVALID_PARAMS_MSG && err.data().unwrap().get().contains("invalid type: boolean `false`, expected u16 at line 1 column 6") + MethodsError::JsonRpc(err) if err.code() == ErrorCode::InvalidParams.code() && err.message() == INVALID_PARAMS_MSG && err.data().unwrap().get().contains("invalid type: boolean `false`, expected u16 at line 1 column 6") )); // Call async method with params and context @@ -211,7 +210,7 @@ async fn calling_method_without_server_using_proc_macro() { let err = module.call::<_, EmptyServerParams>("rebel", (Gun { shoots: true }, false)).await.unwrap_err(); assert!(matches!(err, - Error::Call(err) if err.data().unwrap().get().contains("invalid type: boolean `false`, expected a map at line 1 column 5") && + MethodsError::JsonRpc(err) if err.data().unwrap().get().contains("invalid type: boolean `false`, expected a map at line 1 column 5") && err.code() == ErrorCode::InvalidParams.code() && err.message() == INVALID_PARAMS_MSG )); @@ -230,7 +229,7 @@ async fn calling_method_without_server_using_proc_macro() { // Call async method with option which should `Err`. let err = module.call::<_, Option>("can_have_options", vec![2]).await.unwrap_err(); assert!(matches!(err, - Error::Call(err) if err.message() == "too big number" + MethodsError::JsonRpc(err) if err.message() == "too big number" )); } @@ -252,7 +251,7 @@ async fn subscribing_without_server() { tokio::time::sleep(std::time::Duration::from_millis(500)).await; } - Err(Error::Custom("closed successfully".into()).into()) + Err("closed successfully".into()) }) .unwrap(); @@ -336,7 +335,7 @@ async fn subscribing_without_server_bad_params() { let sub = module.subscribe_unbounded("my_sub", EmptyServerParams::new()).await.unwrap_err(); assert!( - matches!(sub, Error::Call(e) if e.data().unwrap().get().contains("invalid length 0, expected an array of length 1 at line 1 column 2") && e.code() == ErrorCode::InvalidParams.code() + matches!(sub, MethodsError::JsonRpc(e) if e.data().unwrap().get().contains("invalid length 0, expected an array of length 1 at line 1 column 2") && e.code() == ErrorCode::InvalidParams.code() && e.message() == INVALID_PARAMS_MSG ) ); @@ -412,7 +411,9 @@ async fn rejected_subscription_without_server() { .unwrap(); let sub_err = module.subscribe_unbounded("my_sub", EmptyServerParams::new()).await.unwrap_err(); - assert!(matches!(sub_err, Error::Call(e) if e.message().contains("rejected") && e.code() == PARSE_ERROR_CODE)); + assert!( + matches!(sub_err, MethodsError::JsonRpc(e) if e.message().contains("rejected") && e.code() == PARSE_ERROR_CODE) + ); } #[tokio::test]