//! This module contains an abstraction for HTTP clients as well as friendly-named re-exports of //! client types that implement this trait. use std::{future::Future, pin::Pin}; use async_trait::async_trait; use bytes::BufMut; use ruma_api::{OutgoingRequest, SendAccessToken}; use ruma_identifiers::UserId; use crate::{add_user_id_to_query, ResponseError, ResponseResult}; #[cfg(feature = "hyper")] mod hyper; #[cfg(feature = "isahc")] mod isahc; #[cfg(feature = "reqwest")] mod reqwest; #[cfg(feature = "hyper")] pub use self::hyper::Hyper; #[cfg(feature = "hyper-native-tls")] pub use self::hyper::HyperNativeTls; #[cfg(feature = "hyper-rustls")] pub use self::hyper::HyperRustls; #[cfg(feature = "isahc")] pub use self::isahc::Isahc; #[cfg(feature = "reqwest")] pub use self::reqwest::Reqwest; /// An HTTP client that can be used to send requests to a Matrix homeserver. #[async_trait] pub trait HttpClient: Sync { /// The type to use for `try_into_http_request`. type RequestBody: Default + BufMut + Send; /// The type to use for `try_from_http_response`. type ResponseBody: AsRef<[u8]>; /// The error type for the `send_request` function. type Error: Send + Unpin; /// Send an `http::Request` to get back an `http::Response`. async fn send_http_request( &self, req: http::Request, ) -> Result, Self::Error>; } /// An HTTP client that has a default configuration. pub trait DefaultConstructibleHttpClient: HttpClient { /// Creates a new HTTP client with default configuration. fn default() -> Self; } /// Convenience functionality on top of `HttpClient`. /// /// If you want to build your own matrix client type instead of using `ruma_client::Client`, this /// trait should make that relatively easy. pub trait HttpClientExt: HttpClient { /// Send a strongly-typed matrix request to get back a strongly-typed response. // TODO: `R: 'a` bound should not be needed fn send_matrix_request<'a, R: OutgoingRequest + 'a>( &'a self, homeserver_url: &str, access_token: SendAccessToken<'_>, request: R, ) -> Pin> + 'a>> { self.send_customized_matrix_request(homeserver_url, access_token, request, |_| Ok(())) } /// Turn a strongly-typed matrix request into an `http::Request`, customize it and send it to /// get back a strongly-typed response. // TODO: `R: 'a` and `F: 'a` should not be needed fn send_customized_matrix_request<'a, R, F>( &'a self, homeserver_url: &str, access_token: SendAccessToken<'_>, request: R, customize: F, ) -> Pin> + 'a>> where R: OutgoingRequest + 'a, F: FnOnce(&mut http::Request) -> Result<(), ResponseError> + 'a, { Box::pin(crate::send_customized_request( self, homeserver_url, access_token, request, customize, )) } /// Turn a strongly-typed matrix request into an `http::Request`, add a `user_id` query /// parameter to it and send it to get back a strongly-typed response. /// /// This method is meant to be used by application services when interacting with the /// client-server API. fn send_matrix_request_as<'a, R: OutgoingRequest + 'a>( &'a self, homeserver_url: &str, access_token: SendAccessToken<'_>, user_id: &'a UserId, request: R, ) -> Pin> + 'a>> { self.send_customized_matrix_request( homeserver_url, access_token, request, add_user_id_to_query::(user_id), ) } } #[async_trait] impl HttpClientExt for T {} #[doc(hidden)] #[derive(Debug)] pub struct Dummy; #[async_trait] impl HttpClient for Dummy { type RequestBody = Vec; type ResponseBody = Vec; type Error = (); async fn send_http_request( &self, _req: http::Request, ) -> Result, Self::Error> { unimplemented!("this client only exists to allow doctests to compile") } } impl DefaultConstructibleHttpClient for Dummy { fn default() -> Self { Dummy } }