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::{Utf8CString, define_from_for_arc},
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(crate) struct UnparsedResponse {
18
    /// The body of this response.
19
    msg: String,
20
}
21

            
22
impl UnparsedResponse {
23
    /// Construct a new UnparsedResponse.
24
8192
    pub(crate) fn new(msg: String) -> Self {
25
8192
        Self { msg }
26
8192
    }
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
8186
    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
8186
        let json: serde_json::Value = serde_json::from_str(self.as_str())?;
71
8184
        let mut msg: String = serde_json::to_string(&json)?;
72
8184
        debug_assert!(!msg.contains('\n'));
73
8184
        msg.push('\n');
74
8184
        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
        })?;
78
8184
        let response: Response = serde_json::from_value(json)?;
79
8184
        let meta = match ResponseMeta::try_from_response(&response) {
80
8182
            Ok(m) => m?,
81
            Err(_) => {
82
2
                return Err(DecodeResponseError::Fatal(
83
2
                    ErrorResponse::from_validated_string(msg),
84
2
                ));
85
            }
86
        };
87
8182
        Ok(ValidatedResponse { msg, meta })
88
8186
    }
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
8180
    pub(crate) fn is_final(&self) -> bool {
99
        use ResponseKind as K;
100
8180
        match self.meta.kind {
101
4098
            K::Error | K::Success => true,
102
4082
            K::Update => false,
103
        }
104
8180
    }
105

            
106
    /// Return the request ID associated with this response.
107
23648
    pub(crate) fn id(&self) -> &AnyRequestId {
108
23648
        &self.meta.id
109
23648
    }
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
8190
    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
8190
        match value {
170
2014
            RMB::Error(_) => RK::Error,
171
2092
            RMB::Success(_) => RK::Success,
172
4084
            RMB::Update(_) => RK::Update,
173
        }
174
8190
    }
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
8198
    fn try_from_response(
189
8198
        response: &Response,
190
8198
    ) -> Result<Result<Self, DecodeResponseError>, ResponseWasFatal> {
191
        use DecodeResponseError as E;
192
        use ResponseBody as Body;
193
8198
        match (&response.id, &response.body) {
194
4
            (None, Body::Error(_ignore)) => {
195
                // No ID, so this is a fatal response.
196
                // Re-encode the response.
197
4
                Err(ResponseWasFatal)
198
            }
199
4
            (None, _) => Ok(Err(E::ProtocolViolation("Missing ID field"))),
200
8190
            (Some(id), body) => Ok(Ok(ResponseMeta {
201
8190
                id: id.clone(),
202
8190
                kind: (body).into(),
203
8190
            })),
204
        }
205
8198
    }
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
2008
pub(crate) fn try_decode_response_as_err(s: &str) -> Result<Option<RpcError>, DecodeResponseError> {
215
2008
    let Response { body, .. } = serde_json::from_str(s)?;
216
2008
    match body {
217
2008
        ResponseBody::Error(e) => Ok(Some(e)),
218
        _ => Ok(None),
219
    }
220
2008
}
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
2008
    pub fn message(&self) -> &str {
237
2008
        self.message.as_str()
238
2008
    }
239
    /// Return the numeric error code from this error.
240
2008
    pub fn code(&self) -> RpcErrorCode {
241
2008
        self.code
242
2008
    }
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
2008
    pub fn kinds_iter(&self) -> impl Iterator<Item = &'_ str> {
248
3012
        self.kinds.iter().map(|s| s.as_ref())
249
2008
    }
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_time_subtraction)]
284
    #![allow(clippy::useless_vec)]
285
    #![allow(clippy::needless_pass_by_value)]
286
    #![allow(clippy::string_slice)] // See arti#2571
287
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
288

            
289
    use super::*;
290

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

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

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

            
353
        }
354

            
355
        use DecodeResponseError as E;
356

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

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

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