barter_integration/protocol/http/
mod.rs

1use self::rest::RestRequest;
2use crate::error::SocketError;
3use reqwest::StatusCode;
4use serde::de::DeserializeOwned;
5use tracing::error;
6
7/// Defines an abstract [`RestRequest`] that can be executed by a fully
8/// configurable [`RestClient`](rest::client::RestClient).
9pub mod rest;
10
11/// Defines a configurable [`RequestSigner`](private::RequestSigner) that signs Http
12/// [`RestRequest`] using API specific logic.
13pub mod private;
14
15/// Defines a default [`BuildStrategy`] that builds a non-authenticated Http
16/// [`RestRequest`] with no headers.
17pub mod public;
18
19/// [`RestRequest`] build strategy for the API being interacted with.
20///
21/// An API that requires authenticated [`RestRequest`]s will likely utilise the configurable
22/// [`RequestSigner`](private::RequestSigner) to sign the requests before building.
23///
24/// An API that requires no authentication may just add mandatory `reqwest` headers to the
25/// [`RestRequest`] before building.
26pub trait BuildStrategy {
27    /// Use a [`RestRequest`] and [`reqwest::RequestBuilder`] to construct a [`reqwest::Request`]
28    /// that is ready for executing.
29    ///
30    /// It is expected that any signing or performed during this method, or the addition of any
31    /// `reqwest` headers.
32    fn build<Request>(
33        &self,
34        request: Request,
35        builder: reqwest::RequestBuilder,
36    ) -> Result<reqwest::Request, SocketError>
37    where
38        Request: RestRequest;
39}
40
41/// Utilised by a [`RestClient`](rest::client::RestClient) to deserialise
42/// [`RestRequest::Response`], and upon failure parses API errors
43/// returned from the server.
44pub trait HttpParser {
45    type ApiError: DeserializeOwned;
46    type OutputError: From<SocketError>;
47
48    /// Attempt to parse a [`StatusCode`] & bytes payload into a deserialisable `Response`.
49    fn parse<Response>(
50        &self,
51        status: StatusCode,
52        payload: &[u8],
53    ) -> Result<Response, Self::OutputError>
54    where
55        Response: DeserializeOwned,
56    {
57        // Attempt to deserialise reqwest::Response bytes into Ok(Response)
58        let parse_ok_error = match serde_json::from_slice::<Response>(payload) {
59            Ok(response) => return Ok(response),
60            Err(serde_error) => serde_error,
61        };
62
63        // Attempt to deserialise API Error if Ok(Response) deserialisation failed
64        let parse_api_error_error = match serde_json::from_slice::<Self::ApiError>(payload) {
65            Ok(api_error) => return Err(self.parse_api_error(status, api_error)),
66            Err(serde_error) => serde_error,
67        };
68
69        // Log errors if failed to deserialise reqwest::Response into Response or API Self::Error
70        error!(
71            status_code = ?status,
72            ?parse_ok_error,
73            ?parse_api_error_error,
74            response_body = %String::from_utf8_lossy(payload),
75            "error deserializing HTTP response"
76        );
77
78        Err(Self::OutputError::from(SocketError::DeserialiseBinary {
79            error: parse_ok_error,
80            payload: payload.to_vec(),
81        }))
82    }
83
84    /// If [`parse`](Self::parse) fails to deserialise the `Ok(Response)`, this function parses
85    /// to parse the API [`Self::ApiError`] associated with the response.
86    fn parse_api_error(&self, status: StatusCode, error: Self::ApiError) -> Self::OutputError;
87}