1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
//! Types for binary remote procedure calls.
//!
//! Message identifiers have the same semantics as JSON-RPC;
//! if a request does not have an `id` than no reply is expected
//! otherwise a service must reply.
use crate::{mpc::SealedEnvelope, Error, Result};

use http::StatusCode;
use serde::{de::DeserializeOwned, Serialize};
use serde_json::Value;
use std::borrow::Cow;

use async_trait::async_trait;

/// Encrypted envelope sent to a remote server.
#[derive(Default)]
pub struct ServerEnvelope {
    /// Client public key.
    pub public_key: Vec<u8>,
    /// Encrypted payload.
    pub envelope: SealedEnvelope,
}

/// Packet including identity bytes.
#[derive(Default)]
pub struct Packet<'a> {
    pub(crate) payload: Payload<'a>,
}

impl<'a> Packet<'a> {
    /// Create a new request packet.
    pub fn new_request(message: RequestMessage<'a>) -> Self {
        Self {
            payload: Payload::Request(message),
        }
    }

    /// Create a new response packet.
    pub fn new_response(message: ResponseMessage<'a>) -> Self {
        Self {
            payload: Payload::Response(message),
        }
    }
}

impl<'a> TryFrom<Packet<'a>> for RequestMessage<'a> {
    type Error = Error;
    fn try_from(packet: Packet<'a>) -> Result<Self> {
        match packet.payload {
            Payload::Request(val) => Ok(val),
            _ => Err(Error::RpcRequestPayload),
        }
    }
}

impl<'a> TryFrom<Packet<'a>> for ResponseMessage<'a> {
    type Error = Error;
    fn try_from(packet: Packet<'a>) -> Result<Self> {
        match packet.payload {
            Payload::Response(val) => Ok(val),
            _ => Err(Error::RpcResponsePayload),
        }
    }
}

/// Payload for a packet; either a request or response.
#[derive(Default)]
pub enum Payload<'a> {
    /// Default variant.
    #[doc(hidden)]
    #[default]
    Noop,
    /// Request payload.
    Request(RequestMessage<'a>),
    /// Response payload.
    Response(ResponseMessage<'a>),
}

/// An RPC request message.
#[derive(Default, Debug)]
pub struct RequestMessage<'a> {
    pub(crate) id: Option<u64>,
    pub(crate) method: Cow<'a, str>,
    pub(crate) parameters: Value,
    pub(crate) body: Cow<'a, [u8]>,
}

impl<'a> RequestMessage<'a> {
    /// Create a new request message with a body.
    pub fn new<T>(
        id: Option<u64>,
        method: &'a str,
        parameters: T,
        body: Cow<'a, [u8]>,
    ) -> Result<Self>
    where
        T: Serialize,
    {
        Ok(Self {
            id,
            method: Cow::Borrowed(method),
            parameters: serde_json::to_value(parameters)?,
            body,
        })
    }

    /// Create a new request message without a body.
    pub fn new_call<T>(
        id: Option<u64>,
        method: &'a str,
        parameters: T,
    ) -> Result<Self>
    where
        T: Serialize,
    {
        RequestMessage::new(id, method, parameters, Cow::Owned(vec![]))
    }

    /// Get the message identifier.
    pub fn id(&self) -> Option<u64> {
        self.id
    }

    /// Get the method name.
    pub fn method(&self) -> &str {
        self.method.as_ref()
    }

    /// Get the method parameters as type `T`.
    pub fn parameters<T: DeserializeOwned>(&self) -> Result<T> {
        Ok(serde_json::from_value::<T>(self.parameters.clone())?)
    }

    /// Get a slice of the message body.
    pub fn body(&self) -> &[u8] {
        self.body.as_ref()
    }
}

impl From<RequestMessage<'_>> for Vec<u8> {
    fn from(value: RequestMessage<'_>) -> Self {
        value.body.into_owned()
    }
}

/// Result that can be extracted from a response message.
///
/// Contains the message id, HTTP status code, a possible result
/// and the message body.
pub type ResponseResult<T> =
    (Option<u64>, StatusCode, Option<Result<T>>, Vec<u8>);

/// An RPC response message.
#[derive(Default, Debug)]
pub struct ResponseMessage<'a> {
    pub(crate) id: Option<u64>,
    pub(crate) status: StatusCode,
    pub(crate) result: Option<Result<Value>>,
    pub(crate) body: Cow<'a, [u8]>,
}

impl<'a> ResponseMessage<'a> {
    /// Create a new response message.
    pub fn new<T>(
        id: Option<u64>,
        status: StatusCode,
        result: Option<Result<T>>,
        body: Cow<'a, [u8]>,
    ) -> Result<Self>
    where
        T: Serialize,
    {
        let result = match result {
            Some(value) => match value {
                Ok(value) => Some(Ok(serde_json::to_value(value)?)),
                Err(e) => Some(Err(e)),
            },
            None => None,
        };

        Ok(Self {
            id,
            status,
            result,
            body,
        })
    }

    /// Create a new response message with an empty body.
    pub fn new_reply<T>(
        id: Option<u64>,
        status: StatusCode,
        result: Option<Result<T>>,
    ) -> Result<Self>
    where
        T: Serialize,
    {
        ResponseMessage::new(id, status, result, Cow::Owned(vec![]))
    }

    /// Get the message identifier.
    pub fn id(&self) -> Option<u64> {
        self.id
    }

    /// Get the status code.
    pub fn status(&self) -> StatusCode {
        self.status
    }

    /// Take the result.
    pub fn take<T: DeserializeOwned>(self) -> Result<ResponseResult<T>> {
        let value = if let Some(result) = self.result {
            match result {
                Ok(value) => Some(Ok(serde_json::from_value::<T>(value)?)),
                Err(e) => Some(Err(e)),
            }
        } else {
            None
        };
        Ok((self.id, self.status, value, self.body.to_vec()))
    }
}

impl From<ResponseMessage<'_>> for Vec<u8> {
    fn from(value: ResponseMessage<'_>) -> Self {
        value.body.into_owned()
    }
}

impl From<Error> for ResponseMessage<'_> {
    fn from(value: Error) -> Self {
        ResponseMessage::new_reply::<()>(
            None,
            StatusCode::INTERNAL_SERVER_ERROR,
            Some(Err(value)),
        )
        .expect("failed to encode error response message")
    }
}

// NOTE: if we put the id first the compiler complains about a conflict
// NOTE: with a TryFrom implementation
impl From<(StatusCode, Option<u64>)> for ResponseMessage<'_> {
    fn from(value: (StatusCode, Option<u64>)) -> Self {
        let message = value
            .0
            .canonical_reason()
            .map(|s| s.to_owned())
            .unwrap_or_else(|| "unexpected status code".to_owned());

        ResponseMessage::new_reply::<()>(
            value.1,
            value.0,
            Some(Err(Error::RpcError(message))),
        )
        .expect("failed to encode error response message")
    }
}

impl<'a, T: Serialize> TryFrom<(StatusCode, Option<u64>, T)>
    for ResponseMessage<'a>
{
    type Error = Error;

    fn try_from(value: (StatusCode, Option<u64>, T)) -> Result<Self> {
        let reply =
            ResponseMessage::new_reply(value.1, value.0, Some(Ok(value.2)))?;
        Ok(reply)
    }
}

impl<'a, T: Serialize> TryFrom<(Option<u64>, T)> for ResponseMessage<'a> {
    type Error = Error;

    fn try_from(value: (Option<u64>, T)) -> Result<Self> {
        let reply = ResponseMessage::new_reply(
            value.0,
            StatusCode::OK,
            Some(Ok(value.1)),
        )?;
        Ok(reply)
    }
}

/// Trait for implementations that process incoming requests.
#[async_trait]
pub trait Service {
    /// State for this service.
    type State: Send + Sync;

    /// Handle an incoming message.
    async fn handle<'a>(
        &self,
        state: Self::State,
        request: RequestMessage<'a>,
    ) -> Result<ResponseMessage<'a>>;

    /// Serve an incoming request.
    async fn serve<'a>(
        &self,
        state: Self::State,
        request: RequestMessage<'a>,
    ) -> Option<ResponseMessage<'a>> {
        match self.handle(state, request).await {
            Ok(res) => {
                if res.id().is_some() {
                    Some(res)
                } else {
                    None
                }
            }
            Err(e) => {
                let reply: ResponseMessage<'_> = e.into();
                Some(reply)
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{decode, encode};
    use anyhow::Result;
    use http::StatusCode;

    #[tokio::test]
    async fn rpc_encode() -> Result<()> {
        let body = vec![0x0A, 0xFF];
        let message = RequestMessage::new(
            Some(1),
            "GetEventLog",
            (),
            Cow::Borrowed(&body),
        )?;

        let request = encode(&message).await?;
        let decoded: RequestMessage = decode(&request).await?;

        assert_eq!(message.method(), decoded.method());
        //assert_eq!((), decoded.parameters::<()>()?);
        assert_eq!(&body, decoded.body());

        let result = Some(Ok("Foo".to_owned()));
        let reply = ResponseMessage::new(
            Some(1),
            StatusCode::OK,
            result,
            Cow::Borrowed(&body),
        )?;

        let response = encode(&reply).await?;
        let decoded: ResponseMessage = decode(&response).await?;

        let result = decoded.take::<String>()?;
        let value = result.2.unwrap().unwrap();

        assert_eq!(Some(1), result.0);
        assert_eq!("Foo", &value);
        assert_eq!(body, result.3);

        // Check the packet request encoding
        let req = Packet::new_request(message);
        let enc = encode(&req).await?;
        let pkt: Packet<'_> = decode(&enc).await?;

        let incoming: RequestMessage<'_> = pkt.try_into()?;
        assert_eq!(Some(1u64), incoming.id());
        assert_eq!("GetEventLog", incoming.method());
        //assert_eq!((), incoming.parameters::<()>()?);
        assert_eq!(&body, incoming.body());

        // Check the packet response encoding
        let res = Packet::new_response(reply);
        let enc = encode(&res).await?;
        let pkt: Packet<'_> = decode(&enc).await?;

        let incoming: ResponseMessage<'_> = pkt.try_into()?;
        let result = incoming.take::<String>()?;
        let value = result.2.unwrap().unwrap();
        assert_eq!(Some(1), result.0);
        assert_eq!("Foo", &value);
        assert_eq!(body, result.3);

        Ok(())
    }
}