diff --git a/Cargo.toml b/Cargo.toml index 371543da63..5c87c28fc8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ "jsonrpsee", "tests", "types", - "utils", + "core", "ws-client", "ws-server", "proc-macros", diff --git a/benches/bench.rs b/benches/bench.rs index e7ad6c3847..dc4617531d 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -1,16 +1,12 @@ +use std::sync::Arc; + use criterion::*; use futures_util::future::join_all; use helpers::{SUB_METHOD_NAME, UNSUB_METHOD_NAME}; -use jsonrpsee::{ - http_client::HttpClientBuilder, - types::traits::SubscriptionClient, - types::{ - traits::Client, - v2::{Id, ParamsSer, RequestSer}, - }, - ws_client::WsClientBuilder, -}; -use std::sync::Arc; +use jsonrpsee::core::client::{Client, SubscriptionClient}; +use jsonrpsee::http_client::HttpClientBuilder; +use jsonrpsee::types::{Id, ParamsSer, RequestSer}; +use jsonrpsee::ws_client::WsClientBuilder; use tokio::runtime::Runtime as TokioRuntime; mod helpers; diff --git a/benches/helpers.rs b/benches/helpers.rs index dcd3de7394..da6b2ac0af 100644 --- a/benches/helpers.rs +++ b/benches/helpers.rs @@ -26,10 +26,11 @@ pub async fn http_server(handle: tokio::runtime::Handle) -> (String, jsonrpc_htt /// Run jsonrpc WebSocket server for benchmarks. #[cfg(feature = "jsonrpc-crate")] pub async fn ws_server(handle: tokio::runtime::Handle) -> (String, jsonrpc_ws_server::Server) { + use std::sync::atomic::{AtomicU64, Ordering}; + use jsonrpc_pubsub::{PubSubHandler, Session, Subscriber, SubscriptionId}; use jsonrpc_ws_server::jsonrpc_core::*; use jsonrpc_ws_server::*; - use std::sync::atomic::{AtomicU64, Ordering}; const ID: AtomicU64 = AtomicU64::new(0); diff --git a/utils/Cargo.toml b/core/Cargo.toml similarity index 69% rename from utils/Cargo.toml rename to core/Cargo.toml index bc6d7b2779..77fd26206f 100644 --- a/utils/Cargo.toml +++ b/core/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "jsonrpsee-utils" +name = "jsonrpsee-core" version = "0.6.0" authors = ["Parity Technologies "] description = "Utilities for jsonrpsee" @@ -7,38 +7,36 @@ edition = "2018" license = "MIT" [dependencies] +anyhow = "1" arrayvec = "0.7.1" +async-trait = "0.1" beef = { version = "0.5.1", features = ["impl_serde"] } -thiserror = { version = "1", optional = true } -futures-channel = { version = "0.3.14", default-features = false, optional = true } +thiserror = "1" +futures-channel = { version = "0.3.14", default-features = false } futures-util = { version = "0.3.14", default-features = false, optional = true } -hyper = { version = "0.14.10", default-features = false, features = ["stream"], optional = true } -jsonrpsee-types = { path = "../types", version = "0.6.0", optional = true } +hyper = { version = "0.14.10", default-features = false, features = ["stream"] } +jsonrpsee-types = { path = "../types", version = "0.6.0" } tracing = { version = "0.1", optional = true } rustc-hash = { version = "1", optional = true } rand = { version = "0.8", optional = true } -serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } -serde_json = { version = "1", features = ["raw_value"], optional = true } +serde = { version = "1.0", default-features = false, features = ["derive"] } +serde_json = { version = "1", features = ["raw_value"] } +soketto = "0.7.1" parking_lot = { version = "0.11", optional = true } tokio = { version = "1.8", features = ["rt"], optional = true } [features] default = [] -http-helpers = ["hyper", "futures-util", "jsonrpsee-types"] +http-helpers = ["futures-util"] server = [ - "thiserror", - "futures-channel", "futures-util", - "jsonrpsee-types", "rustc-hash", - "serde", - "serde_json", "tracing", "parking_lot", "rand", "tokio", ] -client = ["jsonrpsee-types"] +client = ["futures-util"] [dev-dependencies] serde_json = "1.0" diff --git a/types/src/client.rs b/core/src/client.rs similarity index 75% rename from types/src/client.rs rename to core/src/client.rs index c149bb481c..268bdac18a 100644 --- a/types/src/client.rs +++ b/core/src/client.rs @@ -24,21 +24,101 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::{error::SubscriptionClosed, v2::SubscriptionId, Error}; -use core::marker::PhantomData; -use futures_channel::{mpsc, oneshot}; -use futures_util::{ - future::FutureExt, - sink::SinkExt, - stream::{Stream, StreamExt}, -}; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_json::Value as JsonValue; +//! Shared utilities for `jsonrpsee` clients. use std::pin::Pin; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; use std::task; +use crate::error::{Error, SubscriptionClosed}; +use async_trait::async_trait; +use core::marker::PhantomData; +use futures_channel::{mpsc, oneshot}; +use futures_util::future::FutureExt; +use futures_util::sink::SinkExt; +use futures_util::stream::{Stream, StreamExt}; +use jsonrpsee_types::{ParamsSer, SubscriptionId}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde_json::Value as JsonValue; + +#[doc(hidden)] +pub mod __reexports { + pub use crate::to_json_value; + pub use jsonrpsee_types::ParamsSer; +} + +/// [JSON-RPC](https://www.jsonrpc.org/specification) client interface that can make requests and notifications. +#[async_trait] +pub trait Client { + /// Send a [notification request](https://www.jsonrpc.org/specification#notification) + async fn notification<'a>(&self, method: &'a str, params: Option>) -> Result<(), Error>; + + /// Send a [method call request](https://www.jsonrpc.org/specification#request_object). + async fn request<'a, R>(&self, method: &'a str, params: Option>) -> Result + where + R: DeserializeOwned; + + /// Send a [batch request](https://www.jsonrpc.org/specification#batch). + /// + /// The response to batch are returned in the same order as it was inserted in the batch. + /// + /// Returns `Ok` if all requests in the batch were answered successfully. + /// Returns `Error` if any of the requests in batch fails. + async fn batch_request<'a, R>(&self, batch: Vec<(&'a str, Option>)>) -> Result, Error> + where + R: DeserializeOwned + Default + Clone; +} + +/// [JSON-RPC](https://www.jsonrpc.org/specification) client interface that can make requests, notifications and subscriptions. +#[async_trait] +pub trait SubscriptionClient: Client { + /// Initiate a subscription by performing a JSON-RPC method call where the server responds with + /// a `Subscription ID` that is used to fetch messages on that subscription, + /// + /// The `subscribe_method` and `params` are used to ask for the subscription towards the + /// server. + /// + /// The params may be used as input for the subscription for the server to process. + /// + /// The `unsubscribe_method` is used to close the subscription + /// + /// The `Notif` param is a generic type to receive generic subscriptions, see [`Subscription`] for further + /// documentation. + async fn subscribe<'a, Notif>( + &self, + subscribe_method: &'a str, + params: Option>, + unsubscribe_method: &'a str, + ) -> Result, Error> + where + Notif: DeserializeOwned; + + /// Register a method subscription, this is used to filter only server notifications that a user is interested in. + /// + /// The `Notif` param is a generic type to receive generic subscriptions, see [`Subscription`] for further + /// documentation. + async fn subscribe_to_method<'a, Notif>(&self, method: &'a str) -> Result, Error> + where + Notif: DeserializeOwned; +} + +#[macro_export] +/// Convert the given values to a [`jsonrpsee_types::ParamsSer`] as expected by a jsonrpsee Client (http or websocket). +macro_rules! rpc_params { + ($($param:expr),*) => { + { + let mut __params = vec![]; + $( + __params.push($crate::client::__reexports::to_json_value($param).expect("json serialization is infallible; qed.")); + )* + Some($crate::client::__reexports::ParamsSer::Array(__params)) + } + }; + () => { + None + } +} + /// Subscription kind #[derive(Debug)] #[non_exhaustive] diff --git a/core/src/error.rs b/core/src/error.rs new file mode 100644 index 0000000000..eab468ecd9 --- /dev/null +++ b/core/src/error.rs @@ -0,0 +1,255 @@ +// 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. + +use std::fmt; + +use jsonrpsee_types::error::CallError; +use serde::{Deserialize, Serialize}; + +/// Convenience type for displaying errors. +#[derive(Clone, Debug, PartialEq)] +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)) + } +} + +// NOTE(niklasad1): this `From` impl is a bit opinionated to regard all generic errors as `CallError`. +// In practice this should be the most common use case for users of this library. +impl From for Error { + fn from(err: anyhow::Error) -> Self { + Error::Call(CallError::Failed(err)) + } +} + +/// Error type. +#[derive(Debug, thiserror::Error)] +pub enum Error { + /// Error that occurs when a call failed. + #[error("Server call failed: {0}")] + Call(#[from] CallError), + /// Networking error or error on the low-level protocol layer. + #[error("Networking or low-level protocol error: {0}")] + Transport(#[source] anyhow::Error), + /// JSON-RPC request error. + #[error("JSON-RPC request error: {0:?}")] + Request(String), + /// Frontend/backend channel error. + #[error("Frontend/backend channel error: {0}")] + Internal(#[from] futures_channel::mpsc::SendError), + /// 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(String), + /// 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("Invalid request ID")] + 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), + /// Subscription got closed. + #[error("Subscription closed: {0:?}")] + SubscriptionClosed(SubscriptionClosed), + /// 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 `set_allowed_origins` was empty + #[error("Must set at least one allowed value for the {0} header")] + EmptyAllowList(&'static str), + /// Failed to execute a method because a resource was already at capacity + #[error("Resource at capacity: {0}")] + ResourceAtCapacity(&'static str), + /// Failed to register a resource due to a name conflict + #[error("Resource name already taken: {0}")] + ResourceNameAlreadyTaken(&'static str), + /// Failed to initialize resources for a method at startup + #[error("Resource name `{0}` not found for method `{1}`")] + ResourceNameNotFoundForMethod(&'static str, &'static str), + /// Trying to claim resources for a method execution, but the method resources have not been initialized + #[error("Method `{0}` has uninitialized resources")] + UninitializedMethod(Box), + /// Failed to register a resource due to a maximum number of resources already registered + #[error("Maximum number of resources reached")] + MaxResourcesReached, + /// Custom error. + #[error("Custom error: {0}")] + Custom(String), + /// Not implemented for HTTP clients. + #[error("Not implemented")] + HttpNotImplemented, +} + +impl Error { + /// Create `Error::CallError` from a generic error. + /// Useful if you don't care about specific JSON-RPC error code and + /// just wants to return your custom error type. + pub fn to_call_error(err: E) -> Self + where + E: std::error::Error + Send + Sync + 'static, + { + Error::Call(CallError::from_std_error(err)) + } +} + +/// A type with a special `subscription_closed` field to detect that +/// a subscription has been closed to distinguish valid items produced +/// by the server on the subscription stream from an error. +/// +/// This is included in the `result field` of the SubscriptionResponse +/// when an error is reported by the server. +#[derive(Deserialize, Serialize, Debug, PartialEq)] +#[serde(deny_unknown_fields)] +pub struct SubscriptionClosed { + reason: SubscriptionClosedReason, +} + +impl From for SubscriptionClosed { + fn from(reason: SubscriptionClosedReason) -> Self { + Self::new(reason) + } +} + +impl SubscriptionClosed { + /// Create a new [`SubscriptionClosed`]. + pub fn new(reason: SubscriptionClosedReason) -> Self { + Self { reason } + } + + /// Get the close reason. + pub fn close_reason(&self) -> &SubscriptionClosedReason { + &self.reason + } +} + +/// A type to represent when a subscription gets closed +/// by either the server or client side. +#[derive(Deserialize, Serialize, Debug, PartialEq)] +pub enum SubscriptionClosedReason { + /// The subscription was closed by calling the unsubscribe method. + Unsubscribed, + /// The client closed the connection. + ConnectionReset, + /// The server closed the subscription, providing a description of the reason as a `String`. + Server(String), +} + +/// Generic transport error. +#[derive(Debug, thiserror::Error)] +pub enum GenericTransportError { + /// Request was too large. + #[error("The request was too big")] + TooLarge, + /// Malformed request + #[error("Malformed request")] + Malformed, + /// Concrete transport error. + #[error("Transport error: {0}")] + Inner(T), +} + +impl From for Error { + fn from(io_err: std::io::Error) -> Error { + Error::Transport(io_err.into()) + } +} + +impl From for Error { + fn from(handshake_err: soketto::handshake::Error) -> Error { + Error::Transport(handshake_err.into()) + } +} + +impl From for Error { + fn from(conn_err: soketto::connection::Error) -> Error { + Error::Transport(conn_err.into()) + } +} + +impl From for Error { + fn from(hyper_err: hyper::Error) -> Error { + Error::Transport(hyper_err.into()) + } +} + +#[cfg(test)] +mod tests { + use super::{SubscriptionClosed, SubscriptionClosedReason}; + + #[test] + fn subscription_closed_ser_deser_works() { + let items: Vec<(&str, SubscriptionClosed)> = vec![ + (r#"{"reason":"Unsubscribed"}"#, SubscriptionClosedReason::Unsubscribed.into()), + (r#"{"reason":"ConnectionReset"}"#, SubscriptionClosedReason::ConnectionReset.into()), + (r#"{"reason":{"Server":"hoho"}}"#, SubscriptionClosedReason::Server("hoho".into()).into()), + ]; + + for (s, d) in items { + let dsr: SubscriptionClosed = serde_json::from_str(s).unwrap(); + assert_eq!(dsr, d); + let ser = serde_json::to_string(&d).unwrap(); + assert_eq!(ser, s); + } + } + + #[test] + fn subscription_closed_deny_unknown_field() { + let ser = r#"{"reason":"Unsubscribed","deny":1}"#; + assert!(serde_json::from_str::(ser).is_err()); + } +} diff --git a/utils/src/http_helpers.rs b/core/src/http_helpers.rs similarity index 98% rename from utils/src/http_helpers.rs rename to core/src/http_helpers.rs index 4fc43abbf6..a8daaea3a7 100644 --- a/utils/src/http_helpers.rs +++ b/core/src/http_helpers.rs @@ -26,8 +26,8 @@ //! Utility methods relying on hyper +use crate::error::GenericTransportError; use futures_util::stream::StreamExt; -use jsonrpsee_types::error::GenericTransportError; /// Read a data from a [`hyper::Body`] and return the data if it is valid and within the allowed size range. /// diff --git a/utils/src/lib.rs b/core/src/lib.rs similarity index 67% rename from utils/src/lib.rs rename to core/src/lib.rs index b6cde330e4..396d7c1301 100644 --- a/utils/src/lib.rs +++ b/core/src/lib.rs @@ -28,6 +28,15 @@ #![warn(missing_docs, missing_debug_implementations, unreachable_pub)] +/// Error type. +pub mod error; + +/// Traits +pub mod traits; + +/// Middleware trait and implementation. +pub mod middleware; + /// Shared hyper helpers. #[cfg(feature = "http-helpers")] pub mod http_helpers; @@ -39,3 +48,25 @@ pub mod server; /// Shared code for JSON-RPC clients. #[cfg(feature = "client")] pub mod client; + +pub use async_trait::async_trait; +pub use error::Error; + +/// JSON-RPC result. +pub type RpcResult = std::result::Result; + +/// Re-exports for proc-macro library to not require any additional +/// dependencies to be explicitly added on the client side. +#[doc(hidden)] +pub mod __reexports { + pub use async_trait::async_trait; + pub use serde; + pub use serde_json; +} + +pub use beef::Cow; +pub use serde::{de::DeserializeOwned, Serialize}; +pub use serde_json::{ + to_value as to_json_value, value::to_raw_value as to_json_raw_value, value::RawValue as JsonRawValue, + Value as JsonValue, +}; diff --git a/types/src/middleware.rs b/core/src/middleware.rs similarity index 100% rename from types/src/middleware.rs rename to core/src/middleware.rs diff --git a/utils/src/server/helpers.rs b/core/src/server/helpers.rs similarity index 94% rename from utils/src/server/helpers.rs rename to core/src/server/helpers.rs index add3d87d7d..5ef8b56248 100644 --- a/utils/src/server/helpers.rs +++ b/core/src/server/helpers.rs @@ -24,25 +24,25 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use std::io; + +use crate::{to_json_raw_value, Error}; use futures_channel::mpsc; use futures_util::stream::StreamExt; -use jsonrpsee_types::error::{CallError, Error}; -use jsonrpsee_types::to_json_raw_value; -use jsonrpsee_types::v2::error::{OVERSIZED_RESPONSE_CODE, OVERSIZED_RESPONSE_MSG}; -use jsonrpsee_types::v2::{ - error::{CALL_EXECUTION_FAILED_CODE, UNKNOWN_ERROR_CODE}, - ErrorCode, ErrorObject, Id, InvalidRequest, Response, RpcError, +use jsonrpsee_types::error::{ + CallError, ErrorCode, ErrorObject, ErrorResponse, CALL_EXECUTION_FAILED_CODE, OVERSIZED_RESPONSE_CODE, + OVERSIZED_RESPONSE_MSG, UNKNOWN_ERROR_CODE, }; +use jsonrpsee_types::{Id, InvalidRequest, Response}; use serde::Serialize; -use std::io; - /// Bounded writer that allows writing at most `max_len` bytes. /// /// ``` -/// use jsonrpsee_utils::server::helpers::BoundedWriter; /// use std::io::Write; /// +/// use jsonrpsee_core::server::helpers::BoundedWriter; +/// /// let mut writer = BoundedWriter::new(10); /// (&mut writer).write("hello".as_bytes()).unwrap(); /// assert_eq!(std::str::from_utf8(&writer.into_bytes()).unwrap(), "hello"); @@ -143,7 +143,7 @@ impl MethodSink { /// Send a JSON-RPC error to the client pub fn send_error(&self, id: Id, error: ErrorObject) -> bool { - let json = match serde_json::to_string(&RpcError::new(error, id)) { + let json = match serde_json::to_string(&ErrorResponse::new(error, id)) { Ok(json) => json, Err(err) => { tracing::error!("Error serializing error message: {:?}", err); diff --git a/utils/src/server/mod.rs b/core/src/server/mod.rs similarity index 100% rename from utils/src/server/mod.rs rename to core/src/server/mod.rs diff --git a/utils/src/server/resource_limiting.rs b/core/src/server/resource_limiting.rs similarity index 97% rename from utils/src/server/resource_limiting.rs rename to core/src/server/resource_limiting.rs index 3127176e5e..98e77fba8d 100644 --- a/utils/src/server/resource_limiting.rs +++ b/core/src/server/resource_limiting.rs @@ -52,7 +52,7 @@ //! `#[method]` attribute: //! //! ``` -//! # use jsonrpsee::{types::RpcResult, proc_macros::rpc}; +//! # use jsonrpsee::{core::RpcResult, proc_macros::rpc}; //! # //! #[rpc(server)] //! pub trait Rpc { @@ -67,7 +67,7 @@ //! Alternatively, you can use the `resource` method when creating a module manually without the help of the macro: //! //! ``` -//! # use jsonrpsee::{RpcModule, types::RpcResult}; +//! # use jsonrpsee::{RpcModule, core::RpcResult}; //! # //! # fn main() -> RpcResult<()> { //! # @@ -91,8 +91,8 @@ use std::sync::Arc; +use crate::Error; use arrayvec::ArrayVec; -use jsonrpsee_types::error::Error; use parking_lot::Mutex; // The number of kinds of resources that can be used for limiting. diff --git a/utils/src/server/rpc_module.rs b/core/src/server/rpc_module.rs similarity index 79% rename from utils/src/server/rpc_module.rs rename to core/src/server/rpc_module.rs index 77ad7225a0..f4b74787e1 100644 --- a/utils/src/server/rpc_module.rs +++ b/core/src/server/rpc_module.rs @@ -24,32 +24,27 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use std::collections::hash_map::Entry; +use std::fmt::{self, Debug}; +use std::future::Future; +use std::ops::{Deref, DerefMut}; +use std::sync::Arc; + +use crate::error::{Error, SubscriptionClosed, SubscriptionClosedReason}; use crate::server::helpers::MethodSink; use crate::server::resource_limiting::{ResourceGuard, ResourceTable, ResourceVec, Resources}; +use crate::to_json_raw_value; +use crate::traits::ToRpcParams; use beef::Cow; use futures_channel::{mpsc, oneshot}; use futures_util::{future::BoxFuture, FutureExt, StreamExt}; -use jsonrpsee_types::error::{SubscriptionClosed, SubscriptionClosedReason}; -use jsonrpsee_types::to_json_raw_value; -use jsonrpsee_types::v2::error::{invalid_subscription_err, CALL_EXECUTION_FAILED_CODE}; +use jsonrpsee_types::error::{invalid_subscription_err, ErrorCode, CALL_EXECUTION_FAILED_CODE}; use jsonrpsee_types::{ - error::Error, - traits::ToRpcParams, - v2::{ - ErrorCode, Id, Params, Request, Response, SubscriptionId as RpcSubscriptionId, SubscriptionPayload, - SubscriptionResponse, - }, - DeserializeOwned, + Id, Params, Request, Response, SubscriptionId as RpcSubscriptionId, SubscriptionPayload, SubscriptionResponse, }; - use parking_lot::Mutex; use rustc_hash::FxHashMap; -use serde::Serialize; -use std::collections::hash_map::Entry; -use std::fmt::{self, Debug}; -use std::future::Future; -use std::ops::{Deref, DerefMut}; -use std::sync::Arc; +use serde::{de::DeserializeOwned, Serialize}; /// A `MethodCallback` is an RPC endpoint, callable with a standard JSON-RPC request, /// implemented as a function pointer to a `Fn` function taking four arguments: @@ -394,7 +389,8 @@ impl Methods { /// ``` /// #[tokio::main] /// async fn main() { - /// use jsonrpsee::{RpcModule, types::v2::Response}; + /// use jsonrpsee::RpcModule; + /// use jsonrpsee::types::Response; /// use futures_util::StreamExt; /// /// let mut module = RpcModule::new(()); @@ -630,7 +626,7 @@ impl RpcModule { /// /// ```no_run /// - /// use jsonrpsee_utils::server::rpc_module::RpcModule; + /// use jsonrpsee_core::server::rpc_module::RpcModule; /// /// let mut ctx = RpcModule::new(99_usize); /// ctx.register_subscription("sub", "notif_name", "unsub", |params, mut sink, ctx| { @@ -862,9 +858,7 @@ impl Subscription { /// # Panics /// /// If the decoding the value as `T` fails. - pub async fn next( - &mut self, - ) -> Option), Error>> { + pub async fn next(&mut self) -> Option), Error>> { let raw = self.rx.next().await?; let res = match serde_json::from_str::>(&raw) { Ok(r) => Ok((r.params.result, r.params.subscription.into_owned())), @@ -882,215 +876,3 @@ impl Drop for Subscription { self.close(); } } - -#[cfg(test)] -mod tests { - use super::*; - use jsonrpsee_types::{v2, EmptyParams}; - use serde::Deserialize; - use std::collections::HashMap; - - #[test] - fn rpc_modules_with_different_contexts_can_be_merged() { - let cx = Vec::::new(); - let mut mod1 = RpcModule::new(cx); - mod1.register_method("bla with Vec context", |_: Params, _| Ok(())).unwrap(); - let mut mod2 = RpcModule::new(String::new()); - mod2.register_method("bla with String context", |_: Params, _| Ok(())).unwrap(); - - mod1.merge(mod2).unwrap(); - - assert!(mod1.method("bla with Vec context").is_some()); - assert!(mod1.method("bla with String context").is_some()); - } - - #[test] - fn rpc_context_modules_can_register_subscriptions() { - let cx = (); - let mut cxmodule = RpcModule::new(cx); - let _subscription = cxmodule.register_subscription("hi", "hi", "goodbye", |_, _, _| Ok(())); - - assert!(cxmodule.method("hi").is_some()); - assert!(cxmodule.method("goodbye").is_some()); - } - - #[test] - fn rpc_register_alias() { - let mut module = RpcModule::new(()); - - module.register_method("hello_world", |_: Params, _| Ok(())).unwrap(); - module.register_alias("hello_foobar", "hello_world").unwrap(); - - assert!(module.method("hello_world").is_some()); - assert!(module.method("hello_foobar").is_some()); - } - - #[tokio::test] - async fn calling_method_without_server() { - // Call sync method with no params - let mut module = RpcModule::new(()); - module.register_method("boo", |_: Params, _| Ok(String::from("boo!"))).unwrap(); - - let res: String = module.call("boo", EmptyParams::new()).await.unwrap(); - assert_eq!(&res, "boo!"); - - // Call sync method with params - module - .register_method("foo", |params, _| { - let n: u16 = params.one()?; - Ok(n * 2) - }) - .unwrap(); - let res: u64 = module.call("foo", [3_u64]).await.unwrap(); - assert_eq!(res, 6); - - // Call sync method with bad param - let err = module.call::<_, ()>("foo", (false,)).await.unwrap_err(); - assert!( - matches!(err, Error::Request(err) if err == r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"invalid type: boolean `false`, expected u16 at line 1 column 6"},"id":0}"#) - ); - - // Call async method with params and context - struct MyContext; - impl MyContext { - fn roo(&self, things: Vec) -> u16 { - things.iter().sum::().into() - } - } - let mut module = RpcModule::new(MyContext); - module - .register_async_method("roo", |params, ctx| { - let ns: Vec = params.parse().expect("valid params please"); - async move { Ok(ctx.roo(ns)) } - }) - .unwrap(); - let res: u64 = module.call("roo", [12, 13]).await.unwrap(); - assert_eq!(res, 25); - } - - #[tokio::test] - async fn calling_method_without_server_using_proc_macro() { - use jsonrpsee::{proc_macros::rpc, types::async_trait}; - // Setup - #[derive(Debug, Deserialize, Serialize)] - #[allow(unreachable_pub)] - pub struct Gun { - shoots: bool, - } - - #[derive(Debug, Deserialize, Serialize)] - #[allow(unreachable_pub)] - pub struct Beverage { - ice: bool, - } - - #[rpc(server)] - pub trait Cool { - /// Sync method, no params. - #[method(name = "rebel_without_cause")] - fn rebel_without_cause(&self) -> Result; - - /// Sync method. - #[method(name = "rebel")] - fn rebel(&self, gun: Gun, map: HashMap) -> Result; - - /// Async method. - #[method(name = "revolution")] - async fn can_have_any_name(&self, beverage: Beverage, some_bytes: Vec) -> Result; - } - - struct CoolServerImpl; - - #[async_trait] - impl CoolServer for CoolServerImpl { - fn rebel_without_cause(&self) -> Result { - Ok(false) - } - - fn rebel(&self, gun: Gun, map: HashMap) -> Result { - Ok(format!("{} {:?}", map.values().len(), gun)) - } - - async fn can_have_any_name(&self, beverage: Beverage, some_bytes: Vec) -> Result { - Ok(format!("drink: {:?}, phases: {:?}", beverage, some_bytes)) - } - } - let module = CoolServerImpl.into_rpc(); - - // Call sync method with no params - let res: bool = module.call("rebel_without_cause", EmptyParams::new()).await.unwrap(); - assert!(!res); - - // Call sync method with params - let res: String = module.call("rebel", (Gun { shoots: true }, HashMap::::default())).await.unwrap(); - assert_eq!(&res, "0 Gun { shoots: true }"); - - // Call sync method with bad params - let err = module.call::<_, ()>("rebel", (Gun { shoots: true }, false)).await.unwrap_err(); - assert!(matches!( - err, - Error::Request(err) if err == r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"invalid type: boolean `false`, expected a map at line 1 column 5"},"id":0}"# - )); - - // Call async method with params and context - let result: String = module.call("revolution", (Beverage { ice: true }, vec![1, 2, 3])).await.unwrap(); - assert_eq!(&result, "drink: Beverage { ice: true }, phases: [1, 2, 3]"); - } - - #[tokio::test] - async fn subscribing_without_server() { - let mut module = RpcModule::new(()); - module - .register_subscription("my_sub", "my_sub", "my_unsub", |_, mut sink, _| { - let mut stream_data = vec!['0', '1', '2']; - std::thread::spawn(move || { - while let Some(letter) = stream_data.pop() { - tracing::debug!("This is your friendly subscription sending data."); - if let Err(Error::SubscriptionClosed(_)) = sink.send(&letter) { - return; - } - std::thread::sleep(std::time::Duration::from_millis(500)); - } - }); - Ok(()) - }) - .unwrap(); - - let mut my_sub = module.subscribe("my_sub", EmptyParams::new()).await.unwrap(); - for i in (0..=2).rev() { - let (val, id) = my_sub.next::().await.unwrap().unwrap(); - assert_eq!(val, std::char::from_digit(i, 10).unwrap()); - assert_eq!(id, v2::params::SubscriptionId::Num(my_sub.subscription_id())); - } - - let sub_err = my_sub.next::().await.unwrap().unwrap_err(); - - // The subscription is now closed by the server. - assert!(matches!(sub_err, Error::SubscriptionClosed(_))); - } - - #[tokio::test] - async fn close_test_subscribing_without_server() { - let mut module = RpcModule::new(()); - module - .register_subscription("my_sub", "my_sub", "my_unsub", |_, mut sink, _| { - std::thread::spawn(move || loop { - if let Err(Error::SubscriptionClosed(_)) = sink.send(&"lo") { - return; - } - std::thread::sleep(std::time::Duration::from_millis(500)); - }); - Ok(()) - }) - .unwrap(); - - let mut my_sub = module.subscribe("my_sub", EmptyParams::new()).await.unwrap(); - let (val, id) = my_sub.next::().await.unwrap().unwrap(); - assert_eq!(&val, "lo"); - assert_eq!(id, v2::params::SubscriptionId::Num(my_sub.subscription_id())); - - // close the subscription to ensure it doesn't return any items. - my_sub.close(); - assert!(matches!(my_sub.next::().await, None)); - } -} diff --git a/types/src/traits.rs b/core/src/traits.rs similarity index 53% rename from types/src/traits.rs rename to core/src/traits.rs index 042c0e24d6..3bb03cb9d6 100644 --- a/types/src/traits.rs +++ b/core/src/traits.rs @@ -24,68 +24,9 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::v2::ParamsSer; -use crate::{Error, Subscription}; -use async_trait::async_trait; -use serde::de::DeserializeOwned; use serde::Serialize; use serde_json::value::RawValue; -/// [JSON-RPC](https://www.jsonrpc.org/specification) client interface that can make requests and notifications. -#[async_trait] -pub trait Client { - /// Send a [notification request](https://www.jsonrpc.org/specification#notification) - async fn notification<'a>(&self, method: &'a str, params: Option>) -> Result<(), Error>; - - /// Send a [method call request](https://www.jsonrpc.org/specification#request_object). - async fn request<'a, R>(&self, method: &'a str, params: Option>) -> Result - where - R: DeserializeOwned; - - /// Send a [batch request](https://www.jsonrpc.org/specification#batch). - /// - /// The response to batch are returned in the same order as it was inserted in the batch. - /// - /// Returns `Ok` if all requests in the batch were answered successfully. - /// Returns `Error` if any of the requests in batch fails. - async fn batch_request<'a, R>(&self, batch: Vec<(&'a str, Option>)>) -> Result, Error> - where - R: DeserializeOwned + Default + Clone; -} - -/// [JSON-RPC](https://www.jsonrpc.org/specification) client interface that can make requests, notifications and subscriptions. -#[async_trait] -pub trait SubscriptionClient: Client { - /// Initiate a subscription by performing a JSON-RPC method call where the server responds with - /// a `Subscription ID` that is used to fetch messages on that subscription, - /// - /// The `subscribe_method` and `params` are used to ask for the subscription towards the - /// server. - /// - /// The params may be used as input for the subscription for the server to process. - /// - /// The `unsubscribe_method` is used to close the subscription - /// - /// The `Notif` param is a generic type to receive generic subscriptions, see [`Subscription`] for further - /// documentation. - async fn subscribe<'a, Notif>( - &self, - subscribe_method: &'a str, - params: Option>, - unsubscribe_method: &'a str, - ) -> Result, Error> - where - Notif: DeserializeOwned; - - /// Register a method subscription, this is used to filter only server notifications that a user is interested in. - /// - /// The `Notif` param is a generic type to receive generic subscriptions, see [`Subscription`] for further - /// documentation. - async fn subscribe_to_method<'a, Notif>(&self, method: &'a str) -> Result, Error> - where - Notif: DeserializeOwned; -} - /// Marker trait for types that can be serialized as JSON array/sequence. /// /// If your type isn't a sequence, for example `String`, `usize` or similar diff --git a/examples/http.rs b/examples/http.rs index 8c4d2b485d..c2dc66603d 100644 --- a/examples/http.rs +++ b/examples/http.rs @@ -24,14 +24,13 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use jsonrpsee::{ - http_client::HttpClientBuilder, - http_server::{HttpServerBuilder, HttpServerHandle, RpcModule}, - rpc_params, - types::traits::Client, -}; use std::net::SocketAddr; +use jsonrpsee::core::client::Client; +use jsonrpsee::http_client::HttpClientBuilder; +use jsonrpsee::http_server::{HttpServerBuilder, HttpServerHandle, RpcModule}; +use jsonrpsee::rpc_params; + #[tokio::main] async fn main() -> anyhow::Result<()> { tracing_subscriber::FmtSubscriber::builder() diff --git a/examples/middleware_http.rs b/examples/middleware_http.rs index c734ca5d47..124c7612e0 100644 --- a/examples/middleware_http.rs +++ b/examples/middleware_http.rs @@ -24,14 +24,13 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use jsonrpsee::{ - http_client::HttpClientBuilder, - http_server::{HttpServerBuilder, HttpServerHandle, RpcModule}, - types::{middleware, traits::Client}, -}; use std::net::SocketAddr; use std::time::Instant; +use jsonrpsee::core::{client::Client, middleware}; +use jsonrpsee::http_client::HttpClientBuilder; +use jsonrpsee::http_server::{HttpServerBuilder, HttpServerHandle, RpcModule}; + #[derive(Clone)] struct Timings; diff --git a/examples/middleware_ws.rs b/examples/middleware_ws.rs index 19d262a2bd..aac3556453 100644 --- a/examples/middleware_ws.rs +++ b/examples/middleware_ws.rs @@ -24,14 +24,13 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use jsonrpsee::{ - types::{middleware, traits::Client}, - ws_client::WsClientBuilder, - ws_server::{RpcModule, WsServerBuilder}, -}; use std::net::SocketAddr; use std::time::Instant; +use jsonrpsee::core::{client::Client, middleware}; +use jsonrpsee::ws_client::WsClientBuilder; +use jsonrpsee::ws_server::{RpcModule, WsServerBuilder}; + #[derive(Clone)] struct Timings; diff --git a/examples/multi_middleware.rs b/examples/multi_middleware.rs index 82674ad9ea..6b411115e5 100644 --- a/examples/multi_middleware.rs +++ b/examples/multi_middleware.rs @@ -26,16 +26,15 @@ //! Example showing how to add multiple middlewares to the same server. -use jsonrpsee::{ - rpc_params, - types::{middleware, traits::Client}, - ws_client::WsClientBuilder, - ws_server::{RpcModule, WsServerBuilder}, -}; use std::net::SocketAddr; use std::process::Command; use std::time::Instant; +use jsonrpsee::core::{client::Client, middleware}; +use jsonrpsee::rpc_params; +use jsonrpsee::ws_client::WsClientBuilder; +use jsonrpsee::ws_server::{RpcModule, WsServerBuilder}; + /// Example middleware to measure call execution time. #[derive(Clone)] struct Timings; diff --git a/examples/proc_macro.rs b/examples/proc_macro.rs index b658a85bed..685bf9448c 100644 --- a/examples/proc_macro.rs +++ b/examples/proc_macro.rs @@ -24,14 +24,13 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use jsonrpsee::{ - proc_macros::rpc, - types::{async_trait, error::Error, Subscription}, - ws_client::WsClientBuilder, - ws_server::{SubscriptionSink, WsServerBuilder, WsServerHandle}, -}; use std::net::SocketAddr; +use jsonrpsee::core::{async_trait, client::Subscription, Error}; +use jsonrpsee::proc_macros::rpc; +use jsonrpsee::ws_client::WsClientBuilder; +use jsonrpsee::ws_server::{SubscriptionSink, WsServerBuilder, WsServerHandle}; + type ExampleHash = [u8; 32]; type ExampleStorageKey = Vec; diff --git a/examples/ws.rs b/examples/ws.rs index 641a5cff5d..a6a5032173 100644 --- a/examples/ws.rs +++ b/examples/ws.rs @@ -24,13 +24,12 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use jsonrpsee::{ - types::traits::Client, - ws_client::WsClientBuilder, - ws_server::{RpcModule, WsServerBuilder}, -}; use std::net::SocketAddr; +use jsonrpsee::core::client::Client; +use jsonrpsee::ws_client::WsClientBuilder; +use jsonrpsee::ws_server::{RpcModule, WsServerBuilder}; + #[tokio::main] async fn main() -> anyhow::Result<()> { tracing_subscriber::FmtSubscriber::builder() diff --git a/examples/ws_sub_with_params.rs b/examples/ws_sub_with_params.rs index 1275168d64..90b65129a2 100644 --- a/examples/ws_sub_with_params.rs +++ b/examples/ws_sub_with_params.rs @@ -24,14 +24,13 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use jsonrpsee::{ - rpc_params, - types::traits::SubscriptionClient, - ws_client::WsClientBuilder, - ws_server::{RpcModule, WsServerBuilder}, -}; use std::net::SocketAddr; +use jsonrpsee::core::client::SubscriptionClient; +use jsonrpsee::rpc_params; +use jsonrpsee::ws_client::WsClientBuilder; +use jsonrpsee::ws_server::{RpcModule, WsServerBuilder}; + #[tokio::main] async fn main() -> anyhow::Result<()> { tracing_subscriber::FmtSubscriber::builder() diff --git a/examples/ws_subscription.rs b/examples/ws_subscription.rs index 4af06c9fa0..1ce239c2ef 100644 --- a/examples/ws_subscription.rs +++ b/examples/ws_subscription.rs @@ -24,14 +24,14 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use jsonrpsee::{ - rpc_params, - types::{traits::SubscriptionClient, Error, Subscription}, - ws_client::WsClientBuilder, - ws_server::{RpcModule, WsServerBuilder}, -}; use std::net::SocketAddr; +use jsonrpsee::core::client::{Subscription, SubscriptionClient}; +use jsonrpsee::core::Error; +use jsonrpsee::rpc_params; +use jsonrpsee::ws_client::WsClientBuilder; +use jsonrpsee::ws_server::{RpcModule, WsServerBuilder}; + const NUM_SUBSCRIPTION_RESPONSES: usize = 5; #[tokio::main] diff --git a/http-client/Cargo.toml b/http-client/Cargo.toml index 9526eb17e2..a615b4c5f3 100644 --- a/http-client/Cargo.toml +++ b/http-client/Cargo.toml @@ -15,7 +15,7 @@ rustc-hash = "1" hyper = { version = "0.14.10", features = ["client", "http1", "http2", "tcp"] } hyper-rustls = { version = "0.23", optional = true } jsonrpsee-types = { path = "../types", version = "0.6.0" } -jsonrpsee-utils = { path = "../utils", version = "0.6.0", features = ["client", "http-helpers"] } +jsonrpsee-core = { path = "../core", version = "0.6.0", features = ["client", "http-helpers"] } serde = { version = "1.0", default-features = false, features = ["derive"] } serde_json = "1.0" thiserror = "1.0" diff --git a/http-client/src/client.rs b/http-client/src/client.rs index 878b22dc69..364c7fb616 100644 --- a/http-client/src/client.rs +++ b/http-client/src/client.rs @@ -24,16 +24,16 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use std::sync::Arc; +use std::time::Duration; + use crate::transport::HttpTransportClient; -use crate::types::{ - traits::{Client, SubscriptionClient}, - v2::{Id, NotificationSer, ParamsSer, RequestSer, Response, RpcError}, - CertificateStore, Error, RequestIdManager, Subscription, TEN_MB_SIZE_BYTES, -}; +use crate::types::{ErrorResponse, Id, NotificationSer, ParamsSer, RequestSer, Response, TEN_MB_SIZE_BYTES}; use async_trait::async_trait; +use jsonrpsee_core::client::{CertificateStore, Client, RequestIdManager, Subscription, SubscriptionClient}; +use jsonrpsee_core::Error; use rustc_hash::FxHashMap; use serde::de::DeserializeOwned; -use std::{sync::Arc, time::Duration}; /// Http Client Builder. #[derive(Debug)] @@ -137,7 +137,7 @@ impl Client for HttpClient { let response: Response<_> = match serde_json::from_slice(&body) { Ok(response) => response, Err(_) => { - let err: RpcError = serde_json::from_slice(&body).map_err(Error::ParseError)?; + let err: ErrorResponse = serde_json::from_slice(&body).map_err(Error::ParseError)?; return Err(Error::Request(err.to_string())); } }; @@ -176,7 +176,7 @@ impl Client for HttpClient { }; let rps: Vec> = - serde_json::from_slice(&body).map_err(|_| match serde_json::from_slice::(&body) { + serde_json::from_slice(&body).map_err(|_| match serde_json::from_slice::(&body) { Ok(e) => Error::Request(e.to_string()), Err(e) => Error::ParseError(e), })?; diff --git a/http-client/src/tests.rs b/http-client/src/tests.rs index d70bbc0218..c14fabea1a 100644 --- a/http-client/src/tests.rs +++ b/http-client/src/tests.rs @@ -24,16 +24,16 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::types::{ - traits::Client, - v2::{ErrorCode, ErrorObject, ParamsSer, RpcError}, - Error, JsonValue, -}; +use crate::types::error::{ErrorCode, ErrorObject, ErrorResponse}; +use crate::types::ParamsSer; use crate::HttpClientBuilder; +use jsonrpsee_core::client::Client; +use jsonrpsee_core::rpc_params; +use jsonrpsee_core::Error; use jsonrpsee_test_utils::helpers::*; use jsonrpsee_test_utils::mocks::Id; use jsonrpsee_test_utils::TimeoutFutureExt; -use jsonrpsee_utils::rpc_params; +use serde_json::value::Value as JsonValue; #[tokio::test] async fn method_call_works() { @@ -149,7 +149,7 @@ async fn run_request_with_response(response: String) -> Result fn assert_jsonrpc_error_response(err: Error, exp: ErrorObject) { match &err { Error::Request(e) => { - let this: RpcError = serde_json::from_str(e).unwrap(); + let this: ErrorResponse = serde_json::from_str(e).unwrap(); assert_eq!(this.error, exp); } e => panic!("Expected error: \"{}\", got: {:?}", err, e), diff --git a/http-client/src/transport.rs b/http-client/src/transport.rs index f0288854ba..692c821eb0 100644 --- a/http-client/src/transport.rs +++ b/http-client/src/transport.rs @@ -6,11 +6,11 @@ // that we need to be guaranteed that hyper doesn't re-use an existing connection if we ever reset // the JSON-RPC request id to a value that might have already been used. -use crate::types::error::GenericTransportError; use hyper::client::{Client, HttpConnector}; use hyper::Uri; -use jsonrpsee_types::CertificateStore; -use jsonrpsee_utils::http_helpers; +use jsonrpsee_core::client::CertificateStore; +use jsonrpsee_core::error::GenericTransportError; +use jsonrpsee_core::http_helpers; use thiserror::Error; const CONTENT_TYPE_JSON: &str = "application/json"; diff --git a/http-server/Cargo.toml b/http-server/Cargo.toml index 652ef6d0bc..80d1d51493 100644 --- a/http-server/Cargo.toml +++ b/http-server/Cargo.toml @@ -14,7 +14,7 @@ hyper = { version = "0.14.10", features = ["server", "http1", "http2", "tcp"] } futures-channel = "0.3.14" futures-util = { version = "0.3.14", default-features = false } jsonrpsee-types = { path = "../types", version = "0.6.0" } -jsonrpsee-utils = { path = "../utils", version = "0.6.0", features = ["server", "http-helpers"] } +jsonrpsee-core = { path = "../core", version = "0.6.0", features = ["server", "http-helpers"] } globset = "0.4" lazy_static = "1.4" tracing = "0.1" diff --git a/http-server/src/access_control/cors.rs b/http-server/src/access_control/cors.rs index 4340a7016e..3739077d0b 100644 --- a/http-server/src/access_control/cors.rs +++ b/http-server/src/access_control/cors.rs @@ -26,11 +26,12 @@ //! CORS handling utility functions +use std::collections::HashSet; +use std::{fmt, ops}; + use crate::access_control::hosts::{Host, Port}; use crate::access_control::matcher::{Matcher, Pattern}; use lazy_static::lazy_static; -use std::collections::HashSet; -use std::{fmt, ops}; use unicase::Ascii; /// Origin Protocol @@ -322,9 +323,10 @@ lazy_static! { #[cfg(test)] mod tests { + use std::iter; + use super::*; use crate::access_control::hosts::Host; - use std::iter; #[test] fn should_parse_origin() { diff --git a/http-server/src/access_control/matcher.rs b/http-server/src/access_control/matcher.rs index c20ed1d654..9479070ef1 100644 --- a/http-server/src/access_control/matcher.rs +++ b/http-server/src/access_control/matcher.rs @@ -24,8 +24,9 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use globset::{GlobBuilder, GlobMatcher}; use std::{fmt, hash}; + +use globset::{GlobBuilder, GlobMatcher}; use tracing::warn; /// Pattern that can be matched to string. diff --git a/http-server/src/access_control/mod.rs b/http-server/src/access_control/mod.rs index b749bfff4e..8c0fd0d76b 100644 --- a/http-server/src/access_control/mod.rs +++ b/http-server/src/access_control/mod.rs @@ -30,12 +30,10 @@ pub(crate) mod cors; pub(crate) mod hosts; mod matcher; -use crate::types::Error; - use cors::{AccessControlAllowHeaders, AccessControlAllowOrigin}; use hosts::{AllowHosts, Host}; use hyper::header; -use jsonrpsee_utils::http_helpers; +use jsonrpsee_core::{http_helpers, Error}; /// Define access on control on HTTP layer. #[derive(Clone, Debug)] diff --git a/http-server/src/lib.rs b/http-server/src/lib.rs index 81530cd814..d96c19ed51 100644 --- a/http-server/src/lib.rs +++ b/http-server/src/lib.rs @@ -41,8 +41,8 @@ pub use access_control::{ hosts::{AllowHosts, DomainsValidation, Host}, AccessControl, AccessControlBuilder, }; +pub use jsonrpsee_core::server::rpc_module::RpcModule; pub use jsonrpsee_types as types; -pub use jsonrpsee_utils::server::rpc_module::RpcModule; pub use server::{Builder as HttpServerBuilder, Server as HttpServer, ServerHandle as HttpServerHandle}; pub use tracing; diff --git a/http-server/src/response.rs b/http-server/src/response.rs index 0cd24cdb6d..8dbdb20ec6 100644 --- a/http-server/src/response.rs +++ b/http-server/src/response.rs @@ -26,14 +26,15 @@ //! Contains common builders for hyper responses. -use crate::types::v2::{ErrorCode, Id, RpcError}; +use crate::types::error::{ErrorCode, ErrorResponse}; +use crate::types::Id; const JSON: &str = "application/json; charset=utf-8"; const TEXT: &str = "text/plain"; /// Create a response for json internal error. pub fn internal_error() -> hyper::Response { - let error = serde_json::to_string(&RpcError::new(ErrorCode::InternalError.into(), Id::Null)) + let error = serde_json::to_string(&ErrorResponse::new(ErrorCode::InternalError.into(), Id::Null)) .expect("built from known-good data; qed"); from_template(hyper::StatusCode::INTERNAL_SERVER_ERROR, error, JSON) @@ -73,7 +74,7 @@ pub fn invalid_allow_headers() -> hyper::Response { /// Create a json response for oversized requests (413) pub fn too_large() -> hyper::Response { - let error = serde_json::to_string(&RpcError::new(ErrorCode::OversizedRequest.into(), Id::Null)) + let error = serde_json::to_string(&ErrorResponse::new(ErrorCode::OversizedRequest.into(), Id::Null)) .expect("built from known-good data; qed"); from_template(hyper::StatusCode::PAYLOAD_TOO_LARGE, error, JSON) @@ -81,7 +82,7 @@ pub fn too_large() -> hyper::Response { /// Create a json response for empty or malformed requests (400) pub fn malformed() -> hyper::Response { - let error = serde_json::to_string(&RpcError::new(ErrorCode::ParseError.into(), Id::Null)) + let error = serde_json::to_string(&ErrorResponse::new(ErrorCode::ParseError.into(), Id::Null)) .expect("built from known-good data; qed"); from_template(hyper::StatusCode::BAD_REQUEST, error, JSON) diff --git a/http-server/src/server.rs b/http-server/src/server.rs index 7ecb9f7025..5e51f06c37 100644 --- a/http-server/src/server.rs +++ b/http-server/src/server.rs @@ -24,36 +24,28 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use std::cmp; +use std::future::Future; +use std::net::{SocketAddr, TcpListener, ToSocketAddrs}; +use std::pin::Pin; +use std::task::{Context, Poll}; + use crate::{response, AccessControl}; use futures_channel::mpsc; use futures_util::{future::join_all, stream::StreamExt, FutureExt}; -use hyper::{ - server::{conn::AddrIncoming, Builder as HyperBuilder}, - service::{make_service_fn, service_fn}, - Error as HyperError, -}; -use jsonrpsee_types::{ - error::{Error, GenericTransportError}, - middleware::Middleware, - v2::{ErrorCode, Id, Notification, Request}, - TEN_MB_SIZE_BYTES, -}; -use jsonrpsee_utils::http_helpers::read_body; -use jsonrpsee_utils::server::{ - helpers::{collect_batch_response, prepare_error, MethodSink}, - resource_limiting::Resources, - rpc_module::{MethodResult, Methods}, -}; - +use hyper::server::{conn::AddrIncoming, Builder as HyperBuilder}; +use hyper::service::{make_service_fn, service_fn}; +use hyper::Error as HyperError; +use jsonrpsee_core::error::{Error, GenericTransportError}; +use jsonrpsee_core::http_helpers::read_body; +use jsonrpsee_core::middleware::Middleware; +use jsonrpsee_core::server::helpers::{collect_batch_response, prepare_error, MethodSink}; +use jsonrpsee_core::server::resource_limiting::Resources; +use jsonrpsee_core::server::rpc_module::{MethodResult, Methods}; +use jsonrpsee_types::error::ErrorCode; +use jsonrpsee_types::{Id, Notification, Request, TEN_MB_SIZE_BYTES}; use serde_json::value::RawValue; use socket2::{Domain, Socket, Type}; -use std::{ - cmp, - future::Future, - net::{SocketAddr, TcpListener, ToSocketAddrs}, - pin::Pin, - task::{Context, Poll}, -}; /// Builder to create JSON-RPC HTTP server. #[derive(Debug)] @@ -88,13 +80,14 @@ impl Builder { } impl Builder { - /// Add a middleware to the builder [`Middleware`](../jsonrpsee_types/middleware/trait.Middleware.html). + /// Add a middleware to the builder [`Middleware`](../jsonrpsee_core/middleware/trait.Middleware.html). /// /// ``` - /// use jsonrpsee_types::middleware::Middleware; - /// use jsonrpsee_http_server::HttpServerBuilder; /// use std::time::Instant; /// + /// use jsonrpsee_core::middleware::Middleware; + /// use jsonrpsee_http_server::HttpServerBuilder; + /// /// #[derive(Clone)] /// struct MyMiddleware; /// diff --git a/http-server/src/tests.rs b/http-server/src/tests.rs index 6fa15b26e7..40d043f335 100644 --- a/http-server/src/tests.rs +++ b/http-server/src/tests.rs @@ -29,9 +29,9 @@ use std::net::SocketAddr; use std::time::Duration; -use crate::types::error::{CallError, Error}; +use crate::types::error::CallError; use crate::{server::ServerHandle, HttpServerBuilder, RpcModule}; - +use jsonrpsee_core::Error; use jsonrpsee_test_utils::helpers::*; use jsonrpsee_test_utils::mocks::{Id, StatusCode, TestContext}; use jsonrpsee_test_utils::TimeoutFutureExt; diff --git a/jsonrpsee/Cargo.toml b/jsonrpsee/Cargo.toml index 87bc2c0247..ee6d143c02 100644 --- a/jsonrpsee/Cargo.toml +++ b/jsonrpsee/Cargo.toml @@ -17,14 +17,14 @@ jsonrpsee-http-server = { path = "../http-server", version = "0.6.0", package = jsonrpsee-ws-client = { path = "../ws-client", version = "0.6.0", package = "jsonrpsee-ws-client", optional = true } jsonrpsee-ws-server = { path = "../ws-server", version = "0.6.0", package = "jsonrpsee-ws-server", optional = true } jsonrpsee-proc-macros = { path = "../proc-macros", version = "0.6.0", package = "jsonrpsee-proc-macros", optional = true } -jsonrpsee-utils = { path = "../utils", version = "0.6.0", package = "jsonrpsee-utils", optional = true } +jsonrpsee-core = { path = "../core", version = "0.6.0", package = "jsonrpsee-core", optional = true } jsonrpsee-types = { path = "../types", version = "0.6.0", package = "jsonrpsee-types", optional = true } [features] -http-client = ["jsonrpsee-http-client", "jsonrpsee-types", "jsonrpsee-utils/client"] -http-server = ["jsonrpsee-http-server", "jsonrpsee-types", "jsonrpsee-utils"] -ws-client = ["jsonrpsee-ws-client", "jsonrpsee-types", "jsonrpsee-utils/client"] -ws-server = ["jsonrpsee-ws-server", "jsonrpsee-types", "jsonrpsee-utils"] +http-client = ["jsonrpsee-http-client", "jsonrpsee-types", "jsonrpsee-core/client"] +http-server = ["jsonrpsee-http-server", "jsonrpsee-types", "jsonrpsee-core"] +ws-client = ["jsonrpsee-ws-client", "jsonrpsee-types", "jsonrpsee-core/client"] +ws-server = ["jsonrpsee-ws-server", "jsonrpsee-types", "jsonrpsee-core"] macros = ["jsonrpsee-proc-macros", "jsonrpsee-types"] client = ["http-client", "ws-client"] diff --git a/jsonrpsee/src/lib.rs b/jsonrpsee/src/lib.rs index 5d2e97ca5f..098ecff7bd 100644 --- a/jsonrpsee/src/lib.rs +++ b/jsonrpsee/src/lib.rs @@ -54,7 +54,7 @@ pub use jsonrpsee_ws_client as ws_client; /// JSON-RPC client convenience macro to build params. #[cfg(any(feature = "http-client", feature = "ws-client"))] -pub use jsonrpsee_utils::rpc_params; +pub use jsonrpsee_core::rpc_params; /// JSON-RPC HTTP server. #[cfg(feature = "jsonrpsee-http-server")] @@ -74,10 +74,10 @@ pub use jsonrpsee_types as types; /// Set of RPC methods that can be mounted to the server. #[cfg(any(feature = "http-server", feature = "ws-server"))] -pub use jsonrpsee_utils::server::rpc_module::{RpcModule, SubscriptionSink}; +pub use jsonrpsee_core::server::rpc_module::{RpcModule, SubscriptionSink}; #[cfg(any(feature = "http-server", feature = "ws-server"))] -pub use jsonrpsee_utils as utils; +pub use jsonrpsee_core as core; #[cfg(feature = "http-server")] pub use http_server::tracing; diff --git a/proc-macros/src/attributes.rs b/proc-macros/src/attributes.rs index a143a7a873..7a4b49e4ac 100644 --- a/proc-macros/src/attributes.rs +++ b/proc-macros/src/attributes.rs @@ -24,8 +24,9 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use proc_macro2::{Span, TokenStream as TokenStream2, TokenTree}; use std::{fmt, iter}; + +use proc_macro2::{Span, TokenStream as TokenStream2, TokenTree}; use syn::parse::{Parse, ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{spanned::Spanned, Attribute, Error, LitInt, LitStr, Token}; diff --git a/proc-macros/src/helpers.rs b/proc-macros/src/helpers.rs index a1fd1a03eb..2df5bb5fa8 100644 --- a/proc-macros/src/helpers.rs +++ b/proc-macros/src/helpers.rs @@ -24,11 +24,12 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use std::collections::HashSet; + use crate::visitor::{FindAllParams, FindSubscriptionParams}; use proc_macro2::{Span, TokenStream as TokenStream2}; use proc_macro_crate::{crate_name, FoundCrate}; use quote::quote; -use std::collections::HashSet; use syn::{parse_quote, punctuated::Punctuated, visit::Visit, Token}; /// Search for client-side `jsonrpsee` in `Cargo.toml`. @@ -70,7 +71,7 @@ fn find_jsonrpsee_crate(http_name: &str, ws_name: &str) -> Result { @@ -103,17 +104,17 @@ pub(crate) fn generate_where_clause( if is_client { if visitor.input_params.contains(&ty.ident) { - bounds.push(parse_quote!(jsonrpsee::types::Serialize)) + bounds.push(parse_quote!(jsonrpsee::core::Serialize)) } if visitor.ret_params.contains(&ty.ident) || visitor.sub_params.contains(&ty.ident) { - bounds.push(parse_quote!(jsonrpsee::types::DeserializeOwned)) + bounds.push(parse_quote!(jsonrpsee::core::DeserializeOwned)) } } else { if visitor.input_params.contains(&ty.ident) { - bounds.push(parse_quote!(jsonrpsee::types::DeserializeOwned)) + bounds.push(parse_quote!(jsonrpsee::core::DeserializeOwned)) } if visitor.ret_params.contains(&ty.ident) || visitor.sub_params.contains(&ty.ident) { - bounds.push(parse_quote!(jsonrpsee::types::Serialize)) + bounds.push(parse_quote!(jsonrpsee::core::Serialize)) } } diff --git a/proc-macros/src/lib.rs b/proc-macros/src/lib.rs index 62d139353b..f56cb568cb 100644 --- a/proc-macros/src/lib.rs +++ b/proc-macros/src/lib.rs @@ -101,7 +101,7 @@ pub(crate) mod visitor; /// // Note that `subscription_sink` was added automatically. /// fn sub(&self, subscription_sink: SubscriptionSink); /// -/// fn into_rpc(self) -> Result { +/// fn into_rpc(self) -> Result { /// // Actual implementation stripped, but inside we will create /// // a module with one method and one subscription /// } @@ -206,7 +206,7 @@ pub(crate) mod visitor; /// /// // RPC is put into a separate module to clearly show names of generated entities. /// mod rpc_impl { -/// use jsonrpsee::{proc_macros::rpc, types::{async_trait, RpcResult}, ws_server::SubscriptionSink}; +/// use jsonrpsee::{proc_macros::rpc, core::async_trait, core::RpcResult, ws_server::SubscriptionSink}; /// /// // Generate both server and client implementations, prepend all the methods with `foo_` prefix. /// #[rpc(client, server, namespace = "foo")] diff --git a/proc-macros/src/render_client.rs b/proc-macros/src/render_client.rs index 8ec3020f29..6ba94cedd9 100644 --- a/proc-macros/src/render_client.rs +++ b/proc-macros/src/render_client.rs @@ -41,16 +41,16 @@ impl RpcDescription { let (impl_generics, type_generics, _) = self.trait_def.generics.split_for_impl(); let super_trait = if self.subscriptions.is_empty() { - quote! { #jsonrpsee::types::traits::Client } + quote! { #jsonrpsee::core::client::Client } } else { - quote! { #jsonrpsee::types::traits::SubscriptionClient } + quote! { #jsonrpsee::core::client::SubscriptionClient } }; let method_impls = self.methods.iter().map(|method| self.render_method(method)).collect::, _>>()?; let sub_impls = self.subscriptions.iter().map(|sub| self.render_sub(sub)).collect::, _>>()?; - let async_trait = self.jrps_client_item(quote! { types::__reexports::async_trait }); + let async_trait = self.jrps_client_item(quote! { core::__reexports::async_trait }); // Doc-comment to be associated with the client. let doc_comment = format!("Client implementation for the `{}` RPC API.", &self.trait_def.ident); @@ -71,7 +71,7 @@ impl RpcDescription { fn render_method(&self, method: &RpcMethod) -> Result { // `jsonrpsee::Error` - let jrps_error = self.jrps_client_item(quote! { types::Error }); + let jrps_error = self.jrps_client_item(quote! { core::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">)`). @@ -81,7 +81,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::Error`). + // `returns` represent the return type of the *rust method* (`Result< <..>, jsonrpsee::core::Error`). let (called_method, returns) = if let Some(returns) = &method.returns { let called_method = quote::format_ident!("request"); let returns = quote! { #returns }; @@ -112,8 +112,8 @@ impl RpcDescription { } fn render_sub(&self, sub: &RpcSubscription) -> Result { - // `jsonrpsee::Error` - let jrps_error = self.jrps_client_item(quote! { types::Error }); + // `jsonrpsee::core::Error` + let jrps_error = self.jrps_client_item(quote! { core::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">)`). @@ -125,7 +125,7 @@ impl RpcDescription { // `returns` represent the return type of the *rust method*, which is wrapped // into the `Subscription` object. - let sub_type = self.jrps_client_item(quote! { types::Subscription }); + let sub_type = self.jrps_client_item(quote! { core::client::Subscription }); let item = &sub.item; let returns = quote! { Result<#sub_type<#item>, #jrps_error> }; @@ -150,7 +150,7 @@ impl RpcDescription { signature: &syn::TraitItemMethod, ) -> TokenStream2 { if !params.is_empty() { - let serde_json = self.jrps_client_item(quote! { types::__reexports::serde_json }); + let serde_json = self.jrps_client_item(quote! { core::__reexports::serde_json }); let params = params.iter().map(|(param, _param_type)| { quote! { #serde_json::to_value(&#param)? } }); @@ -165,12 +165,12 @@ impl RpcDescription { quote! { (#param, #value) } }); quote! { - Some(types::v2::ParamsSer::Map( - std::collections::BTreeMap::<&str, #serde_json::Value>::from( - [#(#params),*] - ) + Some(types::ParamsSer::Map( + std::collections::BTreeMap::<&str, #serde_json::Value>::from( + [#(#params),*] ) ) + ) } } ParamKind::Array => { diff --git a/proc-macros/src/render_server.rs b/proc-macros/src/render_server.rs index 68d4b7e816..866e71210a 100644 --- a/proc-macros/src/render_server.rs +++ b/proc-macros/src/render_server.rs @@ -24,12 +24,13 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use std::collections::HashSet; + use super::RpcDescription; use crate::attributes::Resource; use crate::helpers::{generate_where_clause, is_option}; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{quote, quote_spanned}; -use std::collections::HashSet; impl RpcDescription { pub(super) fn render_server(&self) -> Result { @@ -39,7 +40,7 @@ impl RpcDescription { let method_impls = self.render_methods()?; let into_rpc_impl = self.render_into_rpc()?; - let async_trait = self.jrps_server_item(quote! { types::__reexports::async_trait }); + let async_trait = self.jrps_server_item(quote! { core::__reexports::async_trait }); // Doc-comment to be associated with the server. let doc_comment = format!("Server trait implementation for the `{}` RPC API.", &self.trait_def.ident); @@ -331,7 +332,7 @@ impl RpcDescription { let decode_map = { let generics = (0..params.len()).map(|n| quote::format_ident!("G{}", n)); - let serde = self.jrps_server_item(quote! { types::__reexports::serde }); + let serde = self.jrps_server_item(quote! { core::__reexports::serde }); let serde_crate = serde.to_string(); let fields = params.iter().zip(generics.clone()).map(|((name, _), ty)| { quote! { #name: #ty, } diff --git a/proc-macros/src/rpc_macro.rs b/proc-macros/src/rpc_macro.rs index 3a04e8b715..ad5d3a93e5 100644 --- a/proc-macros/src/rpc_macro.rs +++ b/proc-macros/src/rpc_macro.rs @@ -26,16 +26,14 @@ //! Declaration of the JSON RPC generator procedural macros. -use crate::{ - attributes::{ - optional, parse_param_kind, Aliases, Argument, AttributeMeta, MissingArgument, NameMapping, ParamKind, Resource, - }, - helpers::extract_doc_comments, -}; +use std::borrow::Cow; +use crate::attributes::{ + optional, parse_param_kind, Aliases, Argument, AttributeMeta, MissingArgument, NameMapping, ParamKind, Resource, +}; +use crate::helpers::extract_doc_comments; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use std::borrow::Cow; use syn::spanned::Spanned; use syn::{punctuated::Punctuated, Attribute, Token}; diff --git a/proc-macros/src/visitor.rs b/proc-macros/src/visitor.rs index 4f796a035f..c77ef9f993 100644 --- a/proc-macros/src/visitor.rs +++ b/proc-macros/src/visitor.rs @@ -25,10 +25,9 @@ // DEALINGS IN THE SOFTWARE. use std::collections::HashSet; -use syn::{ - visit::{self, Visit}, - Ident, -}; + +use syn::visit::{self, Visit}; +use syn::Ident; /// Visitor that parses generic type parameters from `syn::Type` by traversing the AST. /// A `syn::Type` can any type such as `Vec, T, Foo>>, usize or similar`. diff --git a/proc-macros/tests/ui/correct/alias_doesnt_use_namespace.rs b/proc-macros/tests/ui/correct/alias_doesnt_use_namespace.rs index 5ba32df63e..524697d5b0 100644 --- a/proc-macros/tests/ui/correct/alias_doesnt_use_namespace.rs +++ b/proc-macros/tests/ui/correct/alias_doesnt_use_namespace.rs @@ -1,4 +1,4 @@ -use jsonrpsee::{proc_macros::rpc, types::RpcResult}; +use jsonrpsee::{proc_macros::rpc, core::RpcResult}; #[rpc(client, server, namespace = "myapi")] pub trait Rpc { diff --git a/proc-macros/tests/ui/correct/basic.rs b/proc-macros/tests/ui/correct/basic.rs index 922c346410..f97095ae3f 100644 --- a/proc-macros/tests/ui/correct/basic.rs +++ b/proc-macros/tests/ui/correct/basic.rs @@ -1,14 +1,13 @@ //! Example of using proc macro to generate working client and server. -use jsonrpsee::{ - proc_macros::rpc, - rpc_params, - types::{async_trait, traits::Client, RpcResult}, - ws_client::*, - ws_server::{SubscriptionSink, WsServerBuilder}, -}; use std::net::SocketAddr; +use jsonrpsee::proc_macros::rpc; +use jsonrpsee::rpc_params; +use jsonrpsee::core::{async_trait, client::Client, RpcResult}; +use jsonrpsee::ws_client::*; +use jsonrpsee::ws_server::{SubscriptionSink, WsServerBuilder}; + #[rpc(client, server, namespace = "foo")] pub trait Rpc { #[method(name = "foo", aliases = ["fooAlias", "Other"])] diff --git a/proc-macros/tests/ui/correct/only_client.rs b/proc-macros/tests/ui/correct/only_client.rs index 23d3d1fd48..db68b7638b 100644 --- a/proc-macros/tests/ui/correct/only_client.rs +++ b/proc-macros/tests/ui/correct/only_client.rs @@ -1,6 +1,6 @@ //! Example of using proc macro to generate working client and server. -use jsonrpsee::{proc_macros::rpc, types::RpcResult}; +use jsonrpsee::{proc_macros::rpc, core::RpcResult}; #[rpc(client)] pub trait Rpc { diff --git a/proc-macros/tests/ui/correct/only_server.rs b/proc-macros/tests/ui/correct/only_server.rs index 819a326542..b82f7ddcf5 100644 --- a/proc-macros/tests/ui/correct/only_server.rs +++ b/proc-macros/tests/ui/correct/only_server.rs @@ -1,10 +1,9 @@ -use jsonrpsee::{ - proc_macros::rpc, - types::{async_trait, RpcResult}, - ws_server::{SubscriptionSink, WsServerBuilder}, -}; use std::net::SocketAddr; +use jsonrpsee::proc_macros::rpc; +use jsonrpsee::core::{async_trait, RpcResult}; +use jsonrpsee::ws_server::{SubscriptionSink, WsServerBuilder}; + #[rpc(server)] pub trait Rpc { #[method(name = "foo")] diff --git a/proc-macros/tests/ui/correct/param_kind.rs b/proc-macros/tests/ui/correct/param_kind.rs index 52c76ea7ac..efd50f2fe4 100644 --- a/proc-macros/tests/ui/correct/param_kind.rs +++ b/proc-macros/tests/ui/correct/param_kind.rs @@ -1,12 +1,10 @@ -use jsonrpsee::{ - proc_macros::rpc, - types::{async_trait, RpcResult}, - ws_client::*, - ws_server::WsServerBuilder, -}; - use std::net::SocketAddr; +use jsonrpsee::proc_macros::rpc; +use jsonrpsee::core::{async_trait, RpcResult}; +use jsonrpsee::ws_client::*; +use jsonrpsee::ws_server::WsServerBuilder; + #[rpc(client, server, namespace = "foo")] pub trait Rpc { #[method(name = "method_with_array_param", param_kind = array)] diff --git a/proc-macros/tests/ui/correct/parse_angle_brackets.rs b/proc-macros/tests/ui/correct/parse_angle_brackets.rs index 037e86c155..f9e75625f2 100644 --- a/proc-macros/tests/ui/correct/parse_angle_brackets.rs +++ b/proc-macros/tests/ui/correct/parse_angle_brackets.rs @@ -1,4 +1,4 @@ -use jsonrpsee::{proc_macros::rpc, types::RpcResult}; +use jsonrpsee::{proc_macros::rpc, core::RpcResult}; fn main() { #[rpc(server)] diff --git a/proc-macros/tests/ui/correct/rpc_deny_missing_docs.rs b/proc-macros/tests/ui/correct/rpc_deny_missing_docs.rs index cbb60b44b0..9575fa9a42 100644 --- a/proc-macros/tests/ui/correct/rpc_deny_missing_docs.rs +++ b/proc-macros/tests/ui/correct/rpc_deny_missing_docs.rs @@ -3,7 +3,7 @@ #![deny(missing_docs)] use jsonrpsee::proc_macros::rpc; -use jsonrpsee::types::RpcResult; +use jsonrpsee::core::RpcResult; #[rpc(client, server)] pub trait ApiWithDocumentation { diff --git a/proc-macros/tests/ui/incorrect/method/method_no_name.rs b/proc-macros/tests/ui/incorrect/method/method_no_name.rs index 7c9f102e4c..d1aa39a3fe 100644 --- a/proc-macros/tests/ui/incorrect/method/method_no_name.rs +++ b/proc-macros/tests/ui/incorrect/method/method_no_name.rs @@ -4,7 +4,7 @@ use jsonrpsee::proc_macros::rpc; #[rpc(client, server)] pub trait NoMethodName { #[method()] - async fn async_method(&self) -> jsonrpsee::types::RpcResult; + async fn async_method(&self) -> jsonrpsee::core::RpcResult; } fn main() {} diff --git a/proc-macros/tests/ui/incorrect/method/method_unexpected_field.rs b/proc-macros/tests/ui/incorrect/method/method_unexpected_field.rs index a88a0d73bd..57349114d1 100644 --- a/proc-macros/tests/ui/incorrect/method/method_unexpected_field.rs +++ b/proc-macros/tests/ui/incorrect/method/method_unexpected_field.rs @@ -4,7 +4,7 @@ use jsonrpsee::proc_macros::rpc; #[rpc(client, server)] pub trait UnexpectedField { #[method(name = "foo", magic = false)] - async fn async_method(&self) -> jsonrpsee::types::RpcResult; + async fn async_method(&self) -> jsonrpsee::core::RpcResult; } fn main() {} diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_assoc_items.rs b/proc-macros/tests/ui/incorrect/rpc/rpc_assoc_items.rs index 0b3ed3287e..e032a30a5a 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_assoc_items.rs +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_assoc_items.rs @@ -6,7 +6,7 @@ pub trait AssociatedConst { const WOO: usize; #[method(name = "foo")] - async fn async_method(&self) -> jsonrpsee::types::RpcResult; + async fn async_method(&self) -> jsonrpsee::core::RpcResult; } #[rpc(client, server)] @@ -14,7 +14,7 @@ pub trait AssociatedType { type Woo; #[method(name = "foo")] - async fn async_method(&self) -> jsonrpsee::types::RpcResult; + async fn async_method(&self) -> jsonrpsee::core::RpcResult; } fn main() {} diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_conflicting_alias.rs b/proc-macros/tests/ui/incorrect/rpc/rpc_conflicting_alias.rs index 3ba041a144..458a4bb961 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_conflicting_alias.rs +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_conflicting_alias.rs @@ -1,5 +1,5 @@ use jsonrpsee::proc_macros::rpc; -use jsonrpsee::types::RpcResult; +use jsonrpsee::core::RpcResult; #[rpc(client, server)] pub trait DuplicatedAlias { #[method(name = "foo", aliases = ["foo_dup", "foo_dup"])] diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_deprecated_method.rs b/proc-macros/tests/ui/incorrect/rpc/rpc_deprecated_method.rs index 79fdd01972..01938556d3 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_deprecated_method.rs +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_deprecated_method.rs @@ -3,14 +3,13 @@ // Treat warnings as errors to fail the build. #![deny(warnings)] -use jsonrpsee::{ - proc_macros::rpc, - types::{async_trait, RpcResult}, - ws_client::*, - ws_server::WsServerBuilder, -}; use std::net::SocketAddr; +use jsonrpsee::proc_macros::rpc; +use jsonrpsee::core::{async_trait, RpcResult}; +use jsonrpsee::ws_client::*; +use jsonrpsee::ws_server::WsServerBuilder; + #[rpc(client, server)] pub trait Deprecated { // Deprecated method that is called by the client. diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_deprecated_method.stderr b/proc-macros/tests/ui/incorrect/rpc/rpc_deprecated_method.stderr index 0f80a6749a..7a3018a819 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_deprecated_method.stderr +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_deprecated_method.stderr @@ -1,7 +1,7 @@ error: use of deprecated associated function `DeprecatedClient::async_method`: please use `new_method` instead - --> $DIR/rpc_deprecated_method.rs:64:20 + --> $DIR/rpc_deprecated_method.rs:63:20 | -64 | assert_eq!(client.async_method().await.unwrap(), 16); +63 | assert_eq!(client.async_method().await.unwrap(), 16); | ^^^^^^^^^^^^ | note: the lint level is defined here diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_name_conflict.rs b/proc-macros/tests/ui/incorrect/rpc/rpc_name_conflict.rs index f21ac44702..641062edca 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_name_conflict.rs +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_name_conflict.rs @@ -1,4 +1,4 @@ -use jsonrpsee::{proc_macros::rpc, types::RpcResult}; +use jsonrpsee::{proc_macros::rpc, core::RpcResult}; // Names must be unique. #[rpc(client, server)] diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_no_impls.rs b/proc-macros/tests/ui/incorrect/rpc/rpc_no_impls.rs index f371922ef9..d09e33eed6 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_no_impls.rs +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_no_impls.rs @@ -4,7 +4,7 @@ use jsonrpsee::proc_macros::rpc; #[rpc()] pub trait NoImpls { #[method(name = "foo")] - async fn async_method(&self) -> jsonrpsee::types::RpcResult; + async fn async_method(&self) -> jsonrpsee::core::RpcResult; } fn main() {} diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_not_qualified.rs b/proc-macros/tests/ui/incorrect/rpc/rpc_not_qualified.rs index a5cb32e072..84c1252614 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_not_qualified.rs +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_not_qualified.rs @@ -3,7 +3,7 @@ use jsonrpsee::proc_macros::rpc; // Method without type marker, `#[method(…)]` or `#[subscription(…)]`. #[rpc(client, server)] pub trait NotQualified { - async fn async_method(&self) -> jsonrpsee::types::RpcResult; + async fn async_method(&self) -> jsonrpsee::core::RpcResult; } fn main() {} diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_not_qualified.stderr b/proc-macros/tests/ui/incorrect/rpc/rpc_not_qualified.stderr index bd72dc752a..ade498d4db 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_not_qualified.stderr +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_not_qualified.stderr @@ -1,5 +1,5 @@ error: Methods must have either 'method' or 'subscription' attribute --> $DIR/rpc_not_qualified.rs:6:2 | -6 | async fn async_method(&self) -> jsonrpsee::types::RpcResult; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +6 | async fn async_method(&self) -> jsonrpsee::core::RpcResult; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/proc-macros/tests/ui/incorrect/sub/sub_conflicting_alias.rs b/proc-macros/tests/ui/incorrect/sub/sub_conflicting_alias.rs index 993063c51a..8301534c18 100644 --- a/proc-macros/tests/ui/incorrect/sub/sub_conflicting_alias.rs +++ b/proc-macros/tests/ui/incorrect/sub/sub_conflicting_alias.rs @@ -1,4 +1,4 @@ -use jsonrpsee::{proc_macros::rpc, types::RpcResult}; +use jsonrpsee::{proc_macros::rpc, core::RpcResult}; #[rpc(client, server)] pub trait DuplicatedSubAlias { diff --git a/proc-macros/tests/ui/incorrect/sub/sub_dup_name_override.rs b/proc-macros/tests/ui/incorrect/sub/sub_dup_name_override.rs index 53adf3fe2e..9a04dda824 100644 --- a/proc-macros/tests/ui/incorrect/sub/sub_dup_name_override.rs +++ b/proc-macros/tests/ui/incorrect/sub/sub_dup_name_override.rs @@ -1,4 +1,4 @@ -use jsonrpsee::{proc_macros::rpc, types::RpcResult}; +use jsonrpsee::{proc_macros::rpc, core::RpcResult}; // Subscription method must not use the same override name. #[rpc(client, server)] diff --git a/proc-macros/tests/ui/incorrect/sub/sub_name_override.rs b/proc-macros/tests/ui/incorrect/sub/sub_name_override.rs index 740b30699f..8f53d28302 100644 --- a/proc-macros/tests/ui/incorrect/sub/sub_name_override.rs +++ b/proc-macros/tests/ui/incorrect/sub/sub_name_override.rs @@ -1,4 +1,4 @@ -use jsonrpsee::{proc_macros::rpc, types::RpcResult}; +use jsonrpsee::{proc_macros::rpc, core::RpcResult}; // Subscription method name conflict with notif override. #[rpc(client, server)] diff --git a/test-utils/src/helpers.rs b/test-utils/src/helpers.rs index 3db1050a07..3a7e58fae2 100644 --- a/test-utils/src/helpers.rs +++ b/test-utils/src/helpers.rs @@ -24,13 +24,14 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use std::convert::Infallible; +use std::net::SocketAddr; + use crate::mocks::{Body, HttpResponse, Id, Uri}; use hyper::service::{make_service_fn, service_fn}; use hyper::{Request, Response, Server}; use serde::Serialize; use serde_json::Value; -use std::convert::Infallible; -use std::net::SocketAddr; pub const PARSE_ERROR: &str = "Parse error"; pub const INTERNAL_ERROR: &str = "Internal error"; diff --git a/test-utils/src/lib.rs b/test-utils/src/lib.rs index c47211bc62..9158317b82 100644 --- a/test-utils/src/lib.rs +++ b/test-utils/src/lib.rs @@ -28,7 +28,9 @@ #![recursion_limit = "256"] -use std::{future::Future, time::Duration}; +use std::future::Future; +use std::time::Duration; + use tokio::time::{timeout, Timeout}; pub mod helpers; diff --git a/test-utils/src/mocks.rs b/test-utils/src/mocks.rs index faa89f0ffe..4110ca6a2f 100644 --- a/test-utils/src/mocks.rs +++ b/test-utils/src/mocks.rs @@ -24,18 +24,19 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use std::io; +use std::net::SocketAddr; +use std::time::Duration; + use futures_channel::mpsc::{self, Receiver, Sender}; use futures_channel::oneshot; -use futures_util::{ - future::FutureExt, - io::{BufReader, BufWriter}, - pin_mut, select, - sink::SinkExt, - stream::{self, StreamExt}, -}; +use futures_util::future::FutureExt; +use futures_util::io::{BufReader, BufWriter}; +use futures_util::sink::SinkExt; +use futures_util::stream::{self, StreamExt}; +use futures_util::{pin_mut, select}; use serde::{Deserialize, Serialize}; use soketto::handshake::{self, http::is_upgrade_request, server::Response, Error as SokettoError, Server}; -use std::{io, net::SocketAddr, time::Duration}; use tokio::net::TcpStream; use tokio_util::compat::{Compat, TokioAsyncReadCompatExt}; diff --git a/tests/Cargo.toml b/tests/Cargo.toml index b293fe52e9..9daa5653e2 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -14,4 +14,5 @@ futures = { version = "0.3.14", default-features = false, features = ["std"] } jsonrpsee = { path = "../jsonrpsee", features = ["full"] } tokio = { version = "1.8", features = ["full"] } tracing = "0.1" +serde = "1" serde_json = "1" diff --git a/tests/tests/helpers.rs b/tests/tests/helpers.rs index 7df1d923e6..a191f1f3ea 100644 --- a/tests/tests/helpers.rs +++ b/tests/tests/helpers.rs @@ -24,15 +24,14 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use jsonrpsee::{ - http_server::{HttpServerBuilder, HttpServerHandle}, - types::Error, - ws_server::{WsServerBuilder, WsServerHandle}, - RpcModule, -}; use std::net::SocketAddr; use std::time::Duration; +use jsonrpsee::core::Error; +use jsonrpsee::http_server::{HttpServerBuilder, HttpServerHandle}; +use jsonrpsee::ws_server::{WsServerBuilder, WsServerHandle}; +use jsonrpsee::RpcModule; + pub async fn websocket_server_with_subscription() -> (SocketAddr, WsServerHandle) { let server = WsServerBuilder::default().build("127.0.0.1:0").await.unwrap(); diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs index df53b39679..50b964214d 100644 --- a/tests/tests/integration_tests.rs +++ b/tests/tests/integration_tests.rs @@ -27,22 +27,19 @@ #![cfg(test)] #![allow(clippy::blacklisted_name)] -mod helpers; - -use helpers::{http_server, websocket_server, websocket_server_with_subscription}; -use jsonrpsee::{ - http_client::HttpClientBuilder, - rpc_params, - types::{ - error::SubscriptionClosedReason, - traits::{Client, SubscriptionClient}, - Error, JsonValue, Subscription, - }, - ws_client::WsClientBuilder, -}; use std::sync::Arc; use std::time::Duration; +use helpers::{http_server, websocket_server, websocket_server_with_subscription}; +use jsonrpsee::core::client::{Client, Subscription, SubscriptionClient}; +use jsonrpsee::core::error::SubscriptionClosedReason; +use jsonrpsee::core::{Error, JsonValue}; +use jsonrpsee::http_client::HttpClientBuilder; +use jsonrpsee::rpc_params; +use jsonrpsee::ws_client::WsClientBuilder; + +mod helpers; + #[tokio::test] async fn ws_subscription_works() { let (server_addr, _) = websocket_server_with_subscription().await; diff --git a/tests/tests/middleware.rs b/tests/tests/middleware.rs index 6767b3968f..28fee2fbde 100644 --- a/tests/tests/middleware.rs +++ b/tests/tests/middleware.rs @@ -24,22 +24,20 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use jsonrpsee::{ - http_client::HttpClientBuilder, - http_server::{HttpServerBuilder, HttpServerHandle}, - proc_macros::rpc, - types::{middleware::Middleware, traits::Client, Error}, - ws_client::WsClientBuilder, - ws_server::{WsServerBuilder, WsServerHandle}, - RpcModule, -}; -use tokio::time::sleep; - use std::collections::HashMap; use std::net::SocketAddr; use std::sync::{Arc, Mutex}; use std::time::Duration; +use jsonrpsee::core::{client::Client, middleware::Middleware, Error}; +use jsonrpsee::http_client::HttpClientBuilder; +use jsonrpsee::http_server::{HttpServerBuilder, HttpServerHandle}; +use jsonrpsee::proc_macros::rpc; +use jsonrpsee::ws_client::WsClientBuilder; +use jsonrpsee::ws_server::{WsServerBuilder, WsServerHandle}; +use jsonrpsee::RpcModule; +use tokio::time::sleep; + #[derive(Clone, Default)] struct Counter { inner: Arc>, diff --git a/tests/tests/proc_macros.rs b/tests/tests/proc_macros.rs index 82fecc2e01..494258147e 100644 --- a/tests/tests/proc_macros.rs +++ b/tests/tests/proc_macros.rs @@ -28,19 +28,17 @@ use std::net::SocketAddr; -use jsonrpsee::{ - http_client::HttpClientBuilder, http_server::HttpServerBuilder, types::Error, ws_client::*, - ws_server::WsServerBuilder, -}; - +use jsonrpsee::core::Error; +use jsonrpsee::http_client::HttpClientBuilder; +use jsonrpsee::http_server::HttpServerBuilder; +use jsonrpsee::ws_client::*; +use jsonrpsee::ws_server::WsServerBuilder; use serde_json::json; mod rpc_impl { - use jsonrpsee::{ - proc_macros::rpc, - types::{async_trait, RpcResult}, - ws_server::SubscriptionSink, - }; + use jsonrpsee::core::{async_trait, RpcResult}; + use jsonrpsee::proc_macros::rpc; + use jsonrpsee::ws_server::SubscriptionSink; #[rpc(client, server, namespace = "foo")] pub trait Rpc { diff --git a/tests/tests/resource_limiting.rs b/tests/tests/resource_limiting.rs index f36f9cf9cf..f0d0837a7c 100644 --- a/tests/tests/resource_limiting.rs +++ b/tests/tests/resource_limiting.rs @@ -24,20 +24,19 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use jsonrpsee::{ - http_client::HttpClientBuilder, - http_server::{HttpServerBuilder, HttpServerHandle}, - proc_macros::rpc, - types::{traits::Client, Error}, - ws_client::WsClientBuilder, - ws_server::{WsServerBuilder, WsServerHandle}, - RpcModule, -}; -use tokio::time::sleep; - use std::net::SocketAddr; use std::time::Duration; +use jsonrpsee::core::client::Client; +use jsonrpsee::core::Error; +use jsonrpsee::http_client::HttpClientBuilder; +use jsonrpsee::http_server::{HttpServerBuilder, HttpServerHandle}; +use jsonrpsee::proc_macros::rpc; +use jsonrpsee::ws_client::WsClientBuilder; +use jsonrpsee::ws_server::{WsServerBuilder, WsServerHandle}; +use jsonrpsee::RpcModule; +use tokio::time::sleep; + fn module_manual() -> Result, Error> { let mut module = RpcModule::new(()); diff --git a/tests/tests/rpc_module.rs b/tests/tests/rpc_module.rs new file mode 100644 index 0000000000..a36723ce94 --- /dev/null +++ b/tests/tests/rpc_module.rs @@ -0,0 +1,236 @@ +// 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. + +use std::collections::HashMap; + +use jsonrpsee::core::server::rpc_module::*; +use jsonrpsee::core::Error; +use jsonrpsee::types::{EmptyParams, Params, SubscriptionId as RpcSubscriptionId}; +use serde::{Deserialize, Serialize}; + +#[test] +fn rpc_modules_with_different_contexts_can_be_merged() { + let cx = Vec::::new(); + let mut mod1 = RpcModule::new(cx); + mod1.register_method("bla with Vec context", |_: Params, _| Ok(())).unwrap(); + let mut mod2 = RpcModule::new(String::new()); + mod2.register_method("bla with String context", |_: Params, _| Ok(())).unwrap(); + + mod1.merge(mod2).unwrap(); + + assert!(mod1.method("bla with Vec context").is_some()); + assert!(mod1.method("bla with String context").is_some()); +} + +#[test] +fn rpc_context_modules_can_register_subscriptions() { + let cx = (); + let mut cxmodule = RpcModule::new(cx); + let _subscription = cxmodule.register_subscription("hi", "hi", "goodbye", |_, _, _| Ok(())); + + assert!(cxmodule.method("hi").is_some()); + assert!(cxmodule.method("goodbye").is_some()); +} + +#[test] +fn rpc_register_alias() { + let mut module = RpcModule::new(()); + + module.register_method("hello_world", |_: Params, _| Ok(())).unwrap(); + module.register_alias("hello_foobar", "hello_world").unwrap(); + + assert!(module.method("hello_world").is_some()); + assert!(module.method("hello_foobar").is_some()); +} + +#[tokio::test] +async fn calling_method_without_server() { + // Call sync method with no params + let mut module = RpcModule::new(()); + module.register_method("boo", |_: Params, _| Ok(String::from("boo!"))).unwrap(); + + let res: String = module.call("boo", EmptyParams::new()).await.unwrap(); + assert_eq!(&res, "boo!"); + + // Call sync method with params + module + .register_method("foo", |params, _| { + let n: u16 = params.one()?; + Ok(n * 2) + }) + .unwrap(); + let res: u64 = module.call("foo", [3_u64]).await.unwrap(); + assert_eq!(res, 6); + + // Call sync method with bad param + let err = module.call::<_, ()>("foo", (false,)).await.unwrap_err(); + assert!( + matches!(err, Error::Request(err) if err == r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"invalid type: boolean `false`, expected u16 at line 1 column 6"},"id":0}"#) + ); + + // Call async method with params and context + struct MyContext; + impl MyContext { + fn roo(&self, things: Vec) -> u16 { + things.iter().sum::().into() + } + } + let mut module = RpcModule::new(MyContext); + module + .register_async_method("roo", |params, ctx| { + let ns: Vec = params.parse().expect("valid params please"); + async move { Ok(ctx.roo(ns)) } + }) + .unwrap(); + let res: u64 = module.call("roo", [12, 13]).await.unwrap(); + assert_eq!(res, 25); +} + +#[tokio::test] +async fn calling_method_without_server_using_proc_macro() { + use jsonrpsee::{core::async_trait, proc_macros::rpc}; + // Setup + #[derive(Debug, Deserialize, Serialize)] + #[allow(unreachable_pub)] + pub struct Gun { + shoots: bool, + } + + #[derive(Debug, Deserialize, Serialize)] + #[allow(unreachable_pub)] + pub struct Beverage { + ice: bool, + } + + #[rpc(server)] + pub trait Cool { + /// Sync method, no params. + #[method(name = "rebel_without_cause")] + fn rebel_without_cause(&self) -> Result; + + /// Sync method. + #[method(name = "rebel")] + fn rebel(&self, gun: Gun, map: HashMap) -> Result; + + /// Async method. + #[method(name = "revolution")] + async fn can_have_any_name(&self, beverage: Beverage, some_bytes: Vec) -> Result; + } + + struct CoolServerImpl; + + #[async_trait] + impl CoolServer for CoolServerImpl { + fn rebel_without_cause(&self) -> Result { + Ok(false) + } + + fn rebel(&self, gun: Gun, map: HashMap) -> Result { + Ok(format!("{} {:?}", map.values().len(), gun)) + } + + async fn can_have_any_name(&self, beverage: Beverage, some_bytes: Vec) -> Result { + Ok(format!("drink: {:?}, phases: {:?}", beverage, some_bytes)) + } + } + let module = CoolServerImpl.into_rpc(); + + // Call sync method with no params + let res: bool = module.call("rebel_without_cause", EmptyParams::new()).await.unwrap(); + assert!(!res); + + // Call sync method with params + let res: String = module.call("rebel", (Gun { shoots: true }, HashMap::::default())).await.unwrap(); + assert_eq!(&res, "0 Gun { shoots: true }"); + + // Call sync method with bad params + let err = module.call::<_, ()>("rebel", (Gun { shoots: true }, false)).await.unwrap_err(); + assert!(matches!( + err, + Error::Request(err) if err == r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"invalid type: boolean `false`, expected a map at line 1 column 5"},"id":0}"# + )); + + // Call async method with params and context + let result: String = module.call("revolution", (Beverage { ice: true }, vec![1, 2, 3])).await.unwrap(); + assert_eq!(&result, "drink: Beverage { ice: true }, phases: [1, 2, 3]"); +} + +#[tokio::test] +async fn subscribing_without_server() { + let mut module = RpcModule::new(()); + module + .register_subscription("my_sub", "my_sub", "my_unsub", |_, mut sink, _| { + let mut stream_data = vec!['0', '1', '2']; + std::thread::spawn(move || { + while let Some(letter) = stream_data.pop() { + tracing::debug!("This is your friendly subscription sending data."); + if let Err(Error::SubscriptionClosed(_)) = sink.send(&letter) { + return; + } + std::thread::sleep(std::time::Duration::from_millis(500)); + } + }); + Ok(()) + }) + .unwrap(); + + let mut my_sub = module.subscribe("my_sub", EmptyParams::new()).await.unwrap(); + for i in (0..=2).rev() { + let (val, id) = my_sub.next::().await.unwrap().unwrap(); + assert_eq!(val, std::char::from_digit(i, 10).unwrap()); + assert_eq!(id, RpcSubscriptionId::Num(my_sub.subscription_id())); + } + + let sub_err = my_sub.next::().await.unwrap().unwrap_err(); + + // The subscription is now closed by the server. + assert!(matches!(sub_err, Error::SubscriptionClosed(_))); +} + +#[tokio::test] +async fn close_test_subscribing_without_server() { + let mut module = RpcModule::new(()); + module + .register_subscription("my_sub", "my_sub", "my_unsub", |_, mut sink, _| { + std::thread::spawn(move || loop { + if let Err(Error::SubscriptionClosed(_)) = sink.send(&"lo") { + return; + } + std::thread::sleep(std::time::Duration::from_millis(500)); + }); + Ok(()) + }) + .unwrap(); + + let mut my_sub = module.subscribe("my_sub", EmptyParams::new()).await.unwrap(); + let (val, id) = my_sub.next::().await.unwrap().unwrap(); + assert_eq!(&val, "lo"); + assert_eq!(id, RpcSubscriptionId::Num(my_sub.subscription_id())); + + // close the subscription to ensure it doesn't return any items. + my_sub.close(); + assert!(matches!(my_sub.next::().await, None)); +} diff --git a/types/Cargo.toml b/types/Cargo.toml index b4488810ed..423f57d0c3 100644 --- a/types/Cargo.toml +++ b/types/Cargo.toml @@ -10,7 +10,6 @@ homepage = "https://github.com/paritytech/jsonrpsee" documentation = "https://docs.rs/jsonrpsee-types" [dependencies] -async-trait = "0.1" anyhow = "1" beef = { version = "0.5.1", features = ["impl_serde"] } futures-channel = { version = "0.3.14", features = ["sink"] } diff --git a/types/src/error.rs b/types/src/error.rs index 7385dbd973..3bbe1e1014 100644 --- a/types/src/error.rs +++ b/types/src/error.rs @@ -24,262 +24,316 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use std::fmt; + +use crate::params::{Id, TwoPointZero}; +use beef::Cow; +use serde::de::Deserializer; +use serde::ser::Serializer; use serde::{Deserialize, Serialize}; use serde_json::value::RawValue; -use std::fmt; +use thiserror::Error; -/// Convenience type for displaying errors. -#[derive(Clone, Debug, PartialEq)] -pub struct Mismatch { - /// Expected value. - pub expected: T, - /// Actual value. - pub got: T, +/// [Failed JSON-RPC response object](https://www.jsonrpc.org/specification#response_object). +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub struct ErrorResponse<'a> { + /// JSON-RPC version. + pub jsonrpc: TwoPointZero, + /// Error. + #[serde(borrow)] + pub error: ErrorObject<'a>, + /// Request ID + pub id: Id<'a>, } -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)) +impl<'a> ErrorResponse<'a> { + /// Create a new `ErrorResponse`. + pub fn new(error: ErrorObject<'a>, id: Id<'a>) -> Self { + Self { jsonrpc: TwoPointZero, error, id } } } -/// Error that occurs when a call failed. -#[derive(Debug, thiserror::Error)] -pub enum CallError { - /// Invalid params in the call. - #[error("Invalid params in the call: {0}")] - InvalidParams(#[source] anyhow::Error), - /// The call failed (let jsonrpsee assign default error code and error message). - #[error("RPC Call failed: {0}")] - Failed(#[from] anyhow::Error), - /// Custom error with specific JSON-RPC error code, message and data. - #[error("RPC Call failed: code: {code}, message: {message}, data: {data:?}")] - Custom { - /// JSON-RPC error code - code: i32, - /// Short description of the error. - message: String, - /// A primitive or structured value that contains additional information about the error. - data: Option>, - }, +impl<'a> fmt::Display for ErrorResponse<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", serde_json::to_string(&self).expect("infallible; qed")) + } } -impl CallError { - /// Create `CallError` from a generic error. - pub fn from_std_error(err: E) -> Self - where - E: std::error::Error + Send + Sync + 'static, - { - CallError::Failed(err.into()) - } +/// JSON-RPC error object. +#[derive(Debug, Deserialize, Serialize, Clone)] +#[serde(deny_unknown_fields)] +pub struct ErrorObject<'a> { + /// Code + pub code: ErrorCode, + /// Message + #[serde(borrow)] + pub message: Cow<'a, str>, + /// Optional data + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(borrow)] + pub data: Option<&'a RawValue>, } -// NOTE(niklasad1): this `From` impl is a bit opinionated to regard all generic errors as `CallError`. -// In practice this should be the most common use case for users of this library. -impl From for Error { - fn from(err: anyhow::Error) -> Self { - Error::Call(CallError::Failed(err)) +impl<'a> ErrorObject<'a> { + /// Create a new `ErrorObject` with optional data. + pub fn new(code: ErrorCode, data: Option<&'a RawValue>) -> ErrorObject<'a> { + Self { code, message: code.message().into(), data } } } -/// Error type. -#[derive(Debug, thiserror::Error)] -pub enum Error { - /// Error that occurs when a call failed. - #[error("Server call failed: {0}")] - Call(#[from] CallError), - /// Networking error or error on the low-level protocol layer. - #[error("Networking or low-level protocol error: {0}")] - Transport(#[source] anyhow::Error), - /// JSON-RPC request error. - #[error("JSON-RPC request error: {0:?}")] - Request(String), - /// Frontend/backend channel error. - #[error("Frontend/backend channel error: {0}")] - Internal(#[from] futures_channel::mpsc::SendError), - /// 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(String), - /// 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("Invalid request ID")] - 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), - /// Subscription got closed. - #[error("Subscription closed: {0:?}")] - SubscriptionClosed(SubscriptionClosed), - /// 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 `set_allowed_origins` was empty - #[error("Must set at least one allowed value for the {0} header")] - EmptyAllowList(&'static str), - /// Failed to execute a method because a resource was already at capacity - #[error("Resource at capacity: {0}")] - ResourceAtCapacity(&'static str), - /// Failed to register a resource due to a name conflict - #[error("Resource name already taken: {0}")] - ResourceNameAlreadyTaken(&'static str), - /// Failed to initialize resources for a method at startup - #[error("Resource name `{0}` not found for method `{1}`")] - ResourceNameNotFoundForMethod(&'static str, &'static str), - /// Trying to claim resources for a method execution, but the method resources have not been initialized - #[error("Method `{0}` has uninitialized resources")] - UninitializedMethod(Box), - /// Failed to register a resource due to a maximum number of resources already registered - #[error("Maximum number of resources reached")] - MaxResourcesReached, - /// Custom error. - #[error("Custom error: {0}")] - Custom(String), - /// Not implemented for HTTP clients. - #[error("Not implemented")] - HttpNotImplemented, +impl<'a> From for ErrorObject<'a> { + fn from(code: ErrorCode) -> Self { + Self { code, message: code.message().into(), data: None } + } } -impl Error { - /// Create `Error::CallError` from a generic error. - /// Useful if you don't care about specific JSON-RPC error code and - /// just wants to return your custom error type. - pub fn to_call_error(err: E) -> Self - where - E: std::error::Error + Send + Sync + 'static, - { - Error::Call(CallError::from_std_error(err)) +impl<'a> PartialEq for ErrorObject<'a> { + fn eq(&self, other: &Self) -> bool { + let this_raw = self.data.map(|r| r.get()); + let other_raw = other.data.map(|r| r.get()); + self.code == other.code && self.message == other.message && this_raw == other_raw } } -/// A type with a special `subscription_closed` field to detect that -/// a subscription has been closed to distinguish valid items produced -/// by the server on the subscription stream from an error. -/// -/// This is included in the `result field` of the SubscriptionResponse -/// when an error is reported by the server. -#[derive(Deserialize, Serialize, Debug, PartialEq)] -#[serde(deny_unknown_fields)] -pub struct SubscriptionClosed { - reason: SubscriptionClosedReason, -} +/// Parse error code. +pub const PARSE_ERROR_CODE: i32 = -32700; +/// Oversized request error code. +pub const OVERSIZED_REQUEST_CODE: i32 = -32701; +/// Oversized response error code. +pub const OVERSIZED_RESPONSE_CODE: i32 = -32702; +/// Internal error code. +pub const INTERNAL_ERROR_CODE: i32 = -32603; +/// Invalid params error code. +pub const INVALID_PARAMS_CODE: i32 = -32602; +/// Invalid request error code. +pub const INVALID_REQUEST_CODE: i32 = -32600; +/// Method not found error code. +pub const METHOD_NOT_FOUND_CODE: i32 = -32601; +/// Server is busy error code. +pub const SERVER_IS_BUSY_CODE: i32 = -32604; +/// Custom server error when a call failed. +pub const CALL_EXECUTION_FAILED_CODE: i32 = -32000; +/// Unknown error. +pub const UNKNOWN_ERROR_CODE: i32 = -32001; +/// Invalid subscription error code. +pub const INVALID_SUBSCRIPTION_CODE: i32 = -32002; -impl From for SubscriptionClosed { - fn from(reason: SubscriptionClosedReason) -> Self { - Self::new(reason) - } +/// Parse error message +pub const PARSE_ERROR_MSG: &str = "Parse error"; +/// Oversized request message +pub const OVERSIZED_REQUEST_MSG: &str = "Request is too big"; +/// Oversized response message +pub const OVERSIZED_RESPONSE_MSG: &str = "Response is too big"; +/// Internal error message. +pub const INTERNAL_ERROR_MSG: &str = "Internal error"; +/// Invalid params error message. +pub const INVALID_PARAMS_MSG: &str = "Invalid params"; +/// Invalid request error message. +pub const INVALID_REQUEST_MSG: &str = "Invalid request"; +/// Method not found error message. +pub const METHOD_NOT_FOUND_MSG: &str = "Method not found"; +/// Server is busy error message. +pub const SERVER_IS_BUSY_MSG: &str = "Server is busy, try again later"; +/// Reserved for implementation-defined server-errors. +pub const SERVER_ERROR_MSG: &str = "Server error"; + +/// JSONRPC error code +#[derive(Error, Debug, PartialEq, Copy, Clone)] +pub enum ErrorCode { + /// Invalid JSON was received by the server. + /// An error occurred on the server while parsing the JSON text. + ParseError, + /// The request was too big. + OversizedRequest, + /// The JSON sent is not a valid Request object. + InvalidRequest, + /// The method does not exist / is not available. + MethodNotFound, + /// Server is busy / resources are at capacity. + ServerIsBusy, + /// Invalid method parameter(s). + InvalidParams, + /// Internal JSON-RPC error. + InternalError, + /// Reserved for implementation-defined server-errors. + ServerError(i32), } -impl SubscriptionClosed { - /// Create a new [`SubscriptionClosed`]. - pub fn new(reason: SubscriptionClosedReason) -> Self { - Self { reason } +impl ErrorCode { + /// Returns integer code value + pub const fn code(&self) -> i32 { + use ErrorCode::*; + match *self { + ParseError => PARSE_ERROR_CODE, + OversizedRequest => OVERSIZED_REQUEST_CODE, + InvalidRequest => INVALID_REQUEST_CODE, + MethodNotFound => METHOD_NOT_FOUND_CODE, + ServerIsBusy => SERVER_IS_BUSY_CODE, + InvalidParams => INVALID_PARAMS_CODE, + InternalError => INTERNAL_ERROR_CODE, + ServerError(code) => code, + } } - /// Get the close reason. - pub fn close_reason(&self) -> &SubscriptionClosedReason { - &self.reason + /// Returns the message for the given error code. + pub const fn message(&self) -> &'static str { + use ErrorCode::*; + match self { + ParseError => PARSE_ERROR_MSG, + OversizedRequest => OVERSIZED_REQUEST_MSG, + InvalidRequest => INVALID_REQUEST_MSG, + MethodNotFound => METHOD_NOT_FOUND_MSG, + ServerIsBusy => SERVER_IS_BUSY_MSG, + InvalidParams => INVALID_PARAMS_MSG, + InternalError => INTERNAL_ERROR_MSG, + ServerError(_) => SERVER_ERROR_MSG, + } } } -/// A type to represent when a subscription gets closed -/// by either the server or client side. -#[derive(Deserialize, Serialize, Debug, PartialEq)] -pub enum SubscriptionClosedReason { - /// The subscription was closed by calling the unsubscribe method. - Unsubscribed, - /// The client closed the connection. - ConnectionReset, - /// The server closed the subscription, providing a description of the reason as a `String`. - Server(String), +impl fmt::Display for ErrorCode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}: {}", self.code(), self.message()) + } } -/// Generic transport error. -#[derive(Debug, thiserror::Error)] -pub enum GenericTransportError { - /// Request was too large. - #[error("The request was too big")] - TooLarge, - /// Malformed request - #[error("Malformed request")] - Malformed, - /// Concrete transport error. - #[error("Transport error: {0}")] - Inner(T), +impl From for ErrorCode { + fn from(code: i32) -> Self { + use ErrorCode::*; + match code { + PARSE_ERROR_CODE => ParseError, + OVERSIZED_REQUEST_CODE => OversizedRequest, + INVALID_REQUEST_CODE => InvalidRequest, + METHOD_NOT_FOUND_CODE => MethodNotFound, + INVALID_PARAMS_CODE => InvalidParams, + INTERNAL_ERROR_CODE => InternalError, + code => ServerError(code), + } + } } -impl From for Error { - fn from(io_err: std::io::Error) -> Error { - Error::Transport(io_err.into()) +impl<'a> serde::Deserialize<'a> for ErrorCode { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'a>, + { + let code: i32 = Deserialize::deserialize(deserializer)?; + Ok(ErrorCode::from(code)) } } -impl From for Error { - fn from(handshake_err: soketto::handshake::Error) -> Error { - Error::Transport(handshake_err.into()) +impl serde::Serialize for ErrorCode { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_i32(self.code()) } } -impl From for Error { - fn from(conn_err: soketto::connection::Error) -> Error { - Error::Transport(conn_err.into()) - } +/// Error that occurs when a call failed. +#[derive(Debug, thiserror::Error)] +pub enum CallError { + /// Invalid params in the call. + #[error("Invalid params in the call: {0}")] + InvalidParams(#[source] anyhow::Error), + /// The call failed (let jsonrpsee assign default error code and error message). + #[error("RPC Call failed: {0}")] + Failed(#[from] anyhow::Error), + /// Custom error with specific JSON-RPC error code, message and data. + #[error("RPC Call failed: code: {code}, message: {message}, data: {data:?}")] + Custom { + /// JSON-RPC error code + code: i32, + /// Short description of the error. + message: String, + /// A primitive or structured value that contains additional information about the error. + data: Option>, + }, } -impl From for Error { - fn from(hyper_err: hyper::Error) -> Error { - Error::Transport(hyper_err.into()) +impl CallError { + /// Create `CallError` from a generic error. + pub fn from_std_error(err: E) -> Self + where + E: std::error::Error + Send + Sync + 'static, + { + CallError::Failed(err.into()) } } +/// Create a invalid subscription ID error. +pub fn invalid_subscription_err(data: Option<&RawValue>) -> ErrorObject { + ErrorObject::new(ErrorCode::ServerError(INVALID_SUBSCRIPTION_CODE), data) +} + #[cfg(test)] mod tests { - use super::{SubscriptionClosed, SubscriptionClosedReason}; + use super::{ErrorCode, ErrorObject, ErrorResponse, Id, TwoPointZero}; #[test] - fn subscription_closed_ser_deser_works() { - let items: Vec<(&str, SubscriptionClosed)> = vec![ - (r#"{"reason":"Unsubscribed"}"#, SubscriptionClosedReason::Unsubscribed.into()), - (r#"{"reason":"ConnectionReset"}"#, SubscriptionClosedReason::ConnectionReset.into()), - (r#"{"reason":{"Server":"hoho"}}"#, SubscriptionClosedReason::Server("hoho".into()).into()), - ]; + fn deserialize_works() { + let ser = r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#; + let exp = ErrorResponse { + jsonrpc: TwoPointZero, + error: ErrorObject { code: ErrorCode::ParseError, message: "Parse error".into(), data: None }, + id: Id::Null, + }; + let err: ErrorResponse = serde_json::from_str(ser).unwrap(); + assert_eq!(exp, err); + } - for (s, d) in items { - let dsr: SubscriptionClosed = serde_json::from_str(s).unwrap(); - assert_eq!(dsr, d); - let ser = serde_json::to_string(&d).unwrap(); - assert_eq!(ser, s); - } + #[test] + fn deserialize_with_optional_data() { + let ser = r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error", "data":"vegan"},"id":null}"#; + let data = serde_json::value::to_raw_value(&"vegan").unwrap(); + let exp = ErrorResponse { + jsonrpc: TwoPointZero, + error: ErrorObject { code: ErrorCode::ParseError, message: "Parse error".into(), data: Some(&*data) }, + id: Id::Null, + }; + let err: ErrorResponse = serde_json::from_str(ser).unwrap(); + assert_eq!(exp, err); + } + + #[test] + fn deserialized_error_with_quoted_str() { + let raw = r#"{ + "error": { + "code": 1002, + "message": "desc: \"Could not decode `ChargeAssetTxPayment::asset_id`\" } })", + "data": "\\\"validate_transaction\\\"" + }, + "id": 7, + "jsonrpc": "2.0" + }"#; + let err: ErrorResponse = serde_json::from_str(raw).unwrap(); + + let data = serde_json::value::to_raw_value(&"\\\"validate_transaction\\\"").unwrap(); + + assert_eq!( + err, + ErrorResponse { + error: ErrorObject { + code: 1002.into(), + message: "desc: \"Could not decode `ChargeAssetTxPayment::asset_id`\" } })".into(), + data: Some(&*data), + }, + id: Id::Number(7), + jsonrpc: TwoPointZero, + } + ); } #[test] - fn subscription_closed_deny_unknown_field() { - let ser = r#"{"reason":"Unsubscribed","deny":1}"#; - assert!(serde_json::from_str::(ser).is_err()); + fn serialize_works() { + let exp = r#"{"jsonrpc":"2.0","error":{"code":-32603,"message":"Internal error"},"id":1337}"#; + let err = ErrorResponse { + jsonrpc: TwoPointZero, + error: ErrorObject { code: ErrorCode::InternalError, message: "Internal error".into(), data: None }, + id: Id::Number(1337), + }; + let ser = serde_json::to_string(&err).unwrap(); + assert_eq!(exp, ser); } } diff --git a/types/src/lib.rs b/types/src/lib.rs index 48a074930b..9b3d5909c6 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -34,42 +34,23 @@ extern crate alloc; /// Ten megabytes. pub const TEN_MB_SIZE_BYTES: u32 = 10 * 1024 * 1024; -/// JSON-RPC v2.0 specification related types. -pub mod v2; +/// JSON-RPC params related types. +pub mod params; -/// Error type. -pub mod error; - -/// Client types. -mod client; - -/// Traits -pub mod traits; +/// JSON-RPC request object related types +pub mod request; -/// Middleware trait and implementation. -pub mod middleware; +/// JSON-RPC response object related types. +pub mod response; -pub use async_trait::async_trait; -pub use beef::Cow; -pub use client::*; -pub use error::{CallError, Error}; -pub use serde::{de::DeserializeOwned, Serialize}; -pub use serde_json::{ - to_value as to_json_value, value::to_raw_value as to_json_raw_value, value::RawValue as JsonRawValue, - Value as JsonValue, -}; - -/// Re-exports for proc-macro library to not require any additional -/// dependencies to be explicitly added on the client side. -#[doc(hidden)] -pub mod __reexports { - pub use async_trait::async_trait; - pub use serde; - pub use serde_json; -} +/// JSON-RPC response error object related types. +pub mod error; -/// JSON-RPC result. -pub type RpcResult = std::result::Result; +// pub use async_trait::async_trait; +pub use error::ErrorResponse; +pub use params::{Id, Params, ParamsSequence, ParamsSer, SubscriptionId, TwoPointZero}; +pub use request::{InvalidRequest, Notification, NotificationSer, Request, RequestSer}; +pub use response::{Response, SubscriptionPayload, SubscriptionResponse}; /// Empty `RpcParams` type; pub type EmptyParams = Vec<()>; diff --git a/types/src/v2/params.rs b/types/src/params.rs similarity index 98% rename from types/src/v2/params.rs rename to types/src/params.rs index f5ba85a219..5ba9733bc6 100644 --- a/types/src/v2/params.rs +++ b/types/src/params.rs @@ -27,6 +27,9 @@ //! Types to handle JSON-RPC request parameters according to the [spec](https://www.jsonrpc.org/specification#parameter_structures). //! Some types come with a "*Ser" variant that implements [`serde::Serialize`]; these are used in the client. +use std::convert::TryFrom; +use std::fmt; + use crate::error::CallError; use alloc::collections::BTreeMap; use anyhow::anyhow; @@ -35,7 +38,6 @@ use serde::de::{self, Deserializer, Unexpected, Visitor}; use serde::ser::Serializer; use serde::{Deserialize, Serialize}; use serde_json::Value as JsonValue; -use std::{convert::TryFrom, fmt}; /// JSON-RPC v2 marker type. #[derive(Clone, Copy, Debug, Default, PartialEq)] @@ -199,7 +201,7 @@ impl<'a> ParamsSequence<'a> { /// Parse the next parameter to type `T` /// /// ``` - /// # use jsonrpsee_types::v2::params::Params; + /// # use jsonrpsee_types::params::Params; /// let params = Params::new(Some(r#"[true, 10, "foo"]"#)); /// let mut seq = params.sequence(); /// @@ -227,7 +229,7 @@ impl<'a> ParamsSequence<'a> { /// The result will be `None` for `null`, and for missing values in the supplied JSON array. /// /// ``` - /// # use jsonrpsee_types::v2::params::Params; + /// # use jsonrpsee_types::params::Params; /// let params = Params::new(Some(r#"[1, 2, null]"#)); /// let mut seq = params.sequence(); /// @@ -390,7 +392,7 @@ impl<'a> Id<'a> { #[cfg(test)] mod test { use super::{Cow, Id, JsonValue, Params, ParamsSer, SubscriptionId, TwoPointZero}; - use crate::v2::response::SubscriptionPayload; + use crate::response::SubscriptionPayload; #[test] fn id_deserialization() { diff --git a/types/src/v2/request.rs b/types/src/request.rs similarity index 99% rename from types/src/v2/request.rs rename to types/src/request.rs index 57739c0bfe..801e89e283 100644 --- a/types/src/v2/request.rs +++ b/types/src/request.rs @@ -27,7 +27,7 @@ //! Types to handle JSON-RPC requests according to the [spec](https://www.jsonrpc.org/specification#request-object). //! Some types come with a "*Ser" variant that implements [`serde::Serialize`]; these are used in the client. -use crate::v2::params::{Id, ParamsSer, TwoPointZero}; +use crate::params::{Id, ParamsSer, TwoPointZero}; use beef::Cow; use serde::{Deserialize, Serialize}; use serde_json::value::RawValue; diff --git a/types/src/v2/response.rs b/types/src/response.rs similarity index 96% rename from types/src/v2/response.rs rename to types/src/response.rs index 60c338440e..b0abd991f0 100644 --- a/types/src/v2/response.rs +++ b/types/src/response.rs @@ -26,10 +26,8 @@ //! Types pertaining to JSON-RPC responses. -use crate::v2::{ - params::{Id, SubscriptionId, TwoPointZero}, - request::Notification, -}; +use crate::params::{Id, SubscriptionId, TwoPointZero}; +use crate::request::Notification; use serde::{Deserialize, Serialize}; /// JSON-RPC successful response object as defined in the [spec](https://www.jsonrpc.org/specification#response_object). diff --git a/types/src/v2/error.rs b/types/src/v2/error.rs deleted file mode 100644 index c5e1dc1ddd..0000000000 --- a/types/src/v2/error.rs +++ /dev/null @@ -1,307 +0,0 @@ -// 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. - -use crate::v2::params::{Id, TwoPointZero}; -use beef::Cow; -use serde::de::Deserializer; -use serde::ser::Serializer; -use serde::{Deserialize, Serialize}; -use serde_json::value::RawValue; -use std::fmt; -use thiserror::Error; - -/// [Failed JSON-RPC response object](https://www.jsonrpc.org/specification#response_object). -#[derive(Serialize, Deserialize, Debug, PartialEq)] -pub struct RpcError<'a> { - /// JSON-RPC version. - pub jsonrpc: TwoPointZero, - /// Error. - #[serde(borrow)] - pub error: ErrorObject<'a>, - /// Request ID - pub id: Id<'a>, -} - -impl<'a> RpcError<'a> { - /// Create a new `RpcError`. - pub fn new(error: ErrorObject<'a>, id: Id<'a>) -> Self { - Self { jsonrpc: TwoPointZero, error, id } - } -} - -impl<'a> fmt::Display for RpcError<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", serde_json::to_string(&self).expect("infallible; qed")) - } -} - -/// JSON-RPC error object. -#[derive(Debug, Deserialize, Serialize, Clone)] -#[serde(deny_unknown_fields)] -pub struct ErrorObject<'a> { - /// Code - pub code: ErrorCode, - /// Message - #[serde(borrow)] - pub message: Cow<'a, str>, - /// Optional data - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(borrow)] - pub data: Option<&'a RawValue>, -} - -impl<'a> ErrorObject<'a> { - /// Create a new `ErrorObject` with optional data. - pub fn new(code: ErrorCode, data: Option<&'a RawValue>) -> ErrorObject<'a> { - Self { code, message: code.message().into(), data } - } -} - -impl<'a> From for ErrorObject<'a> { - fn from(code: ErrorCode) -> Self { - Self { code, message: code.message().into(), data: None } - } -} - -impl<'a> PartialEq for ErrorObject<'a> { - fn eq(&self, other: &Self) -> bool { - let this_raw = self.data.map(|r| r.get()); - let other_raw = other.data.map(|r| r.get()); - self.code == other.code && self.message == other.message && this_raw == other_raw - } -} - -/// Parse error code. -pub const PARSE_ERROR_CODE: i32 = -32700; -/// Oversized request error code. -pub const OVERSIZED_REQUEST_CODE: i32 = -32701; -/// Oversized response error code. -pub const OVERSIZED_RESPONSE_CODE: i32 = -32702; -/// Internal error code. -pub const INTERNAL_ERROR_CODE: i32 = -32603; -/// Invalid params error code. -pub const INVALID_PARAMS_CODE: i32 = -32602; -/// Invalid request error code. -pub const INVALID_REQUEST_CODE: i32 = -32600; -/// Method not found error code. -pub const METHOD_NOT_FOUND_CODE: i32 = -32601; -/// Server is busy error code. -pub const SERVER_IS_BUSY_CODE: i32 = -32604; -/// Custom server error when a call failed. -pub const CALL_EXECUTION_FAILED_CODE: i32 = -32000; -/// Unknown error. -pub const UNKNOWN_ERROR_CODE: i32 = -32001; -/// Invalid subscription error code. -pub const INVALID_SUBSCRIPTION_CODE: i32 = -32002; - -/// Parse error message -pub const PARSE_ERROR_MSG: &str = "Parse error"; -/// Oversized request message -pub const OVERSIZED_REQUEST_MSG: &str = "Request is too big"; -/// Oversized response message -pub const OVERSIZED_RESPONSE_MSG: &str = "Response is too big"; -/// Internal error message. -pub const INTERNAL_ERROR_MSG: &str = "Internal error"; -/// Invalid params error message. -pub const INVALID_PARAMS_MSG: &str = "Invalid params"; -/// Invalid request error message. -pub const INVALID_REQUEST_MSG: &str = "Invalid request"; -/// Method not found error message. -pub const METHOD_NOT_FOUND_MSG: &str = "Method not found"; -/// Server is busy error message. -pub const SERVER_IS_BUSY_MSG: &str = "Server is busy, try again later"; -/// Reserved for implementation-defined server-errors. -pub const SERVER_ERROR_MSG: &str = "Server error"; - -/// JSONRPC error code -#[derive(Error, Debug, PartialEq, Copy, Clone)] -pub enum ErrorCode { - /// Invalid JSON was received by the server. - /// An error occurred on the server while parsing the JSON text. - ParseError, - /// The request was too big. - OversizedRequest, - /// The JSON sent is not a valid Request object. - InvalidRequest, - /// The method does not exist / is not available. - MethodNotFound, - /// Server is busy / resources are at capacity. - ServerIsBusy, - /// Invalid method parameter(s). - InvalidParams, - /// Internal JSON-RPC error. - InternalError, - /// Reserved for implementation-defined server-errors. - ServerError(i32), -} - -impl ErrorCode { - /// Returns integer code value - pub const fn code(&self) -> i32 { - use ErrorCode::*; - match *self { - ParseError => PARSE_ERROR_CODE, - OversizedRequest => OVERSIZED_REQUEST_CODE, - InvalidRequest => INVALID_REQUEST_CODE, - MethodNotFound => METHOD_NOT_FOUND_CODE, - ServerIsBusy => SERVER_IS_BUSY_CODE, - InvalidParams => INVALID_PARAMS_CODE, - InternalError => INTERNAL_ERROR_CODE, - ServerError(code) => code, - } - } - - /// Returns the message for the given error code. - pub const fn message(&self) -> &'static str { - use ErrorCode::*; - match self { - ParseError => PARSE_ERROR_MSG, - OversizedRequest => OVERSIZED_REQUEST_MSG, - InvalidRequest => INVALID_REQUEST_MSG, - MethodNotFound => METHOD_NOT_FOUND_MSG, - ServerIsBusy => SERVER_IS_BUSY_MSG, - InvalidParams => INVALID_PARAMS_MSG, - InternalError => INTERNAL_ERROR_MSG, - ServerError(_) => SERVER_ERROR_MSG, - } - } -} - -impl fmt::Display for ErrorCode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}: {}", self.code(), self.message()) - } -} - -impl From for ErrorCode { - fn from(code: i32) -> Self { - use ErrorCode::*; - match code { - PARSE_ERROR_CODE => ParseError, - OVERSIZED_REQUEST_CODE => OversizedRequest, - INVALID_REQUEST_CODE => InvalidRequest, - METHOD_NOT_FOUND_CODE => MethodNotFound, - INVALID_PARAMS_CODE => InvalidParams, - INTERNAL_ERROR_CODE => InternalError, - code => ServerError(code), - } - } -} - -impl<'a> serde::Deserialize<'a> for ErrorCode { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'a>, - { - let code: i32 = Deserialize::deserialize(deserializer)?; - Ok(ErrorCode::from(code)) - } -} - -impl serde::Serialize for ErrorCode { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_i32(self.code()) - } -} - -/// Create a invalid subscription ID error. -pub fn invalid_subscription_err(data: Option<&RawValue>) -> ErrorObject { - ErrorObject::new(ErrorCode::ServerError(INVALID_SUBSCRIPTION_CODE), data) -} - -#[cfg(test)] -mod tests { - use super::{ErrorCode, ErrorObject, Id, RpcError, TwoPointZero}; - - #[test] - fn deserialize_works() { - let ser = r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#; - let exp = RpcError { - jsonrpc: TwoPointZero, - error: ErrorObject { code: ErrorCode::ParseError, message: "Parse error".into(), data: None }, - id: Id::Null, - }; - let err: RpcError = serde_json::from_str(ser).unwrap(); - assert_eq!(exp, err); - } - - #[test] - fn deserialize_with_optional_data() { - let ser = r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error", "data":"vegan"},"id":null}"#; - let data = serde_json::value::to_raw_value(&"vegan").unwrap(); - let exp = RpcError { - jsonrpc: TwoPointZero, - error: ErrorObject { code: ErrorCode::ParseError, message: "Parse error".into(), data: Some(&*data) }, - id: Id::Null, - }; - let err: RpcError = serde_json::from_str(ser).unwrap(); - assert_eq!(exp, err); - } - - #[test] - fn deserialized_error_with_quoted_str() { - let raw = r#"{ - "error": { - "code": 1002, - "message": "desc: \"Could not decode `ChargeAssetTxPayment::asset_id`\" } })", - "data": "\\\"validate_transaction\\\"" - }, - "id": 7, - "jsonrpc": "2.0" - }"#; - let err: RpcError = serde_json::from_str(raw).unwrap(); - - let data = serde_json::value::to_raw_value(&"\\\"validate_transaction\\\"").unwrap(); - - assert_eq!( - err, - RpcError { - error: ErrorObject { - code: 1002.into(), - message: "desc: \"Could not decode `ChargeAssetTxPayment::asset_id`\" } })".into(), - data: Some(&*data), - }, - id: Id::Number(7), - jsonrpc: TwoPointZero, - } - ); - } - - #[test] - fn serialize_works() { - let exp = r#"{"jsonrpc":"2.0","error":{"code":-32603,"message":"Internal error"},"id":1337}"#; - let err = RpcError { - jsonrpc: TwoPointZero, - error: ErrorObject { code: ErrorCode::InternalError, message: "Internal error".into(), data: None }, - id: Id::Number(1337), - }; - let ser = serde_json::to_string(&err).unwrap(); - assert_eq!(exp, ser); - } -} diff --git a/types/src/v2/mod.rs b/types/src/v2/mod.rs deleted file mode 100644 index d3dcfdb245..0000000000 --- a/types/src/v2/mod.rs +++ /dev/null @@ -1,41 +0,0 @@ -// 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. - -//! Types to handle in- and outgoing JSON-RPC requests and subscriptions according to the [spec](https://www.jsonrpc.org/specification). - -/// JSON-RPC error related types. -pub mod error; -/// JSON_RPC params related types. -pub mod params; -/// JSON-RPC request object related types -pub mod request; -/// JSON-RPC response object related types. -pub mod response; - -pub use error::{ErrorCode, ErrorObject, RpcError}; -pub use params::{Id, Params, ParamsSequence, ParamsSer, SubscriptionId, TwoPointZero}; -pub use request::{InvalidRequest, Notification, NotificationSer, Request, RequestSer}; -pub use response::{Response, SubscriptionPayload, SubscriptionResponse}; diff --git a/utils/src/client.rs b/utils/src/client.rs deleted file mode 100644 index e765472e6c..0000000000 --- a/utils/src/client.rs +++ /dev/null @@ -1,49 +0,0 @@ -// 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. - -//! Shared utilities for `jsonrpsee` clients. - -#[doc(hidden)] -pub mod __reexports { - pub use jsonrpsee_types::{to_json_value, v2::ParamsSer}; -} - -#[macro_export] -/// Convert the given values to a [`jsonrpsee_types::v2::ParamsSer`] as expected by a jsonrpsee Client (http or websocket). -macro_rules! rpc_params { - ($($param:expr),*) => { - { - let mut __params = vec![]; - $( - __params.push($crate::client::__reexports::to_json_value($param).expect("json serialization is infallible; qed.")); - )* - Some($crate::client::__reexports::ParamsSer::Array(__params)) - } - }; - () => { - None - } -} diff --git a/ws-client/Cargo.toml b/ws-client/Cargo.toml index e7d94de34d..2282667053 100644 --- a/ws-client/Cargo.toml +++ b/ws-client/Cargo.toml @@ -16,6 +16,7 @@ rustc-hash = "1" futures = { version = "0.3.14", default-features = false, features = ["std"] } http = "0.2" jsonrpsee-types = { path = "../types", version = "0.6.0" } +jsonrpsee-core = { path = "../core", features = ["client"] } pin-project = "1" rustls-native-certs = "0.6.0" serde = "1" @@ -31,7 +32,6 @@ webpki-roots = "0.22.0" [dev-dependencies] env_logger = "0.9.0" jsonrpsee-test-utils = { path = "../test-utils" } -jsonrpsee-utils = { path = "../utils", features = ["client"] } tokio = { version = "1.8", features = ["macros"] } [features] diff --git a/ws-client/src/client.rs b/ws-client/src/client.rs index aa7e90d0db..778f859ec2 100644 --- a/ws-client/src/client.rs +++ b/ws-client/src/client.rs @@ -24,32 +24,32 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use std::convert::TryInto; +use std::time::Duration; + +use crate::helpers::{ + build_unsubscribe_message, call_with_timeout, process_batch_response, process_error_response, process_notification, + process_single_response, process_subscription_response, stop_subscription, +}; +use crate::manager::RequestManager; use crate::transport::{Receiver as WsReceiver, Sender as WsSender, WsHandshakeError, WsTransportClientBuilder}; use crate::types::{ - traits::{Client, SubscriptionClient}, - v2::{Id, Notification, NotificationSer, ParamsSer, RequestSer, Response, RpcError, SubscriptionResponse}, - BatchMessage, CertificateStore, Error, FrontToBack, RegisterNotificationMessage, RequestIdManager, RequestMessage, - Subscription, SubscriptionKind, SubscriptionMessage, TEN_MB_SIZE_BYTES, -}; -use crate::{ - helpers::{ - build_unsubscribe_message, call_with_timeout, process_batch_response, process_error_response, - process_notification, process_single_response, process_subscription_response, stop_subscription, - }, - manager::RequestManager, + ErrorResponse, Id, Notification, NotificationSer, ParamsSer, RequestSer, Response, SubscriptionResponse, + TEN_MB_SIZE_BYTES, }; use async_trait::async_trait; -use futures::{ - channel::{mpsc, oneshot}, - future::Either, - prelude::*, - sink::SinkExt, -}; +use futures::channel::{mpsc, oneshot}; +use futures::future::Either; +use futures::prelude::*; +use futures::sink::SinkExt; use http::uri::{InvalidUri, Uri}; -use tokio::sync::Mutex; - +use jsonrpsee_core::client::{ + BatchMessage, CertificateStore, Client, FrontToBack, RegisterNotificationMessage, RequestIdManager, RequestMessage, + Subscription, SubscriptionClient, SubscriptionKind, SubscriptionMessage, +}; +use jsonrpsee_core::Error; use serde::de::DeserializeOwned; -use std::{convert::TryInto, time::Duration}; +use tokio::sync::Mutex; pub use soketto::handshake::client::Header; @@ -192,7 +192,7 @@ impl<'a> WsClientBuilder<'a> { /// will be dropped. /// /// You can also prevent the subscription being dropped by calling - /// [`Subscription::next()`](crate::types::Subscription) frequently enough such that the buffer capacity doesn't + /// [`Subscription::next()`](../../jsonrpsee_core/client/struct.Subscription.html#method.next) frequently enough such that the buffer capacity doesn't /// exceeds. /// /// **Note**: The actual capacity is `num_senders + max_subscription_capacity` @@ -593,7 +593,7 @@ async fn background_task( } } // Error response - else if let Ok(err) = serde_json::from_slice::(&raw) { + else if let Ok(err) = serde_json::from_slice::(&raw) { tracing::debug!("[backend]: recv error response {:?}", err); if let Err(e) = process_error_response(&mut manager, err) { let _ = front_error.send(e); diff --git a/ws-client/src/helpers.rs b/ws-client/src/helpers.rs index eed9a60318..6527105130 100644 --- a/ws-client/src/helpers.rs +++ b/ws-client/src/helpers.rs @@ -24,16 +24,17 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use std::convert::TryInto; +use std::time::Duration; + use crate::manager::{RequestManager, RequestStatus}; use crate::transport::Sender as WsSender; -use crate::types::v2::{ - Id, Notification, ParamsSer, RequestSer, Response, RpcError, SubscriptionId, SubscriptionResponse, +use crate::types::{ + ErrorResponse, Id, Notification, ParamsSer, RequestSer, Response, SubscriptionId, SubscriptionResponse, }; -use crate::types::{Error, RequestMessage}; use futures::channel::{mpsc, oneshot}; +use jsonrpsee_core::{client::RequestMessage, Error}; use serde_json::Value as JsonValue; -use std::convert::TryInto; -use std::time::Duration; /// Attempts to process a batch response. /// @@ -201,7 +202,7 @@ pub fn build_unsubscribe_message( /// /// Returns `Ok` if the response was successfully sent. /// Returns `Err(_)` if the response ID was not found. -pub fn process_error_response(manager: &mut RequestManager, err: RpcError) -> Result<(), Error> { +pub fn process_error_response(manager: &mut RequestManager, err: ErrorResponse) -> Result<(), Error> { let id = err.id.as_number().copied().ok_or(Error::InvalidRequestId)?; match manager.request_status(&id) { RequestStatus::PendingMethodCall => { diff --git a/ws-client/src/manager.rs b/ws-client/src/manager.rs index faaa8f02cf..337786e4d6 100644 --- a/ws-client/src/manager.rs +++ b/ws-client/src/manager.rs @@ -32,10 +32,13 @@ //! > **Note**: The spec allow number, string or null but this crate only supports numbers. //! - SubscriptionId: unique ID generated by server -use crate::types::{v2::SubscriptionId, Error, JsonValue}; +use std::collections::hash_map::{Entry, HashMap}; + +use crate::types::SubscriptionId; use futures::channel::{mpsc, oneshot}; +use jsonrpsee_core::Error; use rustc_hash::FxHashMap; -use std::collections::hash_map::{Entry, HashMap}; +use serde_json::value::Value as JsonValue; #[derive(Debug)] enum Kind { @@ -308,7 +311,7 @@ impl RequestManager { mod tests { use super::{Error, RequestManager}; use futures::channel::{mpsc, oneshot}; - use jsonrpsee_types::v2::SubscriptionId; + use jsonrpsee_types::SubscriptionId; use serde_json::Value as JsonValue; #[test] diff --git a/ws-client/src/stream.rs b/ws-client/src/stream.rs index bb859e9c9c..85b1e0d39b 100644 --- a/ws-client/src/stream.rs +++ b/ws-client/src/stream.rs @@ -26,12 +26,14 @@ //! Convenience wrapper for a stream (AsyncRead + AsyncWrite) which can either be plain TCP or TLS. -use futures::{ - io::{IoSlice, IoSliceMut}, - prelude::*, -}; +use std::io::Error as IoError; +use std::pin::Pin; +use std::task::Context; +use std::task::Poll; + +use futures::io::{IoSlice, IoSliceMut}; +use futures::prelude::*; use pin_project::pin_project; -use std::{io::Error as IoError, pin::Pin, task::Context, task::Poll}; use tokio::net::TcpStream; use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt}; diff --git a/ws-client/src/tests.rs b/ws-client/src/tests.rs index 8e26d78514..a8f7b7bec5 100644 --- a/ws-client/src/tests.rs +++ b/ws-client/src/tests.rs @@ -25,16 +25,16 @@ // DEALINGS IN THE SOFTWARE. #![cfg(test)] -use crate::types::{ - traits::{Client, SubscriptionClient}, - v2::{ErrorCode, ErrorObject, ParamsSer, RpcError}, - Error, Subscription, -}; +use crate::types::error::{ErrorCode, ErrorObject, ErrorResponse}; +use crate::types::ParamsSer; use crate::WsClientBuilder; +use jsonrpsee_core::client::Subscription; +use jsonrpsee_core::client::{Client, SubscriptionClient}; +use jsonrpsee_core::rpc_params; +use jsonrpsee_core::Error; use jsonrpsee_test_utils::helpers::*; use jsonrpsee_test_utils::mocks::{Id, WebSocketTestServer}; use jsonrpsee_test_utils::TimeoutFutureExt; -use jsonrpsee_utils::rpc_params; use serde_json::Value as JsonValue; #[tokio::test] @@ -250,7 +250,7 @@ async fn run_request_with_response(response: String) -> Result fn assert_error_response(err: Error, exp: ErrorObject) { match &err { Error::Request(e) => { - let this: RpcError = serde_json::from_str(e).unwrap(); + let this: ErrorResponse = serde_json::from_str(e).unwrap(); assert_eq!(this.error, exp); } e => panic!("Expected error: \"{}\", got: {:?}", err, e), diff --git a/ws-client/src/transport.rs b/ws-client/src/transport.rs index 8721f93858..0390dbc5e8 100644 --- a/ws-client/src/transport.rs +++ b/ws-client/src/transport.rs @@ -24,19 +24,18 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::{stream::EitherStream, types::CertificateStore}; +use std::convert::{TryFrom, TryInto}; +use std::io; +use std::net::{SocketAddr, ToSocketAddrs}; +use std::time::Duration; + +use crate::stream::EitherStream; use beef::Cow; use futures::io::{BufReader, BufWriter}; use http::Uri; +use jsonrpsee_core::client::CertificateStore; use soketto::connection; use soketto::handshake::client::{Client as WsHandshakeClient, Header, ServerResponse}; -use std::convert::TryInto; -use std::{ - convert::TryFrom, - io, - net::{SocketAddr, ToSocketAddrs}, - time::Duration, -}; use thiserror::Error; use tokio::net::TcpStream; diff --git a/ws-server/Cargo.toml b/ws-server/Cargo.toml index f7f327f1a5..d3b4e04e3e 100644 --- a/ws-server/Cargo.toml +++ b/ws-server/Cargo.toml @@ -13,7 +13,7 @@ documentation = "https://docs.rs/jsonrpsee-ws-server" futures-channel = "0.3.14" futures-util = { version = "0.3.14", default-features = false, features = ["io", "async-await-macro"] } jsonrpsee-types = { path = "../types", version = "0.6.0" } -jsonrpsee-utils = { path = "../utils", version = "0.6.0", features = ["server"] } +jsonrpsee-core = { path = "../core", version = "0.6.0", features = ["server"] } tracing = "0.1" serde_json = { version = "1", features = ["raw_value"] } soketto = "0.7.1" diff --git a/ws-server/src/future.rs b/ws-server/src/future.rs index fd2964d194..9ce47508cb 100644 --- a/ws-server/src/future.rs +++ b/ws-server/src/future.rs @@ -26,16 +26,15 @@ //! Utilities for handling async code. -use crate::types::error::Error; -use futures_util::future::FutureExt; -use futures_util::task::AtomicWaker; use std::future::Future; use std::pin::Pin; -use std::sync::{ - atomic::{AtomicBool, Ordering}, - Arc, Weak, -}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Weak}; use std::task::{Context, Poll}; + +use futures_util::future::FutureExt; +use futures_util::task::AtomicWaker; +use jsonrpsee_core::Error; use tokio::time::{self, Duration, Interval}; /// Polling for server stop monitor interval in milliseconds. diff --git a/ws-server/src/lib.rs b/ws-server/src/lib.rs index d2c02a9870..514253b670 100644 --- a/ws-server/src/lib.rs +++ b/ws-server/src/lib.rs @@ -39,7 +39,7 @@ mod server; mod tests; pub use future::{ServerHandle as WsServerHandle, ShutdownWaiter as WsShutdownWaiter}; +pub use jsonrpsee_core::server::rpc_module::{RpcModule, SubscriptionSink}; pub use jsonrpsee_types as types; -pub use jsonrpsee_utils::server::rpc_module::{RpcModule, SubscriptionSink}; pub use server::{Builder as WsServerBuilder, Server as WsServer}; pub use tracing; diff --git a/ws-server/src/server.rs b/ws-server/src/server.rs index f60df7e682..8adaef98fc 100644 --- a/ws-server/src/server.rs +++ b/ws-server/src/server.rs @@ -21,7 +21,7 @@ // 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 background_task WITH THE SOFTWARE OR THE USE OR OTHER +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. use std::future::Future; @@ -30,29 +30,23 @@ use std::pin::Pin; use std::task::{Context, Poll}; use crate::future::{FutureDriver, ServerHandle, StopMonitor}; -use crate::types::{ - error::Error, - middleware::Middleware, - v2::{ErrorCode, Id, Request}, - TEN_MB_SIZE_BYTES, -}; +use crate::types::error::ErrorCode; +use crate::types::{Id, Request, TEN_MB_SIZE_BYTES}; use futures_channel::mpsc; -use futures_util::future::join_all; -use futures_util::future::FutureExt; +use futures_util::future::{join_all, FutureExt}; use futures_util::io::{BufReader, BufWriter}; use futures_util::stream::StreamExt; +use jsonrpsee_core::middleware::Middleware; +use jsonrpsee_core::server::helpers::{collect_batch_response, prepare_error, MethodSink}; +use jsonrpsee_core::server::resource_limiting::Resources; +use jsonrpsee_core::server::rpc_module::{ConnectionId, MethodResult, Methods}; +use jsonrpsee_core::Error; use soketto::connection::Error as SokettoError; use soketto::handshake::{server::Response, Server as SokettoServer}; use soketto::Sender; use tokio::net::{TcpListener, TcpStream, ToSocketAddrs}; use tokio_util::compat::{Compat, TokioAsyncReadCompatExt}; -use jsonrpsee_utils::server::{ - helpers::{collect_batch_response, prepare_error, MethodSink}, - resource_limiting::Resources, - rpc_module::{ConnectionId, MethodResult, Methods}, -}; - /// Default maximum connections allowed. const MAX_CONNECTIONS: u64 = 100; @@ -577,13 +571,14 @@ impl Builder { Ok(self) } - /// Add a middleware to the builder [`Middleware`](../jsonrpsee_types/middleware/trait.Middleware.html). + /// Add a middleware to the builder [`Middleware`](../jsonrpsee_core/middleware/trait.Middleware.html). /// /// ``` - /// use jsonrpsee_types::middleware::Middleware; - /// use jsonrpsee_ws_server::WsServerBuilder; /// use std::time::Instant; /// + /// use jsonrpsee_core::middleware::Middleware; + /// use jsonrpsee_ws_server::WsServerBuilder; + /// /// #[derive(Clone)] /// struct MyMiddleware; /// diff --git a/ws-server/src/tests.rs b/ws-server/src/tests.rs index 48a5b5e9f0..cc2225c6b5 100644 --- a/ws-server/src/tests.rs +++ b/ws-server/src/tests.rs @@ -25,20 +25,21 @@ // DEALINGS IN THE SOFTWARE. #![cfg(test)] +use std::fmt; +use std::net::SocketAddr; +use std::time::Duration; -use crate::types::error::{CallError, Error}; -use crate::types::v2::{self, Response, RpcError}; -use crate::types::DeserializeOwned; +use crate::types::error::CallError; +use crate::types::{self, ErrorResponse, Response}; use crate::{future::ServerHandle, RpcModule, WsServerBuilder}; use anyhow::anyhow; use futures_util::future::join; +use jsonrpsee_core::{to_json_raw_value, DeserializeOwned, Error}; use jsonrpsee_test_utils::helpers::*; use jsonrpsee_test_utils::mocks::{Id, TestContext, WebSocketTestClient, WebSocketTestError}; use jsonrpsee_test_utils::TimeoutFutureExt; -use jsonrpsee_types::to_json_raw_value; -use jsonrpsee_types::v2::error::invalid_subscription_err; +use jsonrpsee_types::error::invalid_subscription_err; use serde_json::Value as JsonValue; -use std::{fmt, net::SocketAddr, time::Duration}; use tracing_subscriber::{EnvFilter, FmtSubscriber}; fn init_logger() { @@ -610,11 +611,11 @@ async fn unsubscribe_twice_should_indicate_error() { let unsub_call = call("unsubscribe_hello", vec![sub_id], Id::Num(2)); let unsub_2 = client.send_request_text(unsub_call).await.unwrap(); - let unsub_2_err: RpcError = serde_json::from_str(&unsub_2).unwrap(); + let unsub_2_err: ErrorResponse = serde_json::from_str(&unsub_2).unwrap(); let sub_id = to_json_raw_value(&sub_id).unwrap(); let err = Some(to_json_raw_value(&format!("Invalid subscription ID={}", sub_id)).unwrap()); - assert_eq!(unsub_2_err, RpcError::new(invalid_subscription_err(err.as_deref()), v2::Id::Number(2))); + assert_eq!(unsub_2_err, ErrorResponse::new(invalid_subscription_err(err.as_deref()), types::Id::Number(2))); } #[tokio::test] @@ -625,7 +626,7 @@ async fn unsubscribe_wrong_sub_id_type() { let unsub = client.send_request_text(call("unsubscribe_hello", vec!["string_is_not_supported"], Id::Num(0))).await.unwrap(); - let unsub_2_err: RpcError = serde_json::from_str(&unsub).unwrap(); + let unsub_2_err: ErrorResponse = serde_json::from_str(&unsub).unwrap(); let err = Some(to_json_raw_value(&"Invalid subscription ID type, must be integer").unwrap()); - assert_eq!(unsub_2_err, RpcError::new(invalid_subscription_err(err.as_deref()), v2::Id::Number(0))); + assert_eq!(unsub_2_err, ErrorResponse::new(invalid_subscription_err(err.as_deref()), types::Id::Number(0))); }