1//! Support for decoding RPC Responses.
23use std::sync::Arc;
45use serde::{Deserialize, Serialize};
67use super::{AnyRequestId, JsonAnyObj};
8use crate::{
9 conn::ErrorResponse,
10 util::{define_from_for_arc, Utf8CString},
11};
1213/// 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)]
17pub struct UnparsedResponse {
18/// The body of this response.
19msg: String,
20}
2122impl UnparsedResponse {
23/// Construct a new UnparsedResponse.
24pub(crate) fn new(msg: String) -> Self {
25Self { msg }
26 }
27}
2829/// 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)]
34pub(crate) struct ValidatedResponse {
35/// The re-encoded text of this response.
36pub(crate) msg: Utf8CString,
37/// The metadata from this response.
38pub(crate) meta: ResponseMeta,
39}
4041/// An error that occurred when trying to decode an RPC response.
42#[derive(Clone, Debug, thiserror::Error)]
43#[non_exhaustive]
44pub(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")]
47JsonProtocolViolation(#[source] Arc<serde_json::Error>),
4849/// 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}")]
51ProtocolViolation(&'static str),
5253/// 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}")]
56Fatal(ErrorResponse),
57}
58define_from_for_arc!( serde_json::Error => DecodeResponseError [JsonProtocolViolation] );
5960impl 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.
63pub(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.
70let json: serde_json::Value = serde_json::from_str(self.as_str())?;
71let mut msg: String = serde_json::to_string(&json)?;
72debug_assert!(!msg.contains('\n'));
73 msg.push('\n');
74let msg: Utf8CString = msg.try_into().map_err(|_| {
75// (This should be impossible; serde_json rejects NULs.)
76DecodeResponseError::ProtocolViolation("Unexpected NUL in validated message")
77 })?;
78let response: Response = serde_json::from_value(json)?;
79let meta = match ResponseMeta::try_from_response(&response) {
80Ok(m) => m?,
81Err(_) => {
82return Err(DecodeResponseError::Fatal(
83 ErrorResponse::from_validated_string(msg),
84 ))
85 }
86 };
87Ok(ValidatedResponse { msg, meta })
88 }
8990/// Return the inner `str` for this unparsed message.
91pub(crate) fn as_str(&self) -> &str {
92self.msg.as_str()
93 }
94}
9596impl ValidatedResponse {
97/// Return true if no additional response should arrive for this request.
98pub(crate) fn is_final(&self) -> bool {
99use ResponseKind as K;
100match self.meta.kind {
101 K::Error | K::Success => true,
102 K::Update => false,
103 }
104 }
105106/// Return the request ID associated with this response.
107pub(crate) fn id(&self) -> &AnyRequestId {
108&self.meta.id
109 }
110}
111112/// Metadata extracted from a response while decoding it.
113#[derive(Clone, Debug)]
114#[cfg_attr(test, derive(Eq, PartialEq))]
115pub(crate) struct ResponseMeta {
116/// The request ID for this response.
117pub(crate) id: AnyRequestId,
118/// The kind of response that was received.
119pub(crate) kind: ResponseKind,
120}
121122/// A kind of response received from Arti.
123//
124// TODO: Possibly unify or derive from ResponseMetaBodyDe?
125#[derive(Clone, Debug, Eq, PartialEq)]
126pub(crate) enum ResponseKind {
127/// Arti reports that an error has occurred.
128Error,
129/// Arti reports that the request completed successfully.
130Success,
131/// Arti reports an incremental update for the request.
132Update,
133}
134135/// 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)]
138struct Response {
139/// The request ID for this response.
140 ///
141 /// This field is mandatory for any non-Error response.
142id: Option<AnyRequestId>,
143/// The body as decoded for this response.
144#[serde(flatten)]
145body: ResponseBody,
146}
147148/// Inner type to implement `Response``
149#[derive(Deserialize, Debug)]
150enum 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")]
155Error(RpcError),
156/// Arti reports that the request completed successfully.
157#[serde(rename = "result")]
158Success(JsonAnyObj),
159/// Arti reports an incremental update for the request.
160#[serde(rename = "update")]
161Update(JsonAnyObj),
162}
163impl<'a> From<&'a ResponseBody> for ResponseKind {
164fn from(value: &'a ResponseBody) -> Self {
165use ResponseBody as RMB;
166use ResponseKind as RK;
167// TODO RPC: If we keep the current set of types,
168 // we should have this discriminant code be macro-generated.
169match value {
170 RMB::Error(_) => RK::Error,
171 RMB::Success(_) => RK::Success,
172 RMB::Update(_) => RK::Update,
173 }
174 }
175}
176177/// 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)")]
181struct ResponseWasFatal;
182183impl 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.
188fn try_from_response(
189 response: &Response,
190 ) -> Result<Result<Self, DecodeResponseError>, ResponseWasFatal> {
191use DecodeResponseError as E;
192use ResponseBody as Body;
193match (&response.id, &response.body) {
194 (None, Body::Error(_ignore)) => {
195// No ID, so this is a fatal response.
196 // Re-encode the response.
197Err(ResponseWasFatal)
198 }
199 (None, _) => Ok(Err(E::ProtocolViolation("Missing ID field"))),
200 (Some(id), body) => Ok(Ok(ResponseMeta {
201 id: id.clone(),
202 kind: (body).into(),
203 })),
204 }
205 }
206}
207208/// 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.
214pub(crate) fn try_decode_response_as_err(s: &str) -> Result<Option<RpcError>, DecodeResponseError> {
215let Response { body, .. } = serde_json::from_str(s)?;
216match body {
217 ResponseBody::Error(e) => Ok(Some(e)),
218_ => Ok(None),
219 }
220}
221222/// An error sent by Arti, decoded into its parts.
223#[derive(Clone, Debug, Deserialize, Serialize)]
224#[cfg_attr(test, derive(PartialEq, Eq))]
225pub struct RpcError {
226/// A human-readable message from Arti.
227message: String,
228/// An error code representing the underlying problem.
229code: RpcErrorCode,
230/// One or more `ErrorKind`s, encoded as strings.
231kinds: Vec<String>,
232}
233234impl RpcError {
235/// Return the human-readable message that Arti sent as part of this error.
236pub fn message(&self) -> &str {
237self.message.as_str()
238 }
239/// Return the numeric error code from this error.
240pub fn code(&self) -> RpcErrorCode {
241self.code
242 }
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.
247pub fn kinds_iter(&self) -> impl Iterator<Item = &'_ str> {
248self.kinds.iter().map(|s| s.as_ref())
249 }
250}
251252caret::caret_int! {
253#[derive(serde::Deserialize, serde::Serialize)]
254pub struct RpcErrorCode(i32) {
255/// "The JSON sent is not a valid Request object."
256INVALID_REQUEST = -32600,
257/// "The method does not exist ."
258NO_SUCH_METHOD = -32601,
259/// "Invalid method parameter(s)."
260INVALID_PARAMS = -32602,
261/// "The server suffered some kind of internal problem"
262INTERNAL_ERROR = -32603,
263/// "Some requested object was not valid"
264OBJECT_ERROR = 1,
265/// "Some other error occurred"
266REQUEST_ERROR = 2,
267/// This method exists, but wasn't implemented on this object.
268METHOD_NOT_IMPL = 3,
269 }
270}
271272#[cfg(test)]
273mod 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 @@ -->
287288use super::*;
289290/// Helper: Decode a string into a Response, then convert it into
291 /// a ResponseMeta.
292fn response_meta(s: &str) -> Result<ResponseMeta, DecodeResponseError> {
293match ResponseMeta::try_from_response(&serde_json::from_str::<Response>(s)?) {
294Ok(v) => v,
295Err(_) => {
296let utf8 = Utf8CString::try_from(s.to_string())
297 .map_err(|_| DecodeResponseError::ProtocolViolation("not utf8cstr?"))?;
298Err(DecodeResponseError::Fatal(
299 ErrorResponse::from_validated_string(utf8),
300 ))
301 }
302 }
303 }
304305#[test]
306fn response_meta_good() {
307use ResponseKind as RK;
308use ResponseMeta as RM;
309for (s, expected) in [
310 (
311r#"{"id":7, "result": {}}"#,
312 RM {
313 id: 7.into(),
314 kind: RK::Success,
315 },
316 ),
317 (
318r#"{"id":"hi", "update": {"here":["goes", "nothing"]}}"#,
319 RM {
320 id: "hi".to_string().into(),
321 kind: RK::Update,
322 },
323 ),
324 (
325r#"{"id": 6, "error": {"message":"iffy wobbler", "code":999, "kinds": ["BadVibes"]}}"#,
326 RM {
327 id: 6.into(),
328 kind: RK::Error,
329 },
330 ),
331 (
332r#"{"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 ] {
339let got = response_meta(s).unwrap();
340assert_eq!(got, expected);
341 }
342 }
343344#[test]
345fn response_meta_bad() {
346macro_rules! check_err {
347 { $s:expr, $p:pat } => {
348let got_err = response_meta($s).unwrap_err();
349assert!(matches!(got_err, $p));
350 }
351352 }
353354use DecodeResponseError as E;
355356// No ID; arti is saying we screwed up.
357check_err!(
358r#"{"error": {"message":"iffy wobbler", "code":999, "kinds": ["BadVibes"], "data": {"a":"b"}}}"#,
359 E::Fatal(_)
360 );
361// Missing ID on a success.
362check_err!(r#"{"result": {}}"#, E::ProtocolViolation(_));
363// Missing ID on an update.
364check_err!(r#"{"update": {}}"#, E::ProtocolViolation(_));
365// No recognized type.
366check_err!(r#"{"id": 7, "flupdate": {}}"#, E::JsonProtocolViolation(_));
367// Couldn't parse.
368check_err!(r#"{{{{{"#, E::JsonProtocolViolation(_));
369// Error is no good.
370check_err!(
371r#"{"id": 77 "error": {"message":"iffy wobbler"}}"#,
372 E::JsonProtocolViolation(_)
373 );
374 }
375376#[test]
377fn bad_json() {
378// we rely on the json parser rejecting some things.
379for s in [
380"{ ", // not complete
381"", // Empty.
382"{ \0 }", // contains nul byte.
383"{ \"\0\" }", // string contains nul byte.
384] {
385let r: Result<serde_json::Value, _> = serde_json::from_str(s);
386assert!(dbg!(r.err()).is_some());
387 }
388 }
389390#[test]
391fn re_encode() {
392let 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 }"#;
403let json_orig: serde_json::Value = serde_json::from_str(response).unwrap();
404let resp = UnparsedResponse::new(response.into());
405let valid = resp.try_validate().unwrap();
406let msg: &str = valid.msg.as_ref();
407let 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.
410assert_eq!(json_orig, json_reencoded);
411 }
412}