rustify/
client.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//! Contains the [Client] trait for executing
//! [Endpoints][crate::endpoint::Endpoint].
use crate::errors::ClientError;
use async_trait::async_trait;
use http::{Request, Response};
use std::ops::RangeInclusive;

/// An array of HTTP response codes which indicate a successful response
pub const HTTP_SUCCESS_CODES: RangeInclusive<u16> = 200..=208;

/// Represents an HTTP client which is capable of executing
/// [Endpoints][crate::endpoint::Endpoint] by sending the [Request] generated
/// by the Endpoint and returning a [Response].
#[async_trait]
pub trait Client: Sync + Send {
    /// Sends the given [Request] and returns a [Response]. Implementations
    /// should consolidate all errors into the [ClientError] type.
    async fn send(&self, req: Request<Vec<u8>>) -> Result<Response<Vec<u8>>, ClientError>;

    /// Returns the base URL the client is configured with. This is used for
    /// creating the fully qualified URLs used when executing
    /// [Endpoints][crate::endpoint::Endpoint].
    fn base(&self) -> &str;

    /// This method provides a common interface to
    /// [Endpoints][crate::endpoint::Endpoint] for execution.
    // TODO: remove the allow when the upstream clippy issue is fixed:
    // <https://github.com/rust-lang/rust-clippy/issues/12281>
    #[allow(clippy::blocks_in_conditions)]
    #[instrument(skip(self, req), err)]
    async fn execute(&self, req: Request<Vec<u8>>) -> Result<Response<Vec<u8>>, ClientError> {
        debug!(
            "Client sending {} request to {} with {} bytes of data",
            req.method().to_string(),
            req.uri(),
            req.body().len(),
        );
        let response = self.send(req).await?;

        debug!(
            "Client received {} response with {} bytes of body data",
            response.status().as_u16(),
            response.body().len()
        );

        // Check response
        if !HTTP_SUCCESS_CODES.contains(&response.status().as_u16()) {
            return Err(ClientError::ServerResponseError {
                code: response.status().as_u16(),
                content: String::from_utf8(response.body().to_vec()).ok(),
            });
        }

        // Parse response content
        Ok(response)
    }
}