ckb_script_ipc_common/
channel.rs

1use crate::io::{BufReader, BufWriter, Read, Write};
2use crate::{
3    error::{IpcError, ProtocolErrorCode},
4    ipc::Serve,
5    packet::{Packet, RequestPacket, ResponsePacket},
6};
7use alloc::string::String;
8use alloc::vec;
9use serde::{Deserialize, Serialize};
10use serde_json::{from_slice, to_vec};
11
12/// The `Channel` struct facilitates communication between a client and a server.
13/// It handles the transmission of requests from the client to the server and the reception
14/// of responses from the server to the client. Communication is achieved through the use of pipes.
15///
16/// # Fields
17///
18/// * `reader` - Responsible for reading data from the channel.
19/// * `writer` - Responsible for writing data to the channel.
20pub struct Channel<R: Read, W: Write> {
21    reader: BufReader<R>,
22    writer: BufWriter<W>,
23}
24
25impl<R: Read, W: Write> Channel<R, W> {
26    pub fn new(reader: R, writer: W) -> Self {
27        Self {
28            reader: BufReader::new(reader),
29            writer: BufWriter::new(writer),
30        }
31    }
32}
33
34impl<R: Read, W: Write> Channel<R, W> {
35    /// Executes the server loop, processing incoming requests and sending responses.
36    ///
37    /// This function runs an infinite loop, continuously receiving requests from the client,
38    /// processing them using the provided service implementation, and sending back the responses.
39    /// If an error occurs during the processing of a request or the sending of a response, the
40    /// error is logged (if logging is enabled) and the error code is sent back to the client.
41    ///
42    /// # Arguments
43    ///
44    /// * `serve` - A mutable reference to the service implementation that handles the requests and
45    ///   generates the responses. The service must implement the `Serve` trait with the appropriate
46    ///   request and response types.
47    ///
48    /// # Type Parameters
49    ///
50    /// * `Req` - The type of the request messages. It must implement `Serialize` and `Deserialize`.
51    /// * `Resp` - The type of the response messages. It must implement `Serialize` and `Deserialize`.
52    /// * `S` - The type of the service implementation. It must implement the `Serve` trait with
53    ///   `Req` as the request type and `Resp` as the response type.
54    ///
55    /// # Returns
56    ///
57    /// A `Result` indicating the success or failure of the server execution. If the server runs
58    /// successfully, it never returns. If an error occurs, it returns an `IpcError`.
59    pub fn execute<Req, Resp, S>(mut self, serve: &mut S) -> Result<(), IpcError>
60    where
61        Req: Serialize + for<'de> Deserialize<'de>,
62        Resp: Serialize + for<'de> Deserialize<'de>,
63        S: Serve<Req = Req, Resp = Resp>,
64    {
65        loop {
66            let result = self
67                .receive_request()
68                .and_then(|req| serve.serve(req).map_err(Into::into))
69                .and_then(|resp| self.send_response(resp));
70
71            match result {
72                Ok(_) => continue,
73                Err(e) => {
74                    #[cfg(feature = "enable-logging")]
75                    log::error!("Error in execute loop: {:?}", e);
76                    // notify client
77                    self.send_error_code(e.clone().into()).unwrap();
78                    return Err(e);
79                }
80            }
81        }
82    }
83    ///
84    /// Sends a request to the server and waits for a response.
85    ///
86    /// This function serializes the request, sends it to the server, and then waits for the server's response.
87    /// It returns the deserialized response or an `IpcError` if an error occurs during the process.
88    ///
89    /// # Arguments
90    ///
91    /// * `_method_name` - A static string slice representing the name of the method being called.
92    /// * `req` - The request message to be sent to the server. It must implement `Serialize` and `Deserialize`.
93    ///
94    /// # Type Parameters
95    ///
96    /// * `Req` - The type of the request message. It must implement `Serialize` and `Deserialize`.
97    /// * `Resp` - The type of the response message. It must implement `Serialize` and `Deserialize`.
98    ///
99    /// # Returns
100    ///
101    /// A `Result` containing the deserialized response message if the call is successful, or an `IpcError` if
102    /// an error occurs during the process.
103    /// # Example
104    ///
105    /// ```rust,ignore
106    /// use ckb_script_ipc_common::channel::Channel;
107    /// use serde::{Serialize, Deserialize};
108    ///
109    /// #[derive(Serialize, Deserialize)]
110    /// struct MyRequest {
111    ///     // request fields
112    /// }
113    ///
114    /// #[derive(Serialize, Deserialize)]
115    /// struct MyResponse {
116    ///     // response fields
117    /// }
118    ///
119    /// let mut channel = Channel::new(reader, writer);
120    /// let request = MyRequest { /* fields */ };
121    /// let response: MyResponse = channel.call("my_method", request).expect("Failed to call method");
122    /// ```
123    pub fn call<Req, Resp>(
124        &mut self,
125        _method_name: &'static str,
126        req: Req,
127    ) -> Result<Resp, IpcError>
128    where
129        Req: Serialize + for<'de> Deserialize<'de>,
130        Resp: Serialize + for<'de> Deserialize<'de>,
131    {
132        let result = self.send_request(req).and_then(|_| self.receive_response());
133        match result {
134            Ok(resp) => Ok(resp),
135            Err(e) => {
136                #[cfg(feature = "enable-logging")]
137                log::error!("Error in call({}): {:?}", _method_name, e);
138                Err(e)
139            }
140        }
141    }
142    pub(crate) fn send_request<Req: Serialize>(&mut self, req: Req) -> Result<(), IpcError> {
143        let serialized_req = to_vec(&req).map_err(|_| IpcError::SerializeError)?;
144        let packet = RequestPacket::new(serialized_req);
145        #[cfg(feature = "enable-logging")]
146        log::info!("send request: {:?}", packet);
147
148        let bytes = packet.serialize();
149        self.writer.write(&bytes)?;
150        self.writer.flush()?;
151        Ok(())
152    }
153
154    /// Sends a raw JSON string request to the server.
155    ///
156    /// This function takes a JSON string and sends it directly as a request packet to the server,
157    /// without performing any serialization. This is useful when working with raw JSON data that
158    /// doesn't need to be converted from Rust types.
159    ///
160    /// # Arguments
161    ///
162    /// * `json` - A string slice containing the JSON request to be sent.
163    ///
164    /// # Returns
165    ///
166    /// A `Result` indicating whether the request was successfully sent, or an `IpcError` if
167    /// writing to the channel fails.
168    pub fn send_json_request(&mut self, json: &str) -> Result<(), IpcError> {
169        let packet = RequestPacket::new(json.as_bytes().to_vec());
170        #[cfg(feature = "enable-logging")]
171        log::info!("send request: {:?}", packet);
172
173        let bytes = packet.serialize();
174        self.writer.write(&bytes)?;
175        self.writer.flush()?;
176        Ok(())
177    }
178    pub(crate) fn send_response<Resp: Serialize>(&mut self, resp: Resp) -> Result<(), IpcError> {
179        let serialized_resp = to_vec(&resp).map_err(|_| IpcError::SerializeError)?;
180        let packet = ResponsePacket::new(0, serialized_resp);
181        #[cfg(feature = "enable-logging")]
182        log::info!("send response: {:?}", packet);
183
184        let bytes = packet.serialize();
185        self.writer.write(&bytes)?;
186        self.writer.flush()?;
187        Ok(())
188    }
189    pub(crate) fn send_error_code(
190        &mut self,
191        error_code: ProtocolErrorCode,
192    ) -> Result<(), IpcError> {
193        let packet = ResponsePacket::new(error_code.clone() as u64, vec![]);
194        #[cfg(feature = "enable-logging")]
195        log::info!("send error code: {:?}", error_code as u64);
196        let bytes = packet.serialize();
197        self.writer.write(&bytes)?;
198        self.writer.flush()?;
199        Ok(())
200    }
201    pub(crate) fn receive_request<Req: for<'de> Deserialize<'de>>(
202        &mut self,
203    ) -> Result<Req, IpcError> {
204        let packet = RequestPacket::read_from(&mut self.reader)?;
205        #[cfg(feature = "enable-logging")]
206        log::info!("receive request: {:?}", packet);
207        let req = from_slice(packet.payload()).map_err(|_| IpcError::DeserializeError)?;
208        Ok(req)
209    }
210    pub(crate) fn receive_response<Resp: for<'de> Deserialize<'de>>(
211        &mut self,
212    ) -> Result<Resp, IpcError> {
213        let packet = ResponsePacket::read_from(&mut self.reader)?;
214
215        #[cfg(feature = "enable-logging")]
216        log::info!("Received response: {:?}", packet);
217
218        let error_code = ProtocolErrorCode::from(packet.error_code());
219        match error_code {
220            ProtocolErrorCode::Ok => {}
221            e => {
222                #[cfg(feature = "enable-logging")]
223                log::error!("Received error code: {:?}", e);
224                return Err(IpcError::ProtocolError(e));
225            }
226        }
227        from_slice(packet.payload()).map_err(|_| IpcError::DeserializeError)
228    }
229
230    /// Receives a JSON string response from the server.
231    ///
232    /// This function reads a response packet from the server and returns its payload as a String,
233    /// without attempting to deserialize it into a specific type. This is useful when working
234    /// with raw JSON responses that don't need to be converted into specific Rust types.
235    ///
236    /// # Returns
237    ///
238    /// * `Ok(String)` - A String containing the JSON response if successful
239    /// * `Err(IpcError)` - An error if:
240    ///   - Reading from the channel fails
241    ///   - The server returns an error code
242    ///   - The response payload contains invalid UTF-8
243    ///
244    pub fn receive_json_response(&mut self) -> Result<String, IpcError> {
245        let packet = ResponsePacket::read_from(&mut self.reader)?;
246
247        #[cfg(feature = "enable-logging")]
248        log::info!("Received response: {:?}", packet);
249
250        let error_code = ProtocolErrorCode::from(packet.error_code());
251        match error_code {
252            ProtocolErrorCode::Ok => {}
253            e => {
254                #[cfg(feature = "enable-logging")]
255                log::error!("Received error code: {:?}", e);
256                return Err(IpcError::ProtocolError(e));
257            }
258        }
259        Ok(String::from_utf8_lossy(packet.payload()).into_owned())
260    }
261}