pub struct JrConnectionCx<Role: JrRole> { /* private fields */ }Expand description
Trait for types that can provide transport for JSON-RPC messages.
Implementations of this trait bridge between the internal protocol channels
(which carry jsonrpcmsg::Message) and the actual I/O mechanism (byte streams,
in-process channels, network sockets, etc.).
The transport layer is responsible only for moving jsonrpcmsg::Message in and out.
It has no knowledge of protocol semantics like request/response correlation, ID assignment,
or handler dispatch - those are handled by the protocol layer in JrConnection.
§Example
See ByteStreams for the standard byte stream implementation.
Connection context for sending messages and spawning tasks.
This is the primary handle for interacting with the JSON-RPC connection from within handler callbacks. You can use it to:
- Send requests and notifications to the other side
- Spawn concurrent tasks that run alongside the connection
- Respond to requests (via
JrRequestCxwhich wraps this)
§Cloning
JrConnectionCx is cheaply cloneable - all clones refer to the same underlying connection.
This makes it easy to share across async tasks.
§Event Loop and Concurrency
Handler callbacks run on the event loop, which means the connection cannot process new
messages while your handler is running. Use spawn to offload any
expensive or blocking work to concurrent tasks.
See the Event Loop and Concurrency section for more details.
Implementations§
Source§impl<Role: JrRole> JrConnectionCx<Role>
impl<Role: JrRole> JrConnectionCx<Role>
Sourcepub fn spawn(
&self,
task: impl IntoFuture<Output = Result<(), Error>, IntoFuture: Send + 'static>,
) -> Result<(), Error>
pub fn spawn( &self, task: impl IntoFuture<Output = Result<(), Error>, IntoFuture: Send + 'static>, ) -> Result<(), Error>
Spawns a task that will run so long as the JSON-RPC connection is being served.
This is the primary mechanism for offloading expensive work from handler callbacks to avoid blocking the event loop. Spawned tasks run concurrently with the connection, allowing the server to continue processing messages.
§Event Loop
Handler callbacks run on the event loop, which cannot process new messages while
your handler is running. Use spawn for any expensive operations:
connection.on_receive_request(async |req: ProcessRequest, request_cx, cx| {
// Clone cx for the spawned task
cx.spawn({
let connection_cx = cx.clone();
async move {
let result = expensive_operation(&req.data).await?;
connection_cx.send_notification(ProcessComplete { result })?;
Ok(())
}
})?;
// Respond immediately
request_cx.respond(ProcessResponse { result: "started".into() })
})§Errors
If the spawned task returns an error, the entire server will shut down.
Sourcepub fn spawn_connection<H: JrMessageHandler>(
&self,
connection: JrConnection<H>,
serve_future: impl FnOnce(JrConnection<H>) -> BoxFuture<'static, Result<(), Error>>,
) -> Result<JrConnectionCx<H::Role>, Error>
pub fn spawn_connection<H: JrMessageHandler>( &self, connection: JrConnection<H>, serve_future: impl FnOnce(JrConnection<H>) -> BoxFuture<'static, Result<(), Error>>, ) -> Result<JrConnectionCx<H::Role>, Error>
Spawn a JSON-RPC connection in the background and return a JrConnectionCx for sending messages to it.
This is useful for creating multiple connections that communicate with each other, such as implementing proxy patterns or connecting to multiple backend services.
§Arguments
connection: TheJrConnectionto spawn (typically created viaJrConnectionBuilder::connect_to())serve_future: A function that drives the connection (usually|c| Box::pin(c.serve()))
§Returns
A JrConnectionCx that you can use to send requests and notifications to the spawned connection.
§Example: Proxying to a backend connection
// Set up a backend connection
let backend = UntypedRole::builder()
.on_receive_request(async |req: MyRequest, request_cx, _cx| {
request_cx.respond(MyResponse { status: "ok".into() })
})
.connect_to(MockTransport)?;
// Spawn it and get a context to send requests to it
let backend_cx = cx.spawn_connection(backend, |c| Box::pin(c.serve()))?;
// Now you can forward requests to the backend
let response = backend_cx.send_request(MyRequest {}).block_task().await?;Sourcepub fn send_proxied_message_to<End: JrEndpoint, Req: JrRequest<Response: Send>, Notif: JrNotification>(
&self,
end: End,
message: MessageCx<Req, Notif>,
) -> Result<(), Error>where
Role: HasEndpoint<End>,
pub fn send_proxied_message_to<End: JrEndpoint, Req: JrRequest<Response: Send>, Notif: JrNotification>(
&self,
end: End,
message: MessageCx<Req, Notif>,
) -> Result<(), Error>where
Role: HasEndpoint<End>,
Send a request/notification and forward the response appropriately.
The request context’s response type matches the request’s response type, enabling type-safe message forwarding.
Sourcepub fn send_request<Req: JrRequest>(
&self,
request: Req,
) -> JrResponse<Req::Response>
pub fn send_request<Req: JrRequest>( &self, request: Req, ) -> JrResponse<Req::Response>
Send an outgoing request and return a JrResponse for handling the reply.
The returned JrResponse provides methods for receiving the response without
blocking the event loop:
await_when_result_received- Schedule a callback to run when the response arrives (doesn’t block the event loop)block_task- Block the current task until the response arrives (only safe in spawned tasks, not in handlers)
§Anti-Footgun Design
The API intentionally makes it difficult to block on the result directly to prevent the common mistake of blocking the event loop while waiting for a response:
// ❌ This doesn't compile - prevents blocking the event loop
let response = cx.send_request(MyRequest {}).await?;// ✅ Option 1: Schedule callback (safe in handlers)
cx.send_request(MyRequest {})
.await_when_result_received(async |result| {
// Handle the response
Ok(())
})?;
// ✅ Option 2: Block in spawned task (safe because task is concurrent)
cx.spawn({
let cx = cx.clone();
async move {
let response = cx.send_request(MyRequest {})
.block_task()
.await?;
// Process response...
Ok(())
}
})?;Send an outgoing request to the default counterpart role.
This is a convenience method that uses the connection’s remote role.
For explicit control over the target role, use send_request_to.
Sourcepub fn send_request_to<End: JrEndpoint, Req: JrRequest>(
&self,
end: End,
request: Req,
) -> JrResponse<Req::Response>where
Role: HasEndpoint<End>,
pub fn send_request_to<End: JrEndpoint, Req: JrRequest>(
&self,
end: End,
request: Req,
) -> JrResponse<Req::Response>where
Role: HasEndpoint<End>,
Send an outgoing request to a specific counterpart role.
The message will be transformed according to the role’s SendsToRole
implementation before being sent.
Sourcepub fn send_notification<N: JrNotification>(
&self,
notification: N,
) -> Result<(), Error>
pub fn send_notification<N: JrNotification>( &self, notification: N, ) -> Result<(), Error>
Send an outgoing notification to the default counterpart role (no reply expected).
Notifications are fire-and-forget messages that don’t have IDs and don’t expect responses. This method sends the notification immediately and returns.
This is a convenience method that uses the role’s default counterpart.
For explicit control over the target role, use send_notification_to.
cx.send_notification(StatusUpdate {
message: "Processing...".into(),
})?;Sourcepub fn send_notification_to<End: JrEndpoint, N: JrNotification>(
&self,
end: End,
notification: N,
) -> Result<(), Error>where
Role: HasEndpoint<End>,
pub fn send_notification_to<End: JrEndpoint, N: JrNotification>(
&self,
end: End,
notification: N,
) -> Result<(), Error>where
Role: HasEndpoint<End>,
Send an outgoing notification to a specific counterpart role (no reply expected).
The message will be transformed according to the role’s SendsToRole
implementation before being sent.
Sourcepub fn send_error_notification(&self, error: Error) -> Result<(), Error>
pub fn send_error_notification(&self, error: Error) -> Result<(), Error>
Send an error notification (no reply expected).
Sourcepub fn add_dynamic_handler(
&self,
handler: impl JrMessageHandlerSend<Role = Role> + 'static,
) -> Result<DynamicHandlerRegistration<Role>, Error>
pub fn add_dynamic_handler( &self, handler: impl JrMessageHandlerSend<Role = Role> + 'static, ) -> Result<DynamicHandlerRegistration<Role>, Error>
Register a dynamic message handler, used to intercept messages specific to a particular session or some similar modal thing.
Dynamic message handlers are called first for every incoming message.
If they decline to handle the message, then the message is passed to the regular handler chain.
The handler will stay registered until the [DynamicHandlerRegistration] is dropped.
Trait Implementations§
Source§impl<Role: Clone + JrRole> Clone for JrConnectionCx<Role>
impl<Role: Clone + JrRole> Clone for JrConnectionCx<Role>
Source§fn clone(&self) -> JrConnectionCx<Role>
fn clone(&self) -> JrConnectionCx<Role>
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more