From fba3298162f9953f8db12121b41424c50fae69e6 Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Tue, 11 Aug 2020 08:06:43 -0400 Subject: [PATCH 1/4] matrix_sdk: Create HttpSend trait to abstract sending requests --- matrix_sdk/Cargo.toml | 1 + matrix_sdk/src/http_send.rs | 60 +++++++++++++++++++++++++++++++++++++ matrix_sdk/src/lib.rs | 2 ++ 3 files changed, 63 insertions(+) create mode 100644 matrix_sdk/src/http_send.rs diff --git a/matrix_sdk/Cargo.toml b/matrix_sdk/Cargo.toml index ac819118..b72669af 100644 --- a/matrix_sdk/Cargo.toml +++ b/matrix_sdk/Cargo.toml @@ -17,6 +17,7 @@ encryption = ["matrix-sdk-base/encryption"] sqlite-cryptostore = ["matrix-sdk-base/sqlite-cryptostore"] [dependencies] +async-trait = "0.1.36" http = "0.2.1" # FIXME: Revert to regular dependency once 0.10.8 or 0.11.0 is released reqwest = { git = "https://github.com/seanmonstar/reqwest", rev = "dd8441fd23dae6ffb79b4cea2862e5bca0c59743" } diff --git a/matrix_sdk/src/http_send.rs b/matrix_sdk/src/http_send.rs new file mode 100644 index 00000000..f3ba9fb0 --- /dev/null +++ b/matrix_sdk/src/http_send.rs @@ -0,0 +1,60 @@ +// Copyright 2020 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fmt::Debug; + +use matrix_sdk_common_macros::async_trait; +use reqwest::Response; + +use crate::Result; + +/// Abstraction around the http layer. The allows implementors to use different +/// http libraries. +#[async_trait] +pub trait HttpSend: Sync + Send + Debug { + /// The method abstracting sending request types and receiving response types. + /// + /// This is called by the client every time it wants to send anything to a homeserver. + /// + /// # Arguments + /// + /// * `request` - The http request that has been converted from a ruma `Request`. + /// + /// # Returns + /// + /// A `reqwest::Response` that will be converted to a ruma `Response` in the `Client`. + /// + /// # Examples + /// + /// ```ignore + /// use matrix_sdk::HttpSend; + /// use matrix_sdk_common_macros::async_trait; + /// use reqwest::Response; + /// + /// #[derive(Debug)] + /// struct TestSend; + /// + /// impl HttpSend for TestSend { + /// async fn send_request(&self, request: http::Request>) -> Result + /// // send the request somehow + /// let response = send(request, method, homeserver).await?; + /// + /// // reqwest can convert to and from `http::Response` types. + /// Ok(reqwest::Response::from(response)) + /// } + /// } + /// + /// ``` + async fn send_request(&self, request: http::Request>) -> Result; +} diff --git a/matrix_sdk/src/lib.rs b/matrix_sdk/src/lib.rs index 200cbce4..edd20fc1 100644 --- a/matrix_sdk/src/lib.rs +++ b/matrix_sdk/src/lib.rs @@ -51,6 +51,7 @@ pub use reqwest::header::InvalidHeaderValue; mod client; mod error; mod http_client; +mod http_send; mod request_builder; #[cfg(feature = "encryption")] @@ -58,6 +59,7 @@ mod sas; pub use client::{Client, ClientConfig, SyncSettings}; pub use error::{Error, Result}; +pub use http_send::HttpSend; pub use request_builder::{ MessagesRequestBuilder, RegistrationBuilder, RoomBuilder, RoomListFilterBuilder, }; From 9294280dc1a9a2fc00691c6dc4bd5e980d2d7caa Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Tue, 11 Aug 2020 08:07:45 -0400 Subject: [PATCH 2/4] matrix_sdk: Add DefaultHttpClient and impl HttpSend --- matrix_sdk/src/client.rs | 74 +++++++++++++---------------------- matrix_sdk/src/http_client.rs | 70 +++++++++++++++++++++++++++++---- 2 files changed, 91 insertions(+), 53 deletions(-) diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index 74f47a2f..3a8494eb 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -47,15 +47,14 @@ use url::Url; use crate::{ events::{room::message::MessageEventContent, EventType}, + http_client::DefaultHttpClient, identifiers::{EventId, RoomId, RoomIdOrAliasId, UserId}, - Endpoint, + Endpoint, HttpSend, }; #[cfg(feature = "encryption")] use crate::{identifiers::DeviceId, sas::Sas}; -#[cfg(not(target_arch = "wasm32"))] -use crate::VERSION; use crate::{api, http_client::HttpClient, EventEmitter, Result}; use matrix_sdk_base::{BaseClient, BaseClientConfig, Room, Session, StateStore}; @@ -108,11 +107,12 @@ impl Debug for Client { #[derive(Default)] pub struct ClientConfig { #[cfg(not(target_arch = "wasm32"))] - proxy: Option, - user_agent: Option, - disable_ssl_verification: bool, - base_config: BaseClientConfig, - timeout: Option, + pub(crate) proxy: Option, + pub(crate) user_agent: Option, + pub(crate) disable_ssl_verification: bool, + pub(crate) base_config: BaseClientConfig, + pub(crate) timeout: Option, + pub(crate) client: Option>, } // #[cfg_attr(tarpaulin, skip)] @@ -212,6 +212,15 @@ impl ClientConfig { self.timeout = Some(timeout); self } + + /// Specify a client to handle sending requests and receiving responses. + /// + /// Any type that implements the `HttpSend` trait can be used to send/receive + /// `http` types. + pub fn client(mut self, client: Arc) -> Self { + self.client = Some(client); + self + } } #[derive(Debug, Default, Clone)] @@ -322,48 +331,21 @@ impl Client { config: ClientConfig, ) -> Result { let homeserver = if let Ok(u) = homeserver_url.try_into() { - u + Arc::new(u) } else { panic!("Error parsing homeserver url") }; - let http_client = reqwest::Client::builder(); - - #[cfg(not(target_arch = "wasm32"))] - let http_client = { - let http_client = match config.timeout { - Some(x) => http_client.timeout(x), - None => http_client, - }; - - let http_client = if config.disable_ssl_verification { - http_client.danger_accept_invalid_certs(true) - } else { - http_client - }; - - let http_client = match config.proxy { - Some(p) => http_client.proxy(p), - None => http_client, - }; - - let mut headers = reqwest::header::HeaderMap::new(); - - let user_agent = match config.user_agent { - Some(a) => a, - None => HeaderValue::from_str(&format!("matrix-rust-sdk {}", VERSION)).unwrap(), - }; - - headers.insert(reqwest::header::USER_AGENT, user_agent); - - http_client.default_headers(headers) - }; - - let homeserver = Arc::new(homeserver); - - let http_client = HttpClient { - homeserver: homeserver.clone(), - inner: http_client.build()?, + let http_client = if let Some(client) = config.client { + HttpClient { + homeserver: homeserver.clone(), + inner: client, + } + } else { + HttpClient { + homeserver: homeserver.clone(), + inner: Arc::new(DefaultHttpClient::with_config(&config)?), + } }; let base_client = BaseClient::new_with_config(config.base_config)?; diff --git a/matrix_sdk/src/http_client.rs b/matrix_sdk/src/http_client.rs index 3e2367a1..000e8e36 100644 --- a/matrix_sdk/src/http_client.rs +++ b/matrix_sdk/src/http_client.rs @@ -12,10 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::{ - convert::{TryFrom, TryInto}, - sync::Arc, -}; +use std::{convert::TryFrom, sync::Arc}; use http::{HeaderValue, Method as HttpMethod, Response as HttpResponse}; use reqwest::{Client, Response}; @@ -24,11 +21,11 @@ use url::Url; use matrix_sdk_common::{locks::RwLock, FromHttpResponseError}; -use crate::{Endpoint, Error, Result, Session}; +use crate::{ClientConfig, Endpoint, Error, HttpSend, Result, Session}; #[derive(Clone, Debug)] pub(crate) struct HttpClient { - pub(crate) inner: Client, + pub(crate) inner: Arc, pub(crate) homeserver: Arc, } @@ -62,7 +59,7 @@ impl HttpClient { ); } - Ok(self.inner.execute(request.try_into()?).await?) + self.inner.send_request(request).await } async fn response_to_http_response( @@ -100,3 +97,62 @@ impl HttpClient { Ok(Request::IncomingResponse::try_from(response)?) } } + +/// Default http client used if none is specified using `Client::with_client`. +#[derive(Clone, Debug)] +pub struct DefaultHttpClient { + inner: Client, +} + +impl DefaultHttpClient { + /// Build a client with the specified configuration. + pub fn with_config(config: &ClientConfig) -> Result { + let http_client = reqwest::Client::builder(); + + #[cfg(not(target_arch = "wasm32"))] + let http_client = { + let http_client = match config.timeout { + Some(x) => http_client.timeout(x), + None => http_client, + }; + + let http_client = if config.disable_ssl_verification { + http_client.danger_accept_invalid_certs(true) + } else { + http_client + }; + + let http_client = match &config.proxy { + Some(p) => http_client.proxy(p.clone()), + None => http_client, + }; + + let mut headers = reqwest::header::HeaderMap::new(); + + let user_agent = match &config.user_agent { + Some(a) => a.clone(), + None => { + HeaderValue::from_str(&format!("matrix-rust-sdk {}", crate::VERSION)).unwrap() + } + }; + + headers.insert(reqwest::header::USER_AGENT, user_agent); + + http_client.default_headers(headers) + }; + + Ok(Self { + inner: http_client.build()?, + }) + } +} + +#[async_trait::async_trait] +impl HttpSend for DefaultHttpClient { + async fn send_request(&self, request: http::Request>) -> Result { + Ok(self + .inner + .execute(reqwest::Request::try_from(request)?) + .await?) + } +} From 4770dc636a04f9e6b35bb12cbda5a7e11cf99edf Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Tue, 11 Aug 2020 08:08:17 -0400 Subject: [PATCH 3/4] matrix_sdk_common_macros: Bump syn fixing conflicting deps --- matrix_sdk_common_macros/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix_sdk_common_macros/Cargo.toml b/matrix_sdk_common_macros/Cargo.toml index 959d5805..6648570f 100644 --- a/matrix_sdk_common_macros/Cargo.toml +++ b/matrix_sdk_common_macros/Cargo.toml @@ -14,5 +14,5 @@ version = "0.1.0" proc-macro = true [dependencies] -syn = "1.0.34" +syn = "1.0.38" quote = "1.0.7" From 9234ac96e11da9392fa832ea9b2619e3d132a886 Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Tue, 11 Aug 2020 09:17:18 -0400 Subject: [PATCH 4/4] matrix_sdk: Use our version of the async_trait macro --- matrix_sdk/src/http_client.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/matrix_sdk/src/http_client.rs b/matrix_sdk/src/http_client.rs index 000e8e36..45793a23 100644 --- a/matrix_sdk/src/http_client.rs +++ b/matrix_sdk/src/http_client.rs @@ -15,12 +15,12 @@ use std::{convert::TryFrom, sync::Arc}; use http::{HeaderValue, Method as HttpMethod, Response as HttpResponse}; +use matrix_sdk_common::{locks::RwLock, FromHttpResponseError}; +use matrix_sdk_common_macros::async_trait; use reqwest::{Client, Response}; use tracing::trace; use url::Url; -use matrix_sdk_common::{locks::RwLock, FromHttpResponseError}; - use crate::{ClientConfig, Endpoint, Error, HttpSend, Result, Session}; #[derive(Clone, Debug)] @@ -141,13 +141,17 @@ impl DefaultHttpClient { http_client.default_headers(headers) }; + #[cfg(target_arch = "wasm32")] + #[allow(unused)] + let _ = config; + Ok(Self { inner: http_client.build()?, }) } } -#[async_trait::async_trait] +#[async_trait] impl HttpSend for DefaultHttpClient { async fn send_request(&self, request: http::Request>) -> Result { Ok(self