1
//! Support for decoding RPC Responses.
2

            
3
use std::sync::Arc;
4

            
5
use serde::{Deserialize, Serialize};
6

            
7
use super::{AnyRequestId, JsonAnyObj};
8
use crate::{
9
    conn::ErrorResponse,
10
    util::{define_from_for_arc, Utf8CString},
11
};
12

            
13
/// An unparsed and unvalidated response, as received from Arti.
14
///
15
/// (It will have no internal newlines, and a single NL at the end.)
16
#[derive(Clone, Debug, derive_more::AsRef)]
17
pub struct UnparsedResponse {
18
    /// The body of this response.
19
    msg: String,
20
}
21

            
22
impl UnparsedResponse {
23
    /// Construct a new UnparsedResponse.
24
8194
    pub(crate) fn new(msg: String) -> Self {
25
8194
        Self { msg }
26
8194
    }
27
}
28

            
29
/// A response that we have validated for correct syntax,
30
/// re-encoded in canonical form,
31
/// and enough to find the information we need about it
32
/// to deliver it to the application.
33
#[derive(Clone, Debug)]
34
pub(crate) struct ValidatedResponse {
35
    /// The re-encoded text of this response.
36
    pub(crate) msg: Utf8CString,
37
    /// The metadata from this response.
38
    pub(crate) meta: ResponseMeta,
39
}
40

            
41
/// An error that occurred when trying to decode an RPC response.
42
#[derive(Clone, Debug, thiserror::Error)]
43
#[non_exhaustive]
44
pub(crate) enum DecodeResponseError {
45
    /// We couldn't decode a response as json.
46
    #[error("Arti sent a message that didn't conform to the RPC protocol")]
47
    JsonProtocolViolation(#[source] Arc<serde_json::Error>),
48

            
49
    /// There was something (other than json encoding) wrong with a response.
50
    #[error("Arti sent a message that didn't conform to the RPC protocol: {0}")]
51
    ProtocolViolation(&'static str),
52

            
53
    /// We decoded the response, but rather than having an `id`,
54
    /// it had an error message from Arti with no id.  We treat this as fatal.
55
    #[error("Arti reported a fatal error: {0}")]
56
    Fatal(ErrorResponse),
57
}
58
define_from_for_arc!( serde_json::Error => DecodeResponseError [JsonProtocolViolation] );
59

            
60
impl UnparsedResponse {
61
    /// If this response is well-formed, and it corresponds to a single request,
62
    /// re-encode it and return it as a ValidatedResponse.
63
8192
    pub(crate) fn try_validate(self) -> Result<ValidatedResponse, DecodeResponseError> {
64
        // We're using serde_json::Value in order to preserve any unrecognized fields
65
        // in the response when we re-encode it.
66
        //
67
        // The alternative would be to preserve unrecognized fields using serde(flatten) and a
68
        // JsonMap in each struct.  But that creates a risk of forgetting to do so in some
69
        // struct that we create in the future.
70
8192
        let json: serde_json::Value = serde_json::from_str(self.as_str())?;
71
8190
        let mut msg: String = serde_json::to_string(&json)?;
72
8190
        debug_assert!(!msg.contains('\n'));
73
8190
        msg.push('\n');
74
8190
        let msg: Utf8CString = msg.try_into().map_err(|_| {
75
            // (This should be impossible; serde_json rejects NULs.)
76
            DecodeResponseError::ProtocolViolation("Unexpected NUL in validated message")
77
8190
        })?;
78
8190
        let response: Response = serde_json::from_value(json)?;
79
8190
        let meta = match ResponseMeta::try_from_response(&response) {
80
8188
            Ok(m) => m?,
81
            Err(_) => {
82
2
                return Err(DecodeResponseError::Fatal(
83
2
                    ErrorResponse::from_validated_string(msg),
84
2
                ))
85
            }
86
        };
87
8188
        Ok(ValidatedResponse { msg, meta })
88
8192
    }
89

            
90
    /// Return the inner `str` for this unparsed message.
91
8192
    pub(crate) fn as_str(&self) -> &str {
92
8192
        self.msg.as_str()
93
8192
    }
94
}
95

            
96
impl ValidatedResponse {
97
    /// Return true if no additional response should arrive for this request.
98
8186
    pub(crate) fn is_final(&self) -> bool {
99
        use ResponseKind as K;
100
8186
        match self.meta.kind {
101
4098
            K::Error | K::Success => true,
102
4088
            K::Update => false,
103
        }
104
8186
    }
105

            
106
    /// Return the request ID associated with this response.
107
15636
    pub(crate) fn id(&self) -> &AnyRequestId {
108
15636
        &self.meta.id
109
15636
    }
110
}
111

            
112
/// Metadata extracted from a response while decoding it.
113
#[derive(Clone, Debug)]
114
#[cfg_attr(test, derive(Eq, PartialEq))]
115
pub(crate) struct ResponseMeta {
116
    /// The request ID for this response.
117
    pub(crate) id: AnyRequestId,
118
    /// The kind of response that was received.
119
    pub(crate) kind: ResponseKind,
120
}
121

            
122
/// A kind of response received from Arti.
123
//
124
// TODO: Possibly unify or derive from ResponseMetaBodyDe?
125
#[derive(Clone, Debug, Eq, PartialEq)]
126
pub(crate) enum ResponseKind {
127
    /// Arti reports that an error has occurred.
128
    Error,
129
    /// Arti reports that the request completed successfully.
130
    Success,
131
    /// Arti reports an incremental update for the request.
132
    Update,
133
}
134

            
135
/// Serde-only type: decodes enough fields from a response in order to validate it
136
/// and route it to the application.
137
#[derive(Deserialize, Debug)]
138
struct Response {
139
    /// The request ID for this response.
140
    ///
141
    /// This field is mandatory for any non-Error response.
142
    id: Option<AnyRequestId>,
143
    /// The body as decoded for this response.
144
    #[serde(flatten)]
145
    body: ResponseBody,
146
}
147

            
148
/// Inner type to implement `Response``
149
#[derive(Deserialize, Debug)]
150
enum ResponseBody {
151
    /// Arti reports that an error has occurred.
152
    ///
153
    /// In this case, we decode the error to make sure it's well-formed.
154
    #[serde(rename = "error")]
155
    Error(RpcError),
156
    /// Arti reports that the request completed successfully.
157
    #[serde(rename = "result")]
158
    Success(JsonAnyObj),
159
    /// Arti reports an incremental update for the request.
160
    #[serde(rename = "update")]
161
    Update(JsonAnyObj),
162
}
163
impl<'a> From<&'a ResponseBody> for ResponseKind {
164
8196
    fn from(value: &'a ResponseBody) -> Self {
165
        use ResponseBody as RMB;
166
        use ResponseKind as RK;
167
        // TODO RPC: If we keep the current set of types,
168
        // we should have this discriminant code be macro-generated.
169
8196
        match value {
170
2094
            RMB::Error(_) => RK::Error,
171
2012
            RMB::Success(_) => RK::Success,
172
4090
            RMB::Update(_) => RK::Update,
173
        }
174
8196
    }
175
}
176

            
177
/// Error returned from [`ResponseMeta::try_from_response`] when a response
178
/// has no Id field, and therefore indicates a fatal protocol error.
179
#[derive(thiserror::Error, Debug, Clone)]
180
#[error("Response was fatal (it had no ID)")]
181
struct ResponseWasFatal;
182

            
183
impl ResponseMeta {
184
    /// Try to extract a `ResponseMeta` from a response.
185
    ///
186
    /// Return `Err(ResponseWasFatal)` if the ID was missing on an error, and `Err(Err(_))` on any
187
    /// other problem.
188
8204
    fn try_from_response(
189
8204
        response: &Response,
190
8204
    ) -> Result<Result<Self, DecodeResponseError>, ResponseWasFatal> {
191
        use DecodeResponseError as E;
192
        use ResponseBody as Body;
193
8204
        match (&response.id, &response.body) {
194
4
            (None, Body::Error(_ignore)) => {
195
4
                // No ID, so this is a fatal response.
196
4
                // Re-encode the response.
197
4
                Err(ResponseWasFatal)
198
            }
199
4
            (None, _) => Ok(Err(E::ProtocolViolation("Missing ID field"))),
200
8196
            (Some(id), body) => Ok(Ok(ResponseMeta {
201
8196
                id: id.clone(),
202
8196
                kind: (body).into(),
203
8196
            })),
204
        }
205
8204
    }
206
}
207

            
208
/// Try to decode `s` as an error response, and return its error.
209
///
210
/// (Gives an error if this is not an error response)
211
//
212
// TODO RPC: Eventually we should try to refactor this out if we can; it is only called in one
213
// place.
214
2088
pub(crate) fn try_decode_response_as_err(s: &str) -> Result<Option<RpcError>, DecodeResponseError> {
215
2088
    let Response { body, .. } = serde_json::from_str(s)?;
216
2088
    match body {
217
2088
        ResponseBody::Error(e) => Ok(Some(e)),
218
        _ => Ok(None),
219
    }
220
2088
}
221

            
222
/// An error sent by Arti, decoded into its parts.
223
#[derive(Clone, Debug, Deserialize, Serialize)]
224
#[cfg_attr(test, derive(PartialEq, Eq))]
225
pub struct RpcError {
226
    /// A human-readable message from Arti.
227
    message: String,
228
    /// An error code representing the underlying problem.
229
    code: RpcErrorCode,
230
    /// One or more `ErrorKind`s, encoded as strings.
231
    kinds: Vec<String>,
232
}
233

            
234
impl RpcError {
235
    /// Return the human-readable message that Arti sent as part of this error.
236
2088
    pub fn message(&self) -> &str {
237
2088
        self.message.as_str()
238
2088
    }
239
    /// Return the numeric error code from this error.
240
2088
    pub fn code(&self) -> RpcErrorCode {
241
2088
        self.code
242
2088
    }
243
    /// Return an iterator over the ErrorKinds for this error.
244
    //
245
    // Note: This is not a great API for FFI purposes.
246
    // But FFI code should get errors as a String, so that's probably fine.
247
2088
    pub fn kinds_iter(&self) -> impl Iterator<Item = &'_ str> {
248
3132
        self.kinds.iter().map(|s| s.as_ref())
249
2088
    }
250
}
251

            
252
caret::caret_int! {
253
    #[derive(serde::Deserialize, serde::Serialize)]
254
    pub struct RpcErrorCode(i32) {
255
        /// "The JSON sent is not a valid Request object."
256
        INVALID_REQUEST = -32600,
257
        /// "The method does not exist ."
258
        NO_SUCH_METHOD = -32601,
259
        /// "Invalid method parameter(s)."
260
        INVALID_PARAMS = -32602,
261
        /// "The server suffered some kind of internal problem"
262
        INTERNAL_ERROR = -32603,
263
        /// "Some requested object was not valid"
264
        OBJECT_ERROR = 1,
265
        /// "Some other error occurred"
266
        REQUEST_ERROR = 2,
267
        /// This method exists, but wasn't implemented on this object.
268
        METHOD_NOT_IMPL = 3,
269
    }
270
}
271

            
272
#[cfg(test)]
273
mod test {
274
    // @@ begin test lint list maintained by maint/add_warning @@
275
    #![allow(clippy::bool_assert_comparison)]
276
    #![allow(clippy::clone_on_copy)]
277
    #![allow(clippy::dbg_macro)]
278
    #![allow(clippy::mixed_attributes_style)]
279
    #![allow(clippy::print_stderr)]
280
    #![allow(clippy::print_stdout)]
281
    #![allow(clippy::single_char_pattern)]
282
    #![allow(clippy::unwrap_used)]
283
    #![allow(clippy::unchecked_duration_subtraction)]
284
    #![allow(clippy::useless_vec)]
285
    #![allow(clippy::needless_pass_by_value)]
286
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
287

            
288
    use super::*;
289

            
290
    /// Helper: Decode a string into a Response, then convert it into
291
    /// a ResponseMeta.
292
    fn response_meta(s: &str) -> Result<ResponseMeta, DecodeResponseError> {
293
        match ResponseMeta::try_from_response(&serde_json::from_str::<Response>(s)?) {
294
            Ok(v) => v,
295
            Err(_) => {
296
                let utf8 = Utf8CString::try_from(s.to_string())
297
                    .map_err(|_| DecodeResponseError::ProtocolViolation("not utf8cstr?"))?;
298
                Err(DecodeResponseError::Fatal(
299
                    ErrorResponse::from_validated_string(utf8),
300
                ))
301
            }
302
        }
303
    }
304

            
305
    #[test]
306
    fn response_meta_good() {
307
        use ResponseKind as RK;
308
        use ResponseMeta as RM;
309
        for (s, expected) in [
310
            (
311
                r#"{"id":7, "result": {}}"#,
312
                RM {
313
                    id: 7.into(),
314
                    kind: RK::Success,
315
                },
316
            ),
317
            (
318
                r#"{"id":"hi", "update": {"here":["goes", "nothing"]}}"#,
319
                RM {
320
                    id: "hi".to_string().into(),
321
                    kind: RK::Update,
322
                },
323
            ),
324
            (
325
                r#"{"id": 6, "error": {"message":"iffy wobbler", "code":999, "kinds": ["BadVibes"]}}"#,
326
                RM {
327
                    id: 6.into(),
328
                    kind: RK::Error,
329
                },
330
            ),
331
            (
332
                r#"{"id": 6, "error": {"message":"iffy wobbler", "code":999, "kinds": ["BadVibes"], "data": {"a":"b"}}}"#,
333
                RM {
334
                    id: 6.into(),
335
                    kind: RK::Error,
336
                },
337
            ),
338
        ] {
339
            let got = response_meta(s).unwrap();
340
            assert_eq!(got, expected);
341
        }
342
    }
343

            
344
    #[test]
345
    fn response_meta_bad() {
346
        macro_rules! check_err {
347
            { $s:expr, $p:pat } => {
348
                let got_err = response_meta($s).unwrap_err();
349
                assert!(matches!(got_err, $p));
350
            }
351

            
352
        }
353

            
354
        use DecodeResponseError as E;
355

            
356
        // No ID; arti is saying we screwed up.
357
        check_err!(
358
            r#"{"error": {"message":"iffy wobbler", "code":999, "kinds": ["BadVibes"], "data": {"a":"b"}}}"#,
359
            E::Fatal(_)
360
        );
361
        // Missing ID on a success.
362
        check_err!(r#"{"result": {}}"#, E::ProtocolViolation(_));
363
        // Missing ID on an update.
364
        check_err!(r#"{"update": {}}"#, E::ProtocolViolation(_));
365
        // No recognized type.
366
        check_err!(r#"{"id": 7, "flupdate": {}}"#, E::JsonProtocolViolation(_));
367
        // Couldn't parse.
368
        check_err!(r#"{{{{{"#, E::JsonProtocolViolation(_));
369
        // Error is no good.
370
        check_err!(
371
            r#"{"id": 77 "error": {"message":"iffy wobbler"}}"#,
372
            E::JsonProtocolViolation(_)
373
        );
374
    }
375

            
376
    #[test]
377
    fn bad_json() {
378
        // we rely on the json parser rejecting some things.
379
        for s in [
380
            "{ ",         // not complete
381
            "",           // Empty.
382
            "{ \0 }",     // contains nul byte.
383
            "{ \"\0\" }", // string contains nul byte.
384
        ] {
385
            let r: Result<serde_json::Value, _> = serde_json::from_str(s);
386
            assert!(dbg!(r.err()).is_some());
387
        }
388
    }
389

            
390
    #[test]
391
    fn re_encode() {
392
        let response = r#"{
393
            "id": 6,
394
            "error": {
395
                "message":"iffy wobbler",
396
                "code":999,
397
                "kinds": ["BadVibes"],
398
                "data": {"a":"b"},
399
                "explosion": 22
400
             },
401
             "xyzzy":"plugh"
402
        }"#;
403
        let json_orig: serde_json::Value = serde_json::from_str(response).unwrap();
404
        let resp = UnparsedResponse::new(response.into());
405
        let valid = resp.try_validate().unwrap();
406
        let msg: &str = valid.msg.as_ref();
407
        let json_reencoded: serde_json::Value = serde_json::from_str(msg).unwrap();
408
        // To make sure all fields were preserved, we have to compare the json objects for equality;
409
        // we cannot rely on the order of the fields.
410
        assert_eq!(json_orig, json_reencoded);
411
    }
412
}