workflow_rpc/server/mod.rs
1//!
2//! RPC server module (native only). This module encapsulates
3//! server-side types used to create an RPC server: [`RpcServer`],
4//! [`RpcHandler`], [`Messenger`], [`Interface`] and the
5//! protocol handlers: [`BorshProtocol`] and [`JsonProtocol`].
6//!
7
8pub mod error;
9mod interface;
10pub mod prelude;
11pub mod protocol;
12pub mod result;
13
14pub use super::error::*;
15pub use crate::encoding::Encoding;
16use crate::imports::*;
17pub use interface::{Interface, Method, Notification};
18pub use protocol::{BorshProtocol, JsonProtocol, ProtocolHandler};
19pub use std::net::SocketAddr;
20pub use tokio::sync::mpsc::UnboundedSender as TokioUnboundedSender;
21pub use workflow_core::task::spawn;
22pub use workflow_websocket::server::{
23 Error as WebSocketError, Message, Result as WebSocketResult, TcpListener, WebSocketConfig,
24 WebSocketCounters, WebSocketHandler, WebSocketReceiver, WebSocketSender, WebSocketServer,
25 WebSocketServerTrait, WebSocketSink,
26};
27pub mod handshake {
28 //! WebSocket handshake helpers
29 pub use workflow_websocket::server::handshake::*;
30}
31use crate::server::result::Result;
32
33///
34/// method!() macro for declaration of RPC method handlers
35///
36/// This macro simplifies creation of async method handler
37/// closures supplied to the RPC dispatch interface. An
38/// async method closure requires to be *Box*ed
39/// and its result must be *Pin*ned, resulting in the following
40/// syntax:
41///
42/// ```ignore
43///
44/// interface.method(Box::new(MyOps::Method, Method::new(|req: MyReq|
45/// Box::pin(
46/// async move {
47/// // ...
48/// Ok(MyResp { })
49/// }
50/// )
51/// )))
52///
53/// ```
54///
55/// The method macro adds the required Box and Pin syntax,
56/// simplifying the declaration as follows:
57///
58/// ```ignore
59/// interface.method(MyOps::Method, method!(
60/// | connection_ctx: ConnectionCtx,
61/// server_ctx: ServerContext,
62/// req: MyReq |
63/// async move {
64/// // ...
65/// Ok(MyResp { })
66/// }))
67/// ```
68///
69pub use workflow_rpc_macros::server_method as method;
70
71///
72/// notification!() macro for declaration of RPC notification handlers
73///
74/// This macro simplifies creation of async notification handler
75/// closures supplied to the RPC notification interface. An
76/// async notification closure requires to be *Box*ed
77/// and its result must be *Pin*ned, resulting in the following
78/// syntax:
79///
80/// ```ignore
81///
82/// interface.notification(MyOps::Notify,Box::new(Notification::new(|msg: MyMsg|
83/// Box::pin(
84/// async move {
85/// // ...
86/// Ok(())
87/// }
88/// )
89/// )))
90///
91/// ```
92///
93/// The notification macro adds the required Box and Pin syntax,
94/// simplifying the declaration as follows:
95///
96/// ```ignore
97/// interface.notification(MyOps::Notify, notification!(|msg: MyMsg| async move {
98/// // ...
99/// Ok(())
100/// }))
101/// ```
102///
103pub use workflow_rpc_macros::server_notification as notification;
104
105/// A basic example RpcContext, can be used to keep track of
106/// connected peers.
107#[derive(Debug, Clone)]
108pub struct RpcContext {
109 /// Network address of the connected peer.
110 pub peer: SocketAddr,
111}
112
113/// [`RpcHandler`] - a server-side event handler for RPC connections.
114#[async_trait]
115pub trait RpcHandler: Send + Sync + 'static {
116 /// Per-connection context produced during the handshake and supplied to
117 /// all subsequent RPC calls from that connection.
118 type Context: Send + Sync;
119
120 /// Called to determine if the connection should be accepted.
121 fn accept(&self, _peer: &SocketAddr) -> bool {
122 true
123 }
124
125 /// Connection notification - issued when the server has opened a WebSocket
126 /// connection, before any other interactions occur. The supplied argument
127 /// is the [`SocketAddr`] of the incoming connection. This function should
128 /// return [`WebSocketResult::Ok`] if the server accepts connection or
129 /// [`WebSocketError`] if the connection is rejected. This function can
130 /// be used to reject connections based on a ban list.
131 async fn connect(self: Arc<Self>, _peer: &SocketAddr) -> WebSocketResult<()> {
132 Ok(())
133 }
134
135 /// [`RpcHandler::handshake()`] is called right after [`RpcHandler::connect()`]
136 /// and is provided with a [`WebSocketSender`] and [`WebSocketReceiver`] channels
137 /// which can be used to communicate with the underlying WebSocket connection
138 /// to negotiate a connection. The function also receives the `&peer` ([`SocketAddr`])
139 /// of the connection and a [`Messenger`] struct. The [`Messenger`] struct can
140 /// be used to post notifications to the given connection as well as to close it.
141 /// If negotiation is successful, this function should return a `ConnectionContext`
142 /// defined as [`Self::Context`]. This context will be supplied to all subsequent
143 /// RPC calls received from this connection. The [`Messenger`] struct can be
144 /// cloned and captured within the `ConnectionContext`. This allows an RPC
145 /// method handler to later capture and post notifications to the connection
146 /// asynchronously.
147 async fn handshake(
148 self: Arc<Self>,
149 peer: &SocketAddr,
150 sender: &mut WebSocketSender,
151 receiver: &mut WebSocketReceiver,
152 messenger: Arc<Messenger>,
153 ) -> WebSocketResult<Self::Context>;
154
155 /// Disconnect notification, receives the context and the result containing
156 /// the disconnection reason (can be success if the connection is closed gracefully)
157 async fn disconnect(self: Arc<Self>, _ctx: Self::Context, _result: WebSocketResult<()>) {}
158}
159
160///
161/// The [`Messenger`] struct is supplied to the [`RpcHandler::handshake()`] call at
162/// the connection negotiation time. This structure comes in as [`Arc<Messenger>`]
163/// and can be retained for later processing. It provides two methods: [`Messenger::notify`]
164/// that can be used asynchronously to dispatch RPC notifications to the client
165/// and [`Messenger::close`] that can be used to terminate the RPC connection with
166/// the client.
167///
168#[derive(Debug)]
169pub struct Messenger {
170 encoding: Encoding,
171 sink: WebSocketSink,
172}
173
174impl Messenger {
175 /// Create a new messenger for a connection, bound to the given encoding
176 /// and WebSocket sink used to dispatch outgoing messages.
177 pub fn new(encoding: Encoding, sink: &WebSocketSink) -> Self {
178 Self {
179 encoding,
180 sink: sink.clone(),
181 }
182 }
183
184 /// Close the WebSocket connection. The server checks for the connection channel
185 /// for the dispatch of this message and relays it to the client as well as
186 /// proactively terminates the connection.
187 pub fn close(&self) -> Result<()> {
188 self.sink.send(Message::Close(None))?;
189 Ok(())
190 }
191
192 /// Post notification message to the WebSocket connection
193 pub async fn notify<Ops, Msg>(&self, op: Ops, msg: Msg) -> Result<()>
194 where
195 Ops: OpsT,
196 Msg: BorshSerialize + BorshDeserialize + Serialize + Send + Sync + 'static,
197 {
198 match self.encoding {
199 Encoding::Borsh => {
200 self.sink
201 .send(protocol::borsh::create_serialized_notification_message(
202 op, msg,
203 )?)?;
204 }
205 Encoding::SerdeJson => {
206 self.sink
207 .send(protocol::serde_json::create_serialized_notification_message(op, msg)?)?;
208 }
209 }
210
211 Ok(())
212 }
213
214 /// Serialize message into a [`tungstenite::Message`] for direct websocket delivery.
215 /// Once serialized it can be relayed using [`Messenger::send_raw_message()`].
216 pub fn serialize_notification_message<Ops, Msg>(
217 &self,
218 op: Ops,
219 msg: Msg,
220 ) -> Result<tungstenite::Message>
221 where
222 Ops: OpsT,
223 Msg: MsgT,
224 {
225 match self.encoding {
226 Encoding::Borsh => Ok(protocol::borsh::create_serialized_notification_message(
227 op, msg,
228 )?),
229 Encoding::SerdeJson => {
230 Ok(protocol::serde_json::create_serialized_notification_message(op, msg)?)
231 }
232 }
233 }
234
235 /// Send a raw [`tungstenite::Message`] via the websocket tokio channel.
236 pub fn send_raw_message(&self, msg: tungstenite::Message) -> Result<()> {
237 self.sink.send(msg)?;
238 Ok(())
239 }
240
241 /// Provides direct access to the underlying tokio channel.
242 pub fn sink(&self) -> &WebSocketSink {
243 &self.sink
244 }
245
246 /// Get encoding of the current messenger.
247 pub fn encoding(&self) -> Encoding {
248 self.encoding
249 }
250}
251
252/// WebSocket processor in charge of managing
253/// WRPC Request/Response interactions.
254#[derive(Clone)]
255struct RpcWebSocketHandler<ServerContext, ConnectionContext, Protocol, Ops>
256where
257 Ops: OpsT,
258 ServerContext: Clone + Send + Sync + 'static,
259 ConnectionContext: Clone + Send + Sync + 'static,
260 Protocol: ProtocolHandler<ServerContext, ConnectionContext, Ops> + Send + Sync + 'static,
261{
262 rpc_handler: Arc<dyn RpcHandler<Context = ConnectionContext>>,
263 protocol: Arc<Protocol>,
264 enable_async_handling: bool,
265 _server_ctx: PhantomData<ServerContext>,
266 _ops: PhantomData<Ops>,
267}
268
269impl<ServerContext, ConnectionContext, Protocol, Ops>
270 RpcWebSocketHandler<ServerContext, ConnectionContext, Protocol, Ops>
271where
272 Ops: OpsT,
273 ServerContext: Clone + Send + Sync + 'static,
274 ConnectionContext: Clone + Send + Sync + 'static,
275 Protocol: ProtocolHandler<ServerContext, ConnectionContext, Ops> + Send + Sync + 'static,
276{
277 pub fn new(
278 rpc_handler: Arc<dyn RpcHandler<Context = ConnectionContext>>,
279 interface: Arc<Interface<ServerContext, ConnectionContext, Ops>>,
280 enable_async_handling: bool,
281 ) -> Self {
282 let protocol = Arc::new(Protocol::new(interface));
283 Self {
284 rpc_handler,
285 protocol,
286 enable_async_handling,
287 _server_ctx: PhantomData,
288 _ops: PhantomData,
289 }
290 }
291}
292
293#[async_trait]
294impl<ServerContext, ConnectionContext, Protocol, Ops> WebSocketHandler
295 for RpcWebSocketHandler<ServerContext, ConnectionContext, Protocol, Ops>
296where
297 Ops: OpsT,
298 ServerContext: Clone + Send + Sync + 'static,
299 ConnectionContext: Clone + Send + Sync + 'static,
300 Protocol: ProtocolHandler<ServerContext, ConnectionContext, Ops> + Send + Sync + 'static,
301{
302 type Context = ConnectionContext;
303
304 fn accept(&self, peer: &SocketAddr) -> bool {
305 self.rpc_handler.accept(peer)
306 }
307
308 async fn connect(self: &Arc<Self>, peer: &SocketAddr) -> WebSocketResult<()> {
309 self.rpc_handler.clone().connect(peer).await
310 }
311
312 async fn disconnect(self: &Arc<Self>, ctx: Self::Context, result: WebSocketResult<()>) {
313 self.rpc_handler.clone().disconnect(ctx, result).await
314 }
315
316 async fn handshake(
317 self: &Arc<Self>,
318 peer: &SocketAddr,
319 sender: &mut WebSocketSender,
320 receiver: &mut WebSocketReceiver,
321 sink: &WebSocketSink,
322 ) -> WebSocketResult<Self::Context> {
323 let messenger = Arc::new(Messenger::new(self.protocol.encoding(), sink));
324
325 self.rpc_handler
326 .clone()
327 .handshake(peer, sender, receiver, messenger)
328 .await
329 }
330
331 async fn message(
332 self: &Arc<Self>,
333 connection_ctx: &Self::Context,
334 msg: Message,
335 sink: &WebSocketSink,
336 ) -> WebSocketResult<()> {
337 let connection_ctx = (*connection_ctx).clone();
338 if self.enable_async_handling {
339 let sink = sink.clone();
340 let this = self.clone();
341 spawn(async move {
342 this.protocol
343 .handle_message(connection_ctx, msg, &sink)
344 .await
345 });
346 Ok(())
347 } else {
348 self.protocol
349 .handle_message(connection_ctx, msg, sink)
350 .await
351 }
352 }
353}
354
355/// [`RpcServer`] - a server-side object that listens
356/// for incoming websocket connections and delegates interaction
357/// with them to the supplied interfaces: [`RpcHandler`] (for RPC server
358/// management) and [`Interface`] (for method and notification dispatch).
359#[derive(Clone)]
360pub struct RpcServer {
361 ws_server: Arc<dyn WebSocketServerTrait>,
362}
363
364impl RpcServer {
365 /// Create a new [`RpcServer`] supplying an [`Arc`] of the previously-created
366 /// [`RpcHandler`] trait and the [`Interface`] struct.
367 /// This method takes 4 generics:
368 /// - `ConnectionContext`: a struct used as [`RpcHandler::Context`] to
369 /// represent the connection. This struct is passed to each RPC method
370 /// and notification call.
371 /// - `ServerContext`: a struct supplied to the [`Interface`] at the
372 /// Interface creation time. This struct is passed to each RPC method
373 /// and notification call.
374 /// - `Protocol`: A protocol type used for the RPC message serialization
375 /// and deserialization (this can be omitted by using [`RpcServer::new_with_encoding`])
376 /// - `Ops`: A data type (index or an `enum`) representing the RPC method
377 /// or notification.
378 pub fn new<ServerContext, ConnectionContext, Protocol, Ops>(
379 rpc_handler: Arc<dyn RpcHandler<Context = ConnectionContext>>,
380 interface: Arc<Interface<ServerContext, ConnectionContext, Ops>>,
381 counters: Option<Arc<WebSocketCounters>>,
382 enable_async_handling: bool,
383 ) -> RpcServer
384 where
385 ServerContext: Clone + Send + Sync + 'static,
386 ConnectionContext: Clone + Send + Sync + 'static,
387 Protocol: ProtocolHandler<ServerContext, ConnectionContext, Ops> + Send + Sync + 'static,
388 Ops: OpsT,
389 {
390 let ws_handler = Arc::new(RpcWebSocketHandler::<
391 ServerContext,
392 ConnectionContext,
393 Protocol,
394 Ops,
395 >::new(rpc_handler, interface, enable_async_handling));
396
397 let ws_server = WebSocketServer::new(ws_handler, counters);
398 RpcServer { ws_server }
399 }
400 /// Create a new [`RpcServer`] supplying an [`Arc`] of the previously-created
401 /// [`RpcHandler`] trait and the [`Interface`] struct.
402 /// This method takes 4 generics:
403 /// - `ConnectionContext`: a struct used as [`RpcHandler::Context`] to
404 /// represent the connection. This struct is passed to each RPC method
405 /// and notification call.
406 /// - `ServerContext`: a struct supplied to the [`Interface`] at the
407 /// Interface creation time. This struct is passed to each RPC method
408 /// and notification call.
409 /// - `Ops`: A data type (index or an `enum`) representing the RPC method
410 /// or notification.
411 /// - `Id`: A data type representing a message `Id` - this type must implement
412 /// the [`id::Generator`](crate::id::Generator) trait. Implementation for default
413 /// Ids such as [`Id32`] and [`Id64`] can be found in the [`id`](crate::id) module.
414 ///
415 /// This function call receives an `encoding`: [`Encoding`] argument containing
416 /// [`Encoding::Borsh`] or [`Encoding::SerdeJson`], based on which it will
417 /// instantiate the corresponding protocol handler ([`BorshProtocol`] or
418 /// [`JsonProtocol`] respectively).
419 ///
420 /// `enable_async_handling` is a boolean flag that determines if the server
421 /// should spawn a new async task for each incoming message. If set to `false`,
422 /// the server will handle message intake synchronously where each message
423 /// is posted to the underlying handler one-at-a-time. (i.e. RPC awaits for the
424 /// message intake processing to be complete before the next message arrives).
425 /// If `true`, each message is dispatched via a new async task.
426 ///
427 pub fn new_with_encoding<ServerContext, ConnectionContext, Ops, Id>(
428 encoding: Encoding,
429 rpc_handler: Arc<dyn RpcHandler<Context = ConnectionContext>>,
430 interface: Arc<Interface<ServerContext, ConnectionContext, Ops>>,
431 counters: Option<Arc<WebSocketCounters>>,
432 enable_async_handling: bool,
433 ) -> RpcServer
434 where
435 ServerContext: Clone + Send + Sync + 'static,
436 ConnectionContext: Clone + Send + Sync + 'static,
437 Ops: OpsT,
438 Id: IdT,
439 {
440 match encoding {
441 Encoding::Borsh => {
442 RpcServer::new::<
443 ServerContext,
444 ConnectionContext,
445 BorshProtocol<ServerContext, ConnectionContext, Ops, Id>,
446 Ops,
447 >(rpc_handler, interface, counters, enable_async_handling)
448 }
449 Encoding::SerdeJson => {
450 RpcServer::new::<
451 ServerContext,
452 ConnectionContext,
453 JsonProtocol<ServerContext, ConnectionContext, Ops, Id>,
454 Ops,
455 >(rpc_handler, interface, counters, enable_async_handling)
456 }
457 }
458 }
459
460 /// Bind network interface address to the `TcpListener`
461 pub async fn bind(&self, addr: &str) -> WebSocketResult<TcpListener> {
462 let addr = addr.replace("wrpc://", "");
463 self.ws_server.clone().bind(&addr).await
464 }
465
466 /// Start listening for incoming RPC connections on the supplied `TcpListener`
467 pub async fn listen(
468 &self,
469 listener: TcpListener,
470 config: Option<WebSocketConfig>,
471 ) -> WebSocketResult<()> {
472 self.ws_server.clone().listen(listener, config).await
473 }
474
475 /// Signal the listening task to stop
476 pub fn stop(&self) -> WebSocketResult<()> {
477 self.ws_server.stop()
478 }
479
480 /// Blocks until the listening task has stopped
481 pub async fn join(&self) -> WebSocketResult<()> {
482 self.ws_server.join().await
483 }
484
485 /// Signal the listening task to stop and block
486 /// until it has stopped
487 pub async fn stop_and_join(&self) -> WebSocketResult<()> {
488 self.ws_server.stop_and_join().await
489 }
490}