ruma_client/
http_client.rs

1//! This module contains an abstraction for HTTP clients as well as friendly-named re-exports of
2//! client types that implement this trait.
3
4use std::{future::Future, pin::Pin};
5
6use bytes::BufMut;
7use ruma::{
8    api::{OutgoingRequest, SendAccessToken, SupportedVersions},
9    UserId,
10};
11
12use crate::{add_user_id_to_query, ResponseError, ResponseResult};
13
14#[cfg(feature = "hyper")]
15mod hyper;
16#[cfg(feature = "reqwest")]
17mod reqwest;
18
19#[cfg(feature = "hyper")]
20pub use self::hyper::Hyper;
21#[cfg(feature = "hyper-native-tls")]
22pub use self::hyper::HyperNativeTls;
23#[cfg(feature = "hyper-rustls")]
24pub use self::hyper::HyperRustls;
25#[cfg(feature = "reqwest")]
26pub use self::reqwest::Reqwest;
27
28/// An HTTP client that can be used to send requests to a Matrix homeserver.
29pub trait HttpClient: Sync {
30    /// The type to use for `try_into_http_request`.
31    type RequestBody: Default + BufMut + Send;
32
33    /// The type to use for `try_from_http_response`.
34    type ResponseBody: AsRef<[u8]>;
35
36    /// The error type for the `send_request` function.
37    type Error: Send + Unpin;
38
39    /// Send an `http::Request` to get back an `http::Response`.
40    fn send_http_request(
41        &self,
42        req: http::Request<Self::RequestBody>,
43    ) -> impl Future<Output = Result<http::Response<Self::ResponseBody>, Self::Error>> + Send;
44}
45
46/// An HTTP client that has a default configuration.
47pub trait DefaultConstructibleHttpClient: HttpClient {
48    /// Creates a new HTTP client with default configuration.
49    fn default() -> Self;
50}
51
52/// Convenience functionality on top of `HttpClient`.
53///
54/// If you want to build your own matrix client type instead of using `ruma_client::Client`, this
55/// trait should make that relatively easy.
56pub trait HttpClientExt: HttpClient {
57    /// Send a strongly-typed matrix request to get back a strongly-typed response.
58    // TODO: `R: 'a` bound should not be needed
59    fn send_matrix_request<'a, R: OutgoingRequest + 'a>(
60        &'a self,
61        homeserver_url: &str,
62        access_token: SendAccessToken<'_>,
63        for_versions: &SupportedVersions,
64        request: R,
65    ) -> Pin<Box<dyn Future<Output = ResponseResult<Self, R>> + 'a + Send>> {
66        self.send_customized_matrix_request(
67            homeserver_url,
68            access_token,
69            for_versions,
70            request,
71            |_| Ok(()),
72        )
73    }
74
75    /// Turn a strongly-typed matrix request into an `http::Request`, customize it and send it to
76    /// get back a strongly-typed response.
77    // TODO: `R: 'a` and `F: 'a` should not be needed
78    fn send_customized_matrix_request<'a, R, F>(
79        &'a self,
80        homeserver_url: &str,
81        access_token: SendAccessToken<'_>,
82        for_versions: &SupportedVersions,
83        request: R,
84        customize: F,
85    ) -> Pin<Box<dyn Future<Output = ResponseResult<Self, R>> + 'a + Send>>
86    where
87        R: OutgoingRequest + 'a,
88        F: FnOnce(&mut http::Request<Self::RequestBody>) -> Result<(), ResponseError<Self, R>> + 'a,
89    {
90        Box::pin(crate::send_customized_request(
91            self,
92            homeserver_url,
93            access_token,
94            for_versions,
95            request,
96            customize,
97        ))
98    }
99
100    /// Turn a strongly-typed matrix request into an `http::Request`, add a `user_id` query
101    /// parameter to it and send it to get back a strongly-typed response.
102    ///
103    /// This method is meant to be used by application services when interacting with the
104    /// client-server API.
105    fn send_matrix_request_as<'a, R: OutgoingRequest + 'a>(
106        &'a self,
107        homeserver_url: &str,
108        access_token: SendAccessToken<'_>,
109        for_versions: &SupportedVersions,
110        user_id: &'a UserId,
111        request: R,
112    ) -> Pin<Box<dyn Future<Output = ResponseResult<Self, R>> + 'a>> {
113        self.send_customized_matrix_request(
114            homeserver_url,
115            access_token,
116            for_versions,
117            request,
118            add_user_id_to_query::<Self, R>(user_id),
119        )
120    }
121}
122
123impl<T: HttpClient> HttpClientExt for T {}
124
125#[doc(hidden)]
126#[derive(Debug)]
127#[allow(clippy::exhaustive_structs)]
128pub struct Dummy;
129
130impl HttpClient for Dummy {
131    type RequestBody = Vec<u8>;
132    type ResponseBody = Vec<u8>;
133    type Error = ();
134
135    #[allow(clippy::diverging_sub_expression)]
136    async fn send_http_request(
137        &self,
138        _req: http::Request<Self::RequestBody>,
139    ) -> Result<http::Response<Self::ResponseBody>, Self::Error> {
140        unimplemented!("this client only exists to allow doctests to compile")
141    }
142}
143
144impl DefaultConstructibleHttpClient for Dummy {
145    fn default() -> Self {
146        Dummy
147    }
148}