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}