Skip to main content

xrpc/channel/
message.rs

1//! Message-level channel for RPC communication (Layer 2).
2
3use async_trait::async_trait;
4use std::fmt::Debug;
5use std::sync::Arc;
6
7use crate::codec::{BincodeCodec, Codec};
8use crate::error::TransportResult;
9use crate::message::Message;
10use crate::transport::{FrameTransport, TransportStats};
11
12/// Message-level channel for RPC communication (Layer 2).
13#[async_trait]
14pub trait MessageChannel<C: Codec = BincodeCodec>: Send + Sync + Debug {
15    /// Send an RPC message.
16    async fn send(&self, message: &Message<C>) -> TransportResult<()>;
17
18    /// Receive an RPC message.
19    ///
20    /// A `TransportError::Timeout` may leave the channel connected only when the receive made no
21    /// partial framing or commit progress and a later receive is safe. Implementations must make
22    /// the channel terminal before returning a timeout after partial progress. RPC serving loops
23    /// use this contract to retry a timeout only while [`MessageChannel::is_connected`] is true.
24    async fn recv(&self) -> TransportResult<Message<C>>;
25
26    /// Check if the channel can safely continue message operations.
27    ///
28    /// In particular, this must return `false` after a receive timeout that consumed partial
29    /// framing or commit state and left the message boundary unusable.
30    fn is_connected(&self) -> bool;
31
32    /// Check if the channel is healthy.
33    fn is_healthy(&self) -> bool {
34        self.is_connected()
35    }
36
37    /// Close the channel.
38    async fn close(&self) -> TransportResult<()>;
39
40    /// Get transport statistics.
41    fn stats(&self) -> Option<TransportStats> {
42        None
43    }
44}
45
46/// Adapter that wraps a FrameTransport to provide MessageChannel functionality.
47#[derive(Debug)]
48pub struct MessageChannelAdapter<F: FrameTransport, C: Codec = BincodeCodec> {
49    inner: F,
50    _codec: std::marker::PhantomData<C>,
51}
52
53impl<F: FrameTransport> MessageChannelAdapter<F, BincodeCodec> {
54    /// Create a new adapter with default BincodeCodec.
55    pub fn new(transport: F) -> Self {
56        Self {
57            inner: transport,
58            _codec: std::marker::PhantomData,
59        }
60    }
61}
62
63impl<F: FrameTransport, C: Codec> MessageChannelAdapter<F, C> {
64    /// Create a new adapter with a specific codec.
65    pub fn with_codec(transport: F) -> Self {
66        Self {
67            inner: transport,
68            _codec: std::marker::PhantomData,
69        }
70    }
71
72    /// Get a reference to the inner transport.
73    pub fn inner(&self) -> &F {
74        &self.inner
75    }
76
77    /// Consume the adapter and return the inner transport.
78    pub fn into_inner(self) -> F {
79        self.inner
80    }
81}
82
83#[async_trait]
84impl<F: FrameTransport, C: Codec + Default> MessageChannel<C> for MessageChannelAdapter<F, C> {
85    async fn send(&self, message: &Message<C>) -> TransportResult<()> {
86        let bytes = message.encode()?;
87        self.inner.send_frame(&bytes).await
88    }
89
90    async fn recv(&self) -> TransportResult<Message<C>> {
91        let bytes = self.inner.recv_frame().await?;
92        Message::decode(bytes)
93    }
94
95    fn is_connected(&self) -> bool {
96        self.inner.is_connected()
97    }
98
99    fn is_healthy(&self) -> bool {
100        self.inner.is_healthy()
101    }
102
103    async fn close(&self) -> TransportResult<()> {
104        self.inner.close().await
105    }
106
107    fn stats(&self) -> Option<TransportStats> {
108        self.inner.stats()
109    }
110}
111
112#[async_trait]
113impl<T: MessageChannel<C> + ?Sized, C: Codec + Default> MessageChannel<C> for Arc<T> {
114    async fn send(&self, message: &Message<C>) -> TransportResult<()> {
115        (**self).send(message).await
116    }
117
118    async fn recv(&self) -> TransportResult<Message<C>> {
119        (**self).recv().await
120    }
121
122    fn is_connected(&self) -> bool {
123        (**self).is_connected()
124    }
125
126    fn is_healthy(&self) -> bool {
127        (**self).is_healthy()
128    }
129
130    async fn close(&self) -> TransportResult<()> {
131        (**self).close().await
132    }
133
134    fn stats(&self) -> Option<TransportStats> {
135        (**self).stats()
136    }
137}
138
139#[async_trait]
140impl<T: MessageChannel<C> + ?Sized, C: Codec + Default> MessageChannel<C> for Box<T> {
141    async fn send(&self, message: &Message<C>) -> TransportResult<()> {
142        (**self).send(message).await
143    }
144
145    async fn recv(&self) -> TransportResult<Message<C>> {
146        (**self).recv().await
147    }
148
149    fn is_connected(&self) -> bool {
150        (**self).is_connected()
151    }
152
153    fn is_healthy(&self) -> bool {
154        (**self).is_healthy()
155    }
156
157    async fn close(&self) -> TransportResult<()> {
158        (**self).close().await
159    }
160
161    fn stats(&self) -> Option<TransportStats> {
162        (**self).stats()
163    }
164}
165
166// Deprecated aliases for backward compatibility
167#[deprecated(since = "0.2.0", note = "Use MessageChannel instead")]
168pub type MessageTransport<C> = dyn MessageChannel<C>;
169
170#[deprecated(since = "0.2.0", note = "Use MessageChannelAdapter instead")]
171pub type MessageTransportAdapter<F, C> = MessageChannelAdapter<F, C>;
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176    use crate::message::types::MessageType;
177    use crate::transport::channel::{ChannelConfig, ChannelFrameTransport};
178    use serde::{Deserialize, Serialize};
179
180    #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
181    struct TestRequest {
182        value: i32,
183    }
184
185    #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
186    struct TestResponse {
187        result: String,
188    }
189
190    #[tokio::test]
191    async fn test_message_channel_call_reply() {
192        let config = ChannelConfig::default();
193        let (t1, t2) = ChannelFrameTransport::create_pair("test", config).unwrap();
194
195        let ch1 = MessageChannelAdapter::new(t1);
196        let ch2 = MessageChannelAdapter::new(t2);
197
198        let req = TestRequest { value: 42 };
199        let call_msg: Message = Message::call("test_method", req.clone()).unwrap();
200        let msg_id = call_msg.id;
201
202        ch1.send(&call_msg).await.unwrap();
203
204        let received = ch2.recv().await.unwrap();
205        assert_eq!(received.id, msg_id);
206        assert_eq!(received.msg_type, MessageType::Call);
207        assert_eq!(received.method, "test_method");
208
209        let received_req: TestRequest = received.deserialize_payload().unwrap();
210        assert_eq!(received_req, req);
211
212        let resp = TestResponse {
213            result: "ok".to_string(),
214        };
215        let reply_msg: Message = Message::reply(msg_id, resp.clone()).unwrap();
216        ch2.send(&reply_msg).await.unwrap();
217
218        let received_reply = ch1.recv().await.unwrap();
219        assert_eq!(received_reply.id, msg_id);
220        assert_eq!(received_reply.msg_type, MessageType::Reply);
221
222        let received_resp: TestResponse = received_reply.deserialize_payload().unwrap();
223        assert_eq!(received_resp, resp);
224    }
225
226    #[tokio::test]
227    async fn test_message_channel_with_compression() {
228        use crate::message::types::CompressionType;
229
230        let config = ChannelConfig::default();
231        let (t1, t2) = ChannelFrameTransport::create_pair("compress", config).unwrap();
232
233        let ch1 = MessageChannelAdapter::new(t1);
234        let ch2 = MessageChannelAdapter::new(t2);
235
236        let large_data = TestRequest { value: 12345 };
237        let mut msg: Message = Message::call("compressed_method", large_data.clone()).unwrap();
238        msg.metadata.compression = CompressionType::Lz4;
239
240        ch1.send(&msg).await.unwrap();
241
242        let received = ch2.recv().await.unwrap();
243        assert_eq!(received.metadata.compression, CompressionType::Lz4);
244
245        let received_data: TestRequest = received.deserialize_payload().unwrap();
246        assert_eq!(received_data, large_data);
247    }
248
249    #[tokio::test]
250    async fn test_message_channel_delegation() {
251        let config = ChannelConfig::default();
252        let (t1, _t2) = ChannelFrameTransport::create_pair("delegate", config).unwrap();
253
254        let ch1 = MessageChannelAdapter::new(t1);
255
256        assert!(ch1.is_connected());
257        assert!(ch1.is_healthy());
258        assert!(ch1.stats().is_some());
259    }
260}