Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
26ec635
server: Add a raw method
lexnv Feb 21, 2024
ef24148
server: Register raw methods, blocking or unblocking
lexnv Feb 21, 2024
4f8eb03
proc-macros: Add with-context attribute
lexnv Feb 21, 2024
9eba899
server: Register sync and nonblocking methods for raw API
lexnv Feb 21, 2024
239cafe
examples: Add with context example
lexnv Feb 21, 2024
0de6d95
core: Adjust docs for the raw method registering
lexnv Feb 21, 2024
4ce51c6
proc-macros: Cargo fmt
lexnv Feb 21, 2024
5f2b40a
server: Request Arc<Context> for the raw method callback
lexnv Feb 22, 2024
753224a
proc-macros: Per method raw-method attribute
lexnv Feb 22, 2024
d526b70
examples: Add server raw method
lexnv Feb 22, 2024
c8ff969
tests/ui: Check correct proc-macro behavior
lexnv Feb 22, 2024
b4880b9
tests/ui: Negative test for async with raw methods
lexnv Feb 22, 2024
d2da4b0
tests/ui: Negative test for blocking with raw methods
lexnv Feb 22, 2024
0dc48e9
tests/proc-macros: Ensure unique connection IDs from different clients
lexnv Feb 22, 2024
0029418
tests/integration: Ensure unique connection IDs from different clients
lexnv Feb 22, 2024
3a09a7e
proc-macros: Apply cargo fmt
lexnv Feb 22, 2024
1565927
Register raw method as async method
lexnv Feb 23, 2024
f5202ef
Fix testing
lexnv Feb 23, 2024
8cf1947
core: Fix documentation
lexnv Feb 23, 2024
bd571e1
Merge remote-tracking branch 'origin/master' into lexnv/low-level-con…
lexnv Mar 4, 2024
04dfd25
server: Rename raw method to `module.register_async_with_details`
lexnv Mar 4, 2024
e02ba61
server: Add connection details wrapper
lexnv Mar 4, 2024
7b72d8a
server: Add asyncWithDetails and connection details
lexnv Mar 4, 2024
87585ad
proc-macros: Provide connection details to methods
lexnv Mar 4, 2024
2c50375
Update core/src/server/rpc_module.rs
lexnv Mar 4, 2024
66b6a39
server: Remove connection details builder
lexnv Mar 4, 2024
cfe1aec
server: Refactor `.register_async_with_details` to `.register_async_m…
lexnv Mar 4, 2024
7327d8b
proc-macro: Clarify comment
lexnv Mar 4, 2024
6605409
Merge remote-tracking branch 'origin/lexnv/low-level-context-api-v2' …
lexnv Mar 4, 2024
650e24a
core: Doc hidden for async with details
lexnv Mar 5, 2024
f4d9085
Rename example
lexnv Mar 5, 2024
40d2bcf
Update core/src/server/rpc_module.rs
lexnv Mar 5, 2024
8185193
Merge remote-tracking branch 'origin/lexnv/low-level-context-api-v2' …
lexnv Mar 5, 2024
11ef18c
core: Remove doc(hidden) from ConnectionDetails::id
lexnv Mar 5, 2024
4f459ac
Update core/src/server/rpc_module.rs
niklasad1 Mar 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
server: Add asyncWithDetails and connection details
Signed-off-by: Alexandru Vasile <[email protected]>
  • Loading branch information
lexnv committed Mar 4, 2024
commit 7b72d8afe7bdc73ea024c447f873eef0cd46eac8
37 changes: 34 additions & 3 deletions core/src/server/rpc_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ pub type SyncMethod = Arc<dyn Send + Sync + Fn(Id, Params, MaxResponseSize) -> M
/// Similar to [`SyncMethod`], but represents an asynchronous handler.
pub type AsyncMethod<'a> =
Arc<dyn Send + Sync + Fn(Id<'a>, Params<'a>, ConnectionId, MaxResponseSize) -> BoxFuture<'a, MethodResponse>>;
/// Similar to [`SyncMethod`], but represents an asynchronous handler with connection details.
pub type AsyncMethodWithDetails<'a> =
Arc<dyn Send + Sync + Fn(Id<'a>, Params<'a>, ConnectionDetails, MaxResponseSize) -> BoxFuture<'a, MethodResponse>>;
/// Similar to [`SyncMethod`], but represents a raw handler that has access to the connection Id.
/// Method callback for subscriptions.
pub type SubscriptionMethod<'a> =
Expand All @@ -80,15 +83,17 @@ pub type MaxResponseSize = usize;
/// - a [`mpsc::UnboundedReceiver<String>`] to receive future subscription results
pub type RawRpcResponse = (String, mpsc::Receiver<String>);

#[derive(Debug, Clone)]
#[allow(missing_copy_implementations)]
/// The connection details exposed to the server methods.
pub struct ConnectionDetails {
id: ConnectionId,
}

impl ConnectionDetails {
/// Construct a new [`ConnectionDetails`] with the given connection ID.
pub(crate) fn new(id: ConnectionId) -> Self {
Self { id }
/// Construct a new [`ConnectionDetailsBuilder`].
pub fn builder() -> ConnectionDetailsBuilder {
ConnectionDetailsBuilder { id: 0 }
}

/// Get the connection ID.
Expand All @@ -97,6 +102,26 @@ impl ConnectionDetails {
}
}

#[derive(Debug, Clone)]
#[allow(missing_copy_implementations)]
/// The connection details exposed to the server methods.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why we need the builder?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RpcService should be the last "layer" before the user-callback is called.

We construct the ConnectionDetails in this layer:

let connection_details = ConnectionDetails::builder().id(conn_id).build();
let fut = (callback)(id, params, connection_details, max_response_body_size);
ResponseFuture::future(fut)

I think we could just as easily provide a constructor for this:

impl ConnectionDetails {
  pub fn new(id: ConnectionId) ..
}

The new method needs to be public, because the ConnectionDetails is declared in the core crate. And the ConnectionDetails needs to be instantiated in the server crate as well.

We'd have to eventually change this method to new(id: ConnectionId, extensions: Extenstions) which will be a breaking change (although we should be the only ones building this object).

TLDR; since we'll do a breaking change either way, I think a new method should be fine for now! 🙏

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I see makes sense

Copy link
Collaborator

@jsdw jsdw Mar 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe have it be #[doc(hidden)] and comment that it's not part of the public APi and can change without notice? I don't think anything outside of jsonrpsee needs to build it, so then we could also just use new() instead of a builder (Or maybe eg _new(connection_id) to really emphasise the hiddenness of it but shrug)

pub struct ConnectionDetailsBuilder {
id: ConnectionId,
}

impl ConnectionDetailsBuilder {
/// Set the connection ID.
pub fn id(mut self, id: ConnectionId) -> Self {
self.id = id;
self
}

/// Build the [`ConnectionDetails`].
pub fn build(self) -> ConnectionDetails {
ConnectionDetails { id: self.id }
}
}

/// The error that can occur when [`Methods::call`] or [`Methods::subscribe`] is invoked.
#[derive(thiserror::Error, Debug)]
pub enum MethodsError {
Expand Down Expand Up @@ -149,6 +174,8 @@ pub enum MethodCallback {
Sync(SyncMethod),
/// Asynchronous method handler.
Async(AsyncMethod<'static>),
/// Asynchronous method handler with details.
AsyncWithDetails(AsyncMethodWithDetails<'static>),
/// Subscription method handler.
Subscription(SubscriptionMethod<'static>),
/// Unsubscription method handler.
Expand Down Expand Up @@ -202,6 +229,7 @@ impl Debug for MethodCallback {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Async(_) => write!(f, "Async"),
Self::AsyncWithDetails(_) => write!(f, "AsyncWithDetails"),
Self::Sync(_) => write!(f, "Sync"),
Self::Subscription(_) => write!(f, "Subscription"),
Self::Unsubscription(_) => write!(f, "Unsubscription"),
Expand Down Expand Up @@ -373,6 +401,9 @@ impl Methods {
None => MethodResponse::error(req.id, ErrorObject::from(ErrorCode::MethodNotFound)),
Some(MethodCallback::Sync(cb)) => (cb)(id, params, usize::MAX),
Some(MethodCallback::Async(cb)) => (cb)(id.into_owned(), params.into_owned(), 0, usize::MAX).await,
Some(MethodCallback::AsyncWithDetails(cb)) => {
(cb)(id.into_owned(), params.into_owned(), ConnectionDetails::builder().build(), usize::MAX).await
}
Some(MethodCallback::Subscription(cb)) => {
let conn_state =
SubscriptionState { conn_id: 0, id_provider: &RandomIntegerIdProvider, subscription_permit };
Expand Down
11 changes: 10 additions & 1 deletion server/src/middleware/rpc/layer/rpc_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use std::sync::Arc;
use crate::middleware::rpc::RpcServiceT;
use futures_util::future::BoxFuture;
use jsonrpsee_core::server::{
BoundedSubscriptions, MethodCallback, MethodResponse, MethodSink, Methods, SubscriptionState,
BoundedSubscriptions, ConnectionDetails, MethodCallback, MethodResponse, MethodSink, Methods, SubscriptionState,
};
use jsonrpsee_core::traits::IdProvider;
use jsonrpsee_types::error::{reject_too_many_subscriptions, ErrorCode};
Expand Down Expand Up @@ -94,6 +94,15 @@ impl<'a> RpcServiceT<'a> for RpcService {
let fut = (callback)(id, params, conn_id, max_response_body_size);
ResponseFuture::future(fut)
}
MethodCallback::AsyncWithDetails(callback) => {
let params = params.into_owned();
let id = id.into_owned();

// Note: Add the `Request::extensions` to the connection details when available here.
let connection_details = ConnectionDetails::builder().id(conn_id).build();
let fut = (callback)(id, params, connection_details, max_response_body_size);
ResponseFuture::future(fut)
}
MethodCallback::Sync(callback) => {
let rp = (callback)(id, params, max_response_body_size);
ResponseFuture::ready(rp)
Expand Down