Replace ClientConfig with ClientBuilder

This includes a few not-strictly-related changes that made sense to do at the
same time:

* Check for a functioning homeserver via get_supported_versions regardless of
  whether a homeserver URL or user ID was supplied
* Rename use_discovery_response to respect_login_well_known
* Small appservice documentation improvement because those docs had to be
  touched anyways
* Some test refactorings in tests that had to be touched anyways
This commit is contained in:
Jonas Platte
2022-03-11 18:29:43 +01:00
parent bd7fbea155
commit 147e948fe6
13 changed files with 604 additions and 498 deletions

View File

@@ -90,10 +90,9 @@ pub use matrix_sdk;
pub use matrix_sdk::ruma;
use matrix_sdk::{
bytes::Bytes,
config::ClientConfig,
event_handler::{EventHandler, EventHandlerResult, SyncEvent},
reqwest::Url,
Client, Session,
Client, ClientBuildError, ClientBuilder, Session,
};
use regex::Regex;
use ruma::{
@@ -210,8 +209,8 @@ impl AppService {
/// Create new AppService
///
/// Also creates and caches a [`Client`] for the [`MainUser`].
/// The default [`ClientConfig`] is used, if you want to customize it
/// use [`Self::with_config()`] instead.
/// A default [`ClientBuilder`] is used, if you want to customize it
/// use [`with_client_builder()`][Self::with_client_builder] instead.
///
/// # Arguments
///
@@ -228,19 +227,19 @@ impl AppService {
registration: AppServiceRegistration,
) -> Result<Self> {
let appservice =
Self::with_config(homeserver_url, server_name, registration, ClientConfig::default())
Self::with_client_builder(homeserver_url, server_name, registration, Client::builder())
.await?;
Ok(appservice)
}
/// Same as [`Self::new()`] but lets you provide a [`ClientConfig`] for the
/// [`Client`]
pub async fn with_config(
/// Same as [`new()`][Self::new] but lets you provide a [`ClientBuilder`]
/// for the [`Client`]
pub async fn with_client_builder(
homeserver_url: impl TryInto<Url, Error = url::ParseError>,
server_name: impl TryInto<Box<ServerName>, Error = identifiers::Error>,
registration: AppServiceRegistration,
client_config: ClientConfig,
builder: ClientBuilder,
) -> Result<Self> {
let homeserver_url = homeserver_url.try_into()?;
let server_name = server_name.try_into()?;
@@ -253,7 +252,7 @@ impl AppService {
AppService { homeserver_url, server_name, registration, clients, event_handler };
// we create and cache the [`MainUser`] by default
appservice.create_and_cache_client(&sender_localpart, client_config).await?;
appservice.create_and_cache_client(&sender_localpart, builder).await?;
Ok(appservice)
}
@@ -266,8 +265,9 @@ impl AppService {
///
/// This method is a singleton that saves the client internally for re-use
/// based on the `localpart`. The cached [`Client`] can be retrieved either
/// by calling this method again or by calling [`Self::get_cached_client()`]
/// which is non-async convenience wrapper.
/// by calling this method again or by calling
/// [`get_cached_client()`][Self::get_cached_client] which is non-async
/// convenience wrapper.
///
/// Note that if you want to do actions like joining rooms with a virtual
/// user it needs to be registered first. `Self::register_virtual_user()`
@@ -281,20 +281,20 @@ impl AppService {
/// [assert the identity]: https://matrix.org/docs/spec/application_service/r0.1.2#identity-assertion
pub async fn virtual_user_client(&self, localpart: impl AsRef<str>) -> Result<Client> {
let client =
self.virtual_user_client_with_config(localpart, ClientConfig::default()).await?;
self.virtual_user_client_with_client_builder(localpart, Client::builder()).await?;
Ok(client)
}
/// Same as [`Self::virtual_user_client()`] but with the ability to pass in
/// a [`ClientConfig`]
/// Same as [`virtual_user_client()`][Self::virtual_user_client] but with
/// the ability to pass in a [`ClientBuilder`]
///
/// Since this method is a singleton follow-up calls with different
/// [`ClientConfig`]s will be ignored.
pub async fn virtual_user_client_with_config(
/// [`ClientBuilder`]s will be ignored.
pub async fn virtual_user_client_with_client_builder(
&self,
localpart: impl AsRef<str>,
config: ClientConfig,
builder: ClientBuilder,
) -> Result<Client> {
// TODO: check if localpart is covered by namespace?
let localpart = localpart.as_ref();
@@ -302,7 +302,7 @@ impl AppService {
let client = if let Some(client) = self.clients.get(localpart) {
client.clone()
} else {
self.create_and_cache_client(localpart, config).await?
self.create_and_cache_client(localpart, builder).await?
};
Ok(client)
@@ -311,7 +311,7 @@ impl AppService {
async fn create_and_cache_client(
&self,
localpart: &str,
mut config: ClientConfig,
mut builder: ClientBuilder,
) -> Result<Client> {
let user_id = UserId::parse_with_server_name(localpart, &self.server_name)?;
@@ -319,11 +319,15 @@ impl AppService {
// (`sender_localpart`) by default, so we don't need to assert identity
// in that case
if localpart != self.registration.sender_localpart {
config = config.assert_identity();
builder = builder.assert_identity();
}
let client =
Client::with_config(self.homeserver_url.clone(), config.appservice_mode()).await?;
let client = builder
.homeserver_url(self.homeserver_url.clone())
.appservice_mode()
.build()
.await
.map_err(ClientBuildError::assert_valid_builder_args)?;
let session = Session {
access_token: self.registration.as_token.clone(),
@@ -341,8 +345,9 @@ impl AppService {
/// Get cached [`Client`]
///
/// Will return the client for the given `localpart` if previously
/// constructed with [`Self::virtual_user_client()`] or
/// [`Self::virtual_user_client_with_config()`].
/// constructed with [`virtual_user_client()`][Self::virtual_user_client] or
/// [`virtual_user_client_with_config()`][Self::
/// virtual_user_client_with_client_builder].
///
/// If no `localpart` is given it assumes the [`MainUser`]'s `localpart`. If
/// no client for `localpart` is found it will return an Error.

View File

@@ -4,8 +4,9 @@ use std::{
};
use matrix_sdk::{
config::{ClientConfig, RequestConfig},
config::RequestConfig,
ruma::{api::appservice::Registration, events::room::member::SyncRoomMemberEvent},
Client,
};
use matrix_sdk_appservice::*;
use matrix_sdk_test::{appservice::TransactionBuilder, async_test, EventsJson};
@@ -32,10 +33,17 @@ async fn appservice(registration: Option<Registration>) -> Result<AppService> {
let homeserver_url = mockito::server_url();
let server_name = "localhost";
let client_config =
ClientConfig::default().request_config(RequestConfig::default().disable_retry());
let client_builder = Client::builder()
.request_config(RequestConfig::default().disable_retry())
.check_supported_versions(false);
AppService::with_config(homeserver_url.as_ref(), server_name, registration, client_config).await
AppService::with_client_builder(
homeserver_url.as_ref(),
server_name,
registration,
client_builder,
)
.await
}
#[async_test]

View File

@@ -1,13 +1,9 @@
use std::{env, process::exit};
use matrix_sdk::{
config::{ClientConfig, SyncSettings},
room::Room,
ruma::events::room::member::StrippedRoomMemberEvent,
Client,
config::SyncSettings, room::Room, ruma::events::room::member::StrippedRoomMemberEvent, Client,
};
use tokio::time::{sleep, Duration};
use url::Url;
async fn on_stripped_state_member(
room_member: StrippedRoomMemberEvent,
@@ -44,11 +40,9 @@ async fn login_and_sync(
homeserver_url: String,
username: &str,
password: &str,
) -> Result<(), matrix_sdk::Error> {
#[cfg(not(any(feature = "sled_state_store", feature = "indexeddb_state_store")))]
let client_config = ClientConfig::new();
#[cfg(any(feature = "sled_state_store", feature = "indexeddb_state_store"))]
let mut client_config = ClientConfig::new();
) -> anyhow::Result<()> {
#[allow(unused_mut)]
let mut client_builder = Client::builder().homeserver_url(homeserver_url);
#[cfg(feature = "sled_state_store")]
{
@@ -56,17 +50,16 @@ async fn login_and_sync(
let mut home = dirs::home_dir().expect("no home directory found");
home.push("autojoin_bot");
let state_store = matrix_sdk_sled::StateStore::open_with_path(home)?;
client_config = client_config.state_store(Box::new(state_store));
client_builder = client_builder.state_store(Box::new(state_store));
}
#[cfg(feature = "indexeddb_state_store")]
{
let state_store = matrix_sdk_indexeddb::StateStore::open();
client_config = client_config.state_store(Box::new(state_store));
client_builder = client_builder.state_store(Box::new(state_store));
}
let homeserver_url = Url::parse(&homeserver_url).expect("Couldn't parse the homeserver URL");
let client = Client::with_config(homeserver_url, client_config).await.unwrap();
let client = client_builder.build().await?;
client.login(username, password, None, Some("autojoin bot")).await?;
@@ -80,7 +73,7 @@ async fn login_and_sync(
}
#[tokio::main]
async fn main() -> Result<(), matrix_sdk::Error> {
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init();
let (homeserver_url, username, password) =

View File

@@ -1,14 +1,13 @@
use std::{env, process::exit};
use matrix_sdk::{
config::{ClientConfig, SyncSettings},
config::SyncSettings,
room::Room,
ruma::events::room::message::{
MessageType, RoomMessageEventContent, SyncRoomMessageEvent, TextMessageEventContent,
},
Client,
};
use url::Url;
async fn on_room_message(event: SyncRoomMessageEvent, room: Room) {
if let Room::Joined(room) = room {
@@ -36,11 +35,9 @@ async fn login_and_sync(
homeserver_url: String,
username: String,
password: String,
) -> Result<(), matrix_sdk::Error> {
#[cfg(not(any(feature = "sled_state_store", feature = "indexeddb_state_store")))]
let client_config = ClientConfig::new();
#[cfg(any(feature = "sled_state_store", feature = "indexeddb_state_store"))]
let mut client_config = ClientConfig::new();
) -> anyhow::Result<()> {
#[allow(unused_mut)]
let mut client_builder = Client::builder().homeserver_url(homeserver_url);
#[cfg(feature = "sled_state_store")]
{
@@ -48,19 +45,16 @@ async fn login_and_sync(
let mut home = dirs::home_dir().expect("no home directory found");
home.push("party_bot");
let state_store = matrix_sdk_sled::StateStore::open_with_path(home)?;
client_config = client_config.state_store(Box::new(state_store));
client_builder = client_builder.state_store(Box::new(state_store));
}
#[cfg(feature = "indexeddb_state_store")]
{
let state_store = matrix_sdk_indexeddb::StateStore::open();
client_config = client_config.state_store(Box::new(state_store));
client_builder = client_builder.state_store(Box::new(state_store));
}
let homeserver_url = Url::parse(&homeserver_url).expect("Couldn't parse the homeserver URL");
// create a new Client with the given homeserver url and config
let client = Client::with_config(homeserver_url, client_config).await.unwrap();
let client = client_builder.build().await.unwrap();
client.login(&username, &password, None, Some("command bot")).await?;
println!("logged in as {}", username);
@@ -84,7 +78,7 @@ async fn login_and_sync(
}
#[tokio::main]
async fn main() -> Result<(), matrix_sdk::Error> {
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init();
let (homeserver_url, username, password) =

View File

@@ -0,0 +1,401 @@
use std::sync::Arc;
use matrix_sdk_base::{locks::RwLock, store::StoreConfig, BaseClient, StateStore};
use ruma::{api::client::discover::discover_homeserver, UserId};
use thiserror::Error;
use url::Url;
use super::{Client, ClientInner};
use crate::{
config::RequestConfig,
http_client::{HttpClient, HttpSend, HttpSettings},
HttpError,
};
/// Builder that allows creating and configuring various parts of a [`Client`].
///
/// When setting the `StateStore` it is up to the user to open/connect
/// the storage backend before client creation.
///
/// # Example
///
/// ```
/// use matrix_sdk::Client;
/// // To pass all the request through mitmproxy set the proxy and disable SSL
/// // verification
///
/// let client_builder = Client::builder()
/// .proxy("http://localhost:8080")?
/// .disable_ssl_verification();
/// ```
///
/// # Example for using a custom http client
///
/// Note: setting a custom http client will ignore `user_agent`, `proxy`, and
/// `disable_ssl_verification` - you'd need to set these yourself if you want
/// them.
///
/// ```
/// use matrix_sdk::Client;
/// use std::sync::Arc;
///
/// // setting up a custom http client
/// let reqwest_builder = reqwest::ClientBuilder::new()
/// .https_only(true)
/// .no_proxy()
/// .user_agent("MyApp/v3.0");
///
/// let client_builder = Client::builder()
/// .http_client(Arc::new(reqwest_builder.build()?));
/// # anyhow::Ok(())
/// ```
#[must_use]
#[derive(Debug)]
pub struct ClientBuilder {
homeserver_cfg: Option<HomeserverConfig>,
http_cfg: Option<HttpConfig>,
store_config: StoreConfig,
request_config: RequestConfig,
respect_login_well_known: bool,
appservice_mode: bool,
check_supported_versions: bool,
}
impl ClientBuilder {
pub(crate) fn new() -> Self {
Self {
homeserver_cfg: None,
http_cfg: None,
store_config: Default::default(),
request_config: Default::default(),
respect_login_well_known: true,
appservice_mode: false,
check_supported_versions: true,
}
}
/// Set the homeserver URL to use.
///
/// This method is mutually exclusive with [`user_id()`][Self::user_id], if
/// you set both whatever was set last will be used.
pub fn homeserver_url(mut self, url: impl AsRef<str>) -> Self {
self.homeserver_cfg = Some(HomeserverConfig::Url(url.as_ref().to_owned()));
self
}
/// Set the user ID to discover the homeserver from.
///
/// This method is mutually exclusive with
/// [`homeserver_url()`][Self::homeserver_url], if you set both whatever was
/// set last will be used.
pub fn user_id(mut self, user_id: &UserId) -> Self {
self.homeserver_cfg = Some(HomeserverConfig::UserId(user_id.to_owned()));
self
}
/// Create a new `ClientConfig` with the given [`StoreConfig`].
///
/// The easiest way to get a [`StoreConfig`] is to use the
/// [`make_store_config`] method from the [`store`] module or directly from
/// one of the store crates.
///
/// # Arguments
///
/// * `store_config` - The configuration of the store.
///
/// # Example
///
/// ```
/// # use matrix_sdk_base::store::MemoryStore;
/// # let custom_state_store = Box::new(MemoryStore::new());
/// use matrix_sdk::{Client, config::StoreConfig};
///
/// let store_config = StoreConfig::new().state_store(custom_state_store);
/// let client_builder = Client::builder().store_config(store_config);
/// ```
/// [`make_store_config`]: crate::store::make_store_config
/// [`store`]: crate::store
pub fn store_config(mut self, store_config: StoreConfig) -> Self {
self.store_config = store_config;
self
}
/// Set a custom implementation of a `StateStore`.
///
/// The state store should be opened before being set.
pub fn state_store(mut self, store: Box<dyn StateStore>) -> Self {
self.store_config = self.store_config.state_store(store);
self
}
/// Set a custom implementation of a `CryptoStore`.
///
/// The crypto store should be opened before being set.
#[cfg(feature = "encryption")]
pub fn crypto_store(
mut self,
store: Box<dyn matrix_sdk_base::crypto::store::CryptoStore>,
) -> Self {
self.store_config = self.store_config.crypto_store(store);
self
}
/// Update the client's homeserver URL with the discovery information
/// present in the login response, if any.
pub fn respect_login_well_known(mut self, value: bool) -> Self {
self.respect_login_well_known = value;
self
}
/// Set the default timeout, fail and retry behavior for all HTTP requests.
pub fn request_config(mut self, request_config: RequestConfig) -> Self {
self.request_config = request_config;
self
}
/// Set the proxy through which all the HTTP requests should go.
///
/// Note, only HTTP proxies are supported.
///
/// # Arguments
///
/// * `proxy` - The HTTP URL of the proxy.
///
/// # Example
///
/// ```
/// # futures::executor::block_on(async {
/// use matrix_sdk::{Client, config::ClientConfig};
///
/// let client_config = ClientConfig::new()
/// .proxy("http://localhost:8080")?;
///
/// # Result::<_, matrix_sdk::Error>::Ok(())
/// # });
/// ```
#[cfg(not(target_arch = "wasm32"))]
pub fn proxy(mut self, proxy: impl AsRef<str>) -> Self {
self.http_settings().proxy = Some(proxy.as_ref().to_owned());
self
}
/// Disable SSL verification for the HTTP requests.
#[cfg(not(target_arch = "wasm32"))]
pub fn disable_ssl_verification(mut self) -> Self {
self.http_settings().disable_ssl_verification = true;
self
}
/// Set a custom HTTP user agent for the client.
#[cfg(not(target_arch = "wasm32"))]
pub fn user_agent(mut self, user_agent: impl AsRef<str>) -> Self {
self.http_settings().user_agent = Some(user_agent.as_ref().to_owned());
self
}
/// Specify an HTTP client to handle sending requests and receiving
/// responses.
///
/// Any type that implements the `HttpSend` trait can be used to send /
/// receive `http` types.
///
/// This method is mutually exclusive with
/// [`user_agent()`][Self::user_agent],
pub fn http_client(mut self, client: Arc<dyn HttpSend>) -> Self {
self.http_cfg = Some(HttpConfig::Custom(client));
self
}
/// Puts the client into application service mode
///
/// This is low-level functionality. For an high-level API check the
/// `matrix_sdk_appservice` crate.
#[doc(hidden)]
#[cfg(feature = "appservice")]
pub fn appservice_mode(mut self) -> Self {
self.appservice_mode = true;
self
}
/// All outgoing http requests will have a GET query key-value appended with
/// `user_id` being the key and the `user_id` from the `Session` being
/// the value. Will error if there's no `Session`. This is called
/// [identity assertion] in the Matrix Application Service Spec
///
/// [identity assertion]: https://spec.matrix.org/unstable/application-service-api/#identity-assertion
#[doc(hidden)]
#[cfg(feature = "appservice")]
pub fn assert_identity(mut self) -> Self {
self.request_config.assert_identity = true;
self
}
/// Specify whether the homeserver functionality should be checked through a
/// get_supported_versions request.
///
/// This is helpful for test code that doesn't care to mock that endpoint.
#[doc(hidden)]
pub fn check_supported_versions(mut self, value: bool) -> Self {
self.check_supported_versions = value;
self
}
#[cfg(not(target_arch = "wasm32"))]
fn http_settings(&mut self) -> &mut HttpSettings {
self.http_cfg.get_or_insert_with(Default::default).settings()
}
/// Create a [`Client`] with the options set on this builder.
///
/// # Errors
///
/// This method can fail for two general reasons:
///
/// * Invalid input: a missing or invalid homeserver URL or invalid proxy
/// URL
/// * HTTP error: If you supplied a user ID instead of a homeserver URL, a
/// server discovery request is made which can fail; if you didn't set
/// [`check_supported_versions(false)`][Self::check_supported_versions],
/// that amounts to another request that can fail
pub async fn build(self) -> Result<Client, ClientBuildError> {
let homeserver_cfg = self.homeserver_cfg.ok_or(ClientBuildError::MissingHomeserver)?;
let inner_http_client = match self.http_cfg.unwrap_or_default() {
#[allow(unused_mut)]
HttpConfig::Settings(mut settings) => {
#[cfg(not(target_arch = "wasm32"))]
{
settings.timeout = self.request_config.timeout;
}
Arc::new(settings.make_client()?)
}
HttpConfig::Custom(c) => c,
};
let base_client = BaseClient::with_store_config(self.store_config);
let mk_http_client = |homeserver| {
HttpClient::new(
inner_http_client.clone(),
homeserver,
base_client.session().clone(),
self.request_config,
)
};
let homeserver = match homeserver_cfg {
HomeserverConfig::Url(url) => url,
HomeserverConfig::UserId(user_id) => {
let homeserver = homeserver_from_user_id(&user_id)?;
let http_client = mk_http_client(Arc::new(RwLock::new(homeserver)));
let well_known =
http_client.send(discover_homeserver::Request::new(), None).await?;
well_known.homeserver.base_url
}
};
let homeserver = Arc::new(RwLock::new(Url::parse(&homeserver)?));
let http_client = mk_http_client(homeserver.clone());
let inner = Arc::new(ClientInner {
homeserver,
http_client,
base_client,
#[cfg(feature = "encryption")]
group_session_locks: Default::default(),
#[cfg(feature = "encryption")]
key_claim_lock: Default::default(),
members_request_locks: Default::default(),
typing_notice_times: Default::default(),
event_handlers: Default::default(),
event_handler_data: Default::default(),
notification_handlers: Default::default(),
appservice_mode: self.appservice_mode,
respect_login_well_known: self.respect_login_well_known,
sync_beat: event_listener::Event::new(),
});
let client = Client { inner };
if self.check_supported_versions {
client.get_supported_versions().await?;
}
Ok(client)
}
}
fn homeserver_from_user_id(user_id: &UserId) -> Result<Url, url::ParseError> {
let homeserver = format!("https://{}", user_id.server_name());
#[allow(unused_mut)]
let mut result = Url::parse(homeserver.as_str())?;
// Mockito only knows how to test http endpoints:
// https://github.com/lipanski/mockito/issues/127
#[cfg(test)]
let _ = result.set_scheme("http");
Ok(result)
}
#[derive(Debug)]
enum HomeserverConfig {
Url(String),
UserId(Box<UserId>),
}
#[derive(Debug)]
enum HttpConfig {
Settings(HttpSettings),
Custom(Arc<dyn HttpSend>),
}
#[cfg(not(target_arch = "wasm32"))]
impl HttpConfig {
fn settings(&mut self) -> &mut HttpSettings {
match self {
Self::Settings(s) => s,
Self::Custom(_) => {
*self = Self::default();
match self {
Self::Settings(s) => s,
Self::Custom(_) => unreachable!(),
}
}
}
}
}
impl Default for HttpConfig {
fn default() -> Self {
Self::Settings(HttpSettings::default())
}
}
/// Errors that can happen in [`ClientBuilder::build`].
#[derive(Debug, Error)]
pub enum ClientBuildError {
/// No homeserver or user ID was configured
#[error("no homeserver or user ID was configured")]
MissingHomeserver,
/// An error encountered when trying to parse the homeserver url.
#[error(transparent)]
Url(#[from] url::ParseError),
/// Error doing an HTTP request.
#[error(transparent)]
Http(#[from] HttpError),
}
impl ClientBuildError {
/// Assert that a valid homeserver URL was given to the builder and no other
/// invalid options were specified, which means the only possible error
/// case is [`Self::Http`].
#[doc(hidden)]
pub fn assert_valid_builder_args(self) -> HttpError {
match self {
ClientBuildError::Http(e) => e,
_ => unreachable!("homeserver URL was asserted to be valid"),
}
}
}

View File

@@ -44,7 +44,7 @@ use ruma::{
capabilities::{get_capabilities, Capabilities},
device::{delete_devices, get_devices},
directory::{get_public_rooms, get_public_rooms_filtered},
discover::{discover_homeserver, get_supported_versions},
discover::get_supported_versions,
filter::{create_filter::v3::Request as FilterUploadRequest, FilterDefinition},
media::{create_content, get_content, get_content_thumbnail},
membership::{join_room_by_id, join_room_by_id_or_alias},
@@ -67,13 +67,17 @@ use url::Url;
use crate::{
attachment::{AttachmentInfo, Thumbnail},
config::{ClientConfig, RequestConfig},
config::RequestConfig,
error::{HttpError, HttpResult},
event_handler::{EventHandler, EventHandlerData, EventHandlerResult, EventKind, SyncEvent},
http_client::{client_with_config, HttpClient},
http_client::HttpClient,
room, Account, Error, Result,
};
mod builder;
pub use self::builder::{ClientBuildError, ClientBuilder};
/// A conservative upload speed of 1Mbps
const DEFAULT_UPLOAD_SPEED: u64 = 125_000;
/// 5 min minimal upload request timeout, used to clamp the request timeout.
@@ -139,7 +143,7 @@ pub(crate) struct ClientInner {
appservice_mode: bool,
/// Whether the client should update its homeserver URL with the discovery
/// information present in the login response.
use_discovery_response: bool,
respect_login_well_known: bool,
/// An event that can be listened on to wait for a successful sync. The
/// event will only be fired if a sync loop is running. Can be used for
/// synchronization, e.g. if we send out a request to create a room, we can
@@ -161,53 +165,12 @@ impl Client {
/// # Arguments
///
/// * `homeserver_url` - The homeserver that the client should connect to.
pub async fn new(homeserver_url: Url) -> Result<Self> {
let config = ClientConfig::new();
Client::with_config(homeserver_url, config).await
}
/// Create a new [`Client`] for the given homeserver and use the given
/// configuration.
///
/// # Arguments
///
/// * `homeserver_url` - The homeserver that the client should connect to.
///
/// * `config` - Configuration for the client.
pub async fn with_config(homeserver_url: Url, config: ClientConfig) -> Result<Self> {
let homeserver = Arc::new(RwLock::new(homeserver_url));
let client = if let Some(client) = config.client {
client
} else {
Arc::new(client_with_config(&config)?)
};
let base_client = BaseClient::with_store_config(config.store_config);
let session = base_client.session().clone();
let http_client =
HttpClient::new(client, homeserver.clone(), session, config.request_config);
let inner = Arc::new(ClientInner {
homeserver,
http_client,
base_client,
#[cfg(feature = "encryption")]
group_session_locks: Default::default(),
#[cfg(feature = "encryption")]
key_claim_lock: Default::default(),
members_request_locks: Default::default(),
typing_notice_times: Default::default(),
event_handlers: Default::default(),
event_handler_data: Default::default(),
notification_handlers: Default::default(),
appservice_mode: config.appservice_mode,
use_discovery_response: config.use_discovery_response,
sync_beat: event_listener::Event::new(),
});
Ok(Self { inner })
pub async fn new(homeserver_url: Url) -> Result<Self, HttpError> {
Self::builder()
.homeserver_url(homeserver_url)
.build()
.await
.map_err(ClientBuildError::assert_valid_builder_args)
}
/// Create a new [`Client`] using homeserver auto discovery.
@@ -240,37 +203,17 @@ impl Client {
/// ```
///
/// [spec]: https://spec.matrix.org/unstable/client-server-api/#well-known-uri
pub async fn new_from_user_id(user_id: &UserId) -> Result<Self> {
let config = ClientConfig::new();
Client::new_from_user_id_with_config(user_id, config).await
pub async fn new_from_user_id(user_id: &UserId) -> Result<Self, HttpError> {
Self::builder()
.user_id(user_id)
.build()
.await
.map_err(ClientBuildError::assert_valid_builder_args)
}
/// Create a new [`Client`] using homeserver auto discovery.
///
/// This method will create a [`Client`] object that will attempt to
/// discover and configure the homeserver for the given user. Follows the
/// homeserver discovery directions described in the [spec].
///
/// # Arguments
///
/// * `user_id` - The id of the user whose homeserver the client should
/// connect to.
///
/// * `config` - Configuration for the client.
///
/// [spec]: https://spec.matrix.org/unstable/client-server-api/#well-known-uri
pub async fn new_from_user_id_with_config(
user_id: &UserId,
config: ClientConfig,
) -> Result<Self> {
let homeserver = Client::homeserver_from_user_id(user_id)?;
let client = Client::with_config(homeserver, config).await?;
let well_known = client.discover_homeserver().await?;
let well_known = Url::parse(well_known.homeserver.base_url.as_ref())?;
client.set_homeserver(well_known).await;
client.get_supported_versions().await?;
Ok(client)
/// Create a new [`ClientBuilder`].
pub fn builder() -> ClientBuilder {
ClientBuilder::new()
}
pub(crate) fn base_client(&self) -> &BaseClient {
@@ -291,22 +234,6 @@ impl Client {
self.base_client().mark_request_as_sent(request_id, response).await
}
fn homeserver_from_user_id(user_id: &UserId) -> Result<Url> {
let homeserver = format!("https://{}", user_id.server_name());
#[allow(unused_mut)]
let mut result = Url::parse(homeserver.as_str())?;
// Mockito only knows how to test http endpoints:
// https://github.com/lipanski/mockito/issues/127
#[cfg(test)]
let _ = result.set_scheme("http");
Ok(result)
}
async fn discover_homeserver(&self) -> HttpResult<discover_homeserver::Response> {
self.send(discover_homeserver::Request::new(), Some(RequestConfig::new().disable_retry()))
.await
}
/// Change the homeserver URL used by this client.
///
/// # Arguments
@@ -472,7 +399,12 @@ impl Client {
/// use serde::{Deserialize, Serialize};
///
/// # block_on(async {
/// # let client = Client::new(homeserver).await.unwrap();
/// # let client = matrix_sdk::Client::builder()
/// # .homeserver_url(homeserver)
/// # .check_supported_versions(false)
/// # .build()
/// # .await
/// # .unwrap();
/// client
/// .register_event_handler(
/// |ev: SyncRoomMessageEvent, room: Room, client: Client| async move {
@@ -586,7 +518,12 @@ impl Client {
/// # fn obtain_gui_handle() -> SomeType { SomeType }
/// # let homeserver = url::Url::parse("http://localhost:8080").unwrap();
/// # block_on(async {
/// # let client = matrix_sdk::Client::new(homeserver).await.unwrap();
/// # let client = matrix_sdk::Client::builder()
/// # .homeserver_url(homeserver)
/// # .check_supported_versions(false)
/// # .build()
/// # .await
/// # .unwrap();
///
/// // Handle used to send messages to the UI part of the app
/// let my_gui_handle: SomeType = obtain_gui_handle();
@@ -1135,7 +1072,7 @@ impl Client {
///
/// * `response` - A successful login response.
async fn receive_login_response(&self, response: &login::v3::Response) -> Result<()> {
if self.inner.use_discovery_response {
if self.inner.respect_login_well_known {
if let Some(well_known) = &response.well_known {
if let Ok(homeserver) = Url::parse(&well_known.homeserver.base_url) {
self.set_homeserver(homeserver).await;
@@ -2325,6 +2262,7 @@ impl Client {
})
}
}
// mockito (the http mocking library) is not supported for wasm32
#[cfg(all(test, not(target_arch = "wasm32")))]
pub(crate) mod test {
@@ -2367,26 +2305,38 @@ pub(crate) mod test {
mxc_uri, room_id, thirdparty, uint, user_id, TransactionId, UserId,
};
use serde_json::json;
use url::Url;
use super::{Client, Session, Url};
use super::{Client, ClientBuilder, Session};
use crate::{
attachment::{
AttachmentConfig, AttachmentInfo, BaseImageInfo, BaseThumbnailInfo, BaseVideoInfo,
Thumbnail,
},
config::{ClientConfig, RequestConfig, SyncSettings},
config::{RequestConfig, SyncSettings},
HttpError, RoomMember,
};
fn test_client_builder() -> ClientBuilder {
let homeserver = Url::parse(&mockito::server_url()).unwrap();
Client::builder().homeserver_url(homeserver).check_supported_versions(false)
}
async fn no_retry_test_client() -> Client {
test_client_builder()
.request_config(RequestConfig::new().disable_retry())
.build()
.await
.unwrap()
}
pub(crate) async fn logged_in_client() -> Client {
let session = Session {
access_token: "1234".to_owned(),
user_id: user_id!("@example:localhost").to_owned(),
device_id: device_id!("DEVICEID").to_owned(),
};
let homeserver = url::Url::parse(&mockito::server_url()).unwrap();
let config = ClientConfig::new().request_config(RequestConfig::new().disable_retry());
let client = Client::with_config(homeserver, config).await.unwrap();
let client = no_retry_test_client().await;
client.restore_login(session).await.unwrap();
client
@@ -2394,12 +2344,8 @@ pub(crate) mod test {
#[async_test]
async fn set_homeserver() {
let client = no_retry_test_client().await;
let homeserver = Url::from_str("http://example.com/").unwrap();
let client = Client::new(homeserver).await.unwrap();
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
client.set_homeserver(homeserver.clone()).await;
assert_eq!(client.homeserver().await, homeserver);
@@ -2450,8 +2396,7 @@ pub(crate) mod test {
#[async_test]
async fn login() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let client = Client::new(homeserver.clone()).await.unwrap();
let client = no_retry_test_client().await;
let _m_types = mock("GET", "/_matrix/client/r0/login")
.with_status(200)
@@ -2482,10 +2427,7 @@ pub(crate) mod test {
#[async_test]
async fn login_with_discovery() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let config = ClientConfig::new().use_discovery_response();
let client = Client::with_config(homeserver, config).await.unwrap();
let client = no_retry_test_client().await;
let _m_login = mock("POST", "/_matrix/client/r0/login")
.with_status(200)
@@ -2502,10 +2444,7 @@ pub(crate) mod test {
#[async_test]
async fn login_no_discovery() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let config = ClientConfig::new().use_discovery_response();
let client = Client::with_config(homeserver.clone(), config).await.unwrap();
let client = no_retry_test_client().await;
let _m_login = mock("POST", "/_matrix/client/r0/login")
.with_status(200)
@@ -2517,7 +2456,7 @@ pub(crate) mod test {
let logged_in = client.logged_in().await;
assert!(logged_in, "Client should be logged in");
assert_eq!(client.homeserver().await, homeserver);
assert_eq!(client.homeserver().await, Url::parse(&mockito::server_url()).unwrap());
}
#[cfg(feature = "sso_login")]
@@ -2529,7 +2468,7 @@ pub(crate) mod test {
.create();
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let client = Client::new(homeserver).await.unwrap();
let client = no_retry_test_client().await;
let idp = crate::client::get_login_types::v3::IdentityProvider::new(
"some-id".to_owned(),
"idp-name".to_owned(),
@@ -2564,9 +2503,7 @@ pub(crate) mod test {
#[async_test]
async fn login_with_sso_token() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let client = Client::new(homeserver).await.unwrap();
let client = no_retry_test_client().await;
let _m = mock("GET", "/_matrix/client/r0/login")
.with_status(200)
@@ -2610,7 +2547,6 @@ pub(crate) mod test {
#[async_test]
async fn test_join_leave_room() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let room_id = room_id!("!SVkFJHzfwvuaIEawgC:localhost");
let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned()))
@@ -2633,8 +2569,7 @@ pub(crate) mod test {
assert!(room.is_some());
// test store reloads with correct room state from the state store
let config = ClientConfig::new().request_config(RequestConfig::new().disable_retry());
let joined_client = Client::with_config(homeserver, config).await.unwrap();
let joined_client = no_retry_test_client().await;
joined_client.restore_login(session).await.unwrap();
// joined room reloaded from state store
@@ -2694,9 +2629,7 @@ pub(crate) mod test {
#[async_test]
async fn login_error() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let config = ClientConfig::default().request_config(RequestConfig::new().disable_retry());
let client = Client::with_config(homeserver, config).await.unwrap();
let client = no_retry_test_client().await;
let _m = mock("POST", "/_matrix/client/r0/login")
.with_status(403)
@@ -2724,8 +2657,7 @@ pub(crate) mod test {
#[async_test]
async fn register_error() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let client = Client::new(homeserver).await.unwrap();
let client = no_retry_test_client().await;
let _m = mock("POST", Matcher::Regex(r"^/_matrix/client/r0/register\?.*$".to_owned()))
.with_status(403)
@@ -2868,8 +2800,7 @@ pub(crate) mod test {
#[async_test]
async fn room_search_all() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let client = Client::new(homeserver).await.unwrap();
let client = no_retry_test_client().await;
let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/publicRooms".to_owned()))
.with_status(200)
@@ -3554,8 +3485,7 @@ pub(crate) mod test {
#[async_test]
async fn delete_devices() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let client = Client::new(homeserver).await.unwrap();
let client = no_retry_test_client().await;
let _m = mock("POST", "/_matrix/client/r0/delete_devices")
.with_status(401)
@@ -3610,10 +3540,13 @@ pub(crate) mod test {
#[async_test]
async fn retry_limit_http_requests() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let config = ClientConfig::default().request_config(RequestConfig::new().retry_limit(3));
assert!(config.request_config.retry_limit.unwrap() == 3);
let client = Client::with_config(homeserver, config).await.unwrap();
let client = test_client_builder()
.request_config(RequestConfig::new().retry_limit(3))
.build()
.await
.unwrap();
assert!(client.inner.http_client.request_config.retry_limit.unwrap() == 3);
let m = mock("POST", "/_matrix/client/r0/login").with_status(501).expect(3).create();
@@ -3626,13 +3559,15 @@ pub(crate) mod test {
#[async_test]
async fn retry_timeout_http_requests() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
// Keep this timeout small so that the test doesn't take long
let retry_timeout = Duration::from_secs(5);
let config = ClientConfig::default()
.request_config(RequestConfig::new().retry_timeout(retry_timeout));
assert!(config.request_config.retry_timeout.unwrap() == retry_timeout);
let client = Client::with_config(homeserver, config).await.unwrap();
let client = test_client_builder()
.request_config(RequestConfig::new().retry_timeout(retry_timeout))
.build()
.await
.unwrap();
assert!(client.inner.http_client.request_config.retry_timeout.unwrap() == retry_timeout);
let m =
mock("POST", "/_matrix/client/r0/login").with_status(501).expect_at_least(2).create();
@@ -3646,8 +3581,7 @@ pub(crate) mod test {
#[async_test]
async fn short_retry_initial_http_requests() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let client = Client::new(homeserver).await.unwrap();
let client = test_client_builder().build().await.unwrap();
let m =
mock("POST", "/_matrix/client/r0/login").with_status(501).expect_at_least(3).create();
@@ -3759,7 +3693,6 @@ pub(crate) mod test {
#[async_test]
async fn test_state_event_getting() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let room_id = room_id!("!SVkFJHzfwvuaIEawgC:localhost");
let session = Session {
@@ -3828,8 +3761,11 @@ pub(crate) mod test {
.with_body(sync.to_string())
.create();
let config = ClientConfig::default().request_config(RequestConfig::new().retry_limit(3));
let client = Client::with_config(homeserver.clone(), config).await.unwrap();
let client = test_client_builder()
.request_config(RequestConfig::new().retry_limit(3))
.build()
.await
.unwrap();
client.restore_login(session.clone()).await.unwrap();
let room = client.get_joined_room(room_id);

View File

@@ -1,252 +0,0 @@
// Copyright 2021 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.
#[allow(unused_imports)]
use std::{
fmt::{self, Debug},
path::Path,
sync::Arc,
};
use http::header::InvalidHeaderValue;
use matrix_sdk_base::StateStore;
use crate::{
config::{RequestConfig, StoreConfig},
HttpSend, Result,
};
/// Configuration for the creation of the `Client`.
///
/// When setting the `StateStore` it is up to the user to open/connect
/// the storage backend before client creation.
///
/// # Example
///
/// ```
/// use matrix_sdk::config::ClientConfig;
/// // To pass all the request through mitmproxy set the proxy and disable SSL
/// // verification
///
/// # futures::executor::block_on(async {
/// let client_config = ClientConfig::new()
/// .proxy("http://localhost:8080")?
/// .disable_ssl_verification();
/// # matrix_sdk::Result::<()>::Ok(())
/// # });
/// ```
///
/// # Example for using a custom client
/// Note: setting a custom client will ignore `user_agent`, `proxy`, and
/// `disable_ssl_verification` - you'd need to set these yourself if you
/// want them.
///
/// ```
/// use matrix_sdk::config::ClientConfig;
/// use reqwest::ClientBuilder;
/// use std::sync::Arc;
///
/// // setting up a custom builder
/// let builder = ClientBuilder::new()
/// .https_only(true)
/// .no_proxy()
/// .user_agent("MyApp/v3.0");
///
/// # futures::executor::block_on(async {
/// let client_config = ClientConfig::new()
/// .client(Arc::new(builder.build()?));
/// # matrix_sdk::Result::<()>::Ok(())
/// # });
/// ```
#[derive(Default)]
pub struct ClientConfig {
#[cfg(not(target_arch = "wasm32"))]
pub(crate) proxy: Option<reqwest::Proxy>,
pub(crate) user_agent: Option<String>,
pub(crate) disable_ssl_verification: bool,
pub(crate) store_config: StoreConfig,
pub(crate) request_config: RequestConfig,
pub(crate) client: Option<Arc<dyn HttpSend>>,
pub(crate) appservice_mode: bool,
pub(crate) use_discovery_response: bool,
}
#[cfg(not(tarpaulin_include))]
impl Debug for ClientConfig {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut res = fmt.debug_struct("ClientConfig");
#[cfg(not(target_arch = "wasm32"))]
let res = res.field("proxy", &self.proxy);
res.field("user_agent", &self.user_agent)
.field("disable_ssl_verification", &self.disable_ssl_verification)
.field("request_config", &self.request_config)
.finish()
}
}
impl ClientConfig {
/// Create a new default `ClientConfig`.
pub fn new() -> Self {
Self::default()
}
/// Create a new `ClientConfig` with the given [`StoreConfig`].
///
/// The easiest way to get a [`StoreConfig`] is to use the
/// [`make_store_config`] method from the [`store`] module or directly from
/// one of the store crates.
///
/// # Arguments
///
/// * `store_config` - The configuration of the store.
///
/// # Example
///
/// ```
/// # futures::executor::block_on(async {
/// # use matrix_sdk_base::store::MemoryStore;
/// # let custom_state_store = Box::new(MemoryStore::new());
/// use matrix_sdk::{Client, config::{ClientConfig, StoreConfig}};
///
/// let store_config = StoreConfig::new().state_store(custom_state_store);
/// let client_config = ClientConfig::new()
/// .store_config(store_config)
/// .use_discovery_response();
///
/// # Result::<_, matrix_sdk::Error>::Ok(())
/// # });
/// ```
/// [`make_store_config`]: crate::store::make_store_config
/// [`store`]: crate::store
pub fn store_config(mut self, store_config: StoreConfig) -> Self {
self.store_config = store_config;
self
}
/// Set the proxy through which all the HTTP requests should go.
///
/// Note, only HTTP proxies are supported.
///
/// # Arguments
///
/// * `proxy` - The HTTP URL of the proxy.
///
/// # Example
///
/// ```
/// # futures::executor::block_on(async {
/// use matrix_sdk::{Client, config::ClientConfig};
///
/// let client_config = ClientConfig::new()
/// .proxy("http://localhost:8080")?;
///
/// # Result::<_, matrix_sdk::Error>::Ok(())
/// # });
/// ```
#[cfg(not(target_arch = "wasm32"))]
pub fn proxy(mut self, proxy: &str) -> Result<Self> {
self.proxy = Some(reqwest::Proxy::all(proxy)?);
Ok(self)
}
/// Disable SSL verification for the HTTP requests.
#[must_use]
pub fn disable_ssl_verification(mut self) -> Self {
self.disable_ssl_verification = true;
self
}
/// Set a custom HTTP user agent for the client.
pub fn user_agent(mut self, user_agent: &str) -> Result<Self, InvalidHeaderValue> {
self.user_agent = Some(user_agent.to_owned());
Ok(self)
}
/// Set a custom implementation of a `StateStore`.
///
/// The state store should be opened before being set.
pub fn state_store(mut self, store: Box<dyn StateStore>) -> Self {
self.store_config = self.store_config.state_store(store);
self
}
/// Set the default timeout, fail and retry behavior for all HTTP requests.
#[must_use]
pub fn request_config(mut self, request_config: RequestConfig) -> Self {
self.request_config = request_config;
self
}
/// Get the [`RequestConfig`]
pub fn get_request_config(&self) -> &RequestConfig {
&self.request_config
}
/// 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.
#[must_use]
pub fn client(mut self, client: Arc<dyn HttpSend>) -> Self {
self.client = Some(client);
self
}
/// Puts the client into application service mode
///
/// This is low-level functionality. For an high-level API check the
/// `matrix_sdk_appservice` crate.
#[cfg(feature = "appservice")]
#[must_use]
pub fn appservice_mode(mut self) -> Self {
self.appservice_mode = true;
self
}
/// Set a custom implementation of a `CryptoStore`.
///
/// The crypto store should be opened before being set.
#[cfg(feature = "encryption")]
#[must_use]
pub fn crypto_store(
mut self,
store: Box<dyn matrix_sdk_base::crypto::store::CryptoStore>,
) -> Self {
self.store_config = self.store_config.crypto_store(store);
self
}
/// Update the client's homeserver URL with the discovery information
/// present in the login response, if any.
#[must_use]
pub fn use_discovery_response(mut self) -> Self {
self.use_discovery_response = true;
self
}
/// All outgoing http requests will have a GET query key-value appended with
/// `user_id` being the key and the `user_id` from the `Session` being
/// the value. Will error if there's no `Session`. This is called
/// [identity assertion] in the Matrix Application Service Spec
///
/// [identity assertion]: https://spec.matrix.org/unstable/application-service-api/#identity-assertion
#[cfg(feature = "appservice")]
#[must_use]
pub fn assert_identity(mut self) -> Self {
self.request_config.assert_identity = true;
self
}
}

View File

@@ -14,11 +14,9 @@
//! Configuration to change the behaviour of the [`Client`][crate::Client].
mod client;
mod request;
mod sync;
pub use client::ClientConfig;
pub use matrix_sdk_base::store::StoreConfig;
pub use request::RequestConfig;
pub use sync::SyncSettings;

View File

@@ -17,7 +17,7 @@ use std::{
time::Duration,
};
const DEFAULT_REQUEST_TIMEOUT: Duration = Duration::from_secs(10);
use crate::http_client::DEFAULT_REQUEST_TIMEOUT;
/// Configuration for requests the `Client` makes.
///

View File

@@ -168,7 +168,7 @@ to work.
2. To persist the encryption keys, you can use one of the provided backend
constructors as described in the documentation of the [`store`] module or you
can provide your own backend that implements [`CryptoStore`] in a
[`StoreConfig`] or via [`ClientConfig::crypto_store()`].
[`StoreConfig`] or via [`ClientBuilder::crypto_store()`].
## Restoring a client
@@ -233,4 +233,4 @@ is **not** supported using the default store.
[`store`]: crate::store
[`CryptoStore`]: matrix_sdk_base::crypto::store::CryptoStore
[`StoreConfig`]: crate::config::StoreConfig
[`ClientConfig::crypto_store()`]: crate::config::ClientConfig::crypto_store()
[`ClientBuilder::crypto_store()`]: crate::ClientBuilder::crypto_store()

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::{convert::TryFrom, fmt::Debug, sync::Arc};
use std::{convert::TryFrom, fmt::Debug, sync::Arc, time::Duration};
use bytes::{Bytes, BytesMut};
use http::Response as HttpResponse;
@@ -25,11 +25,9 @@ use ruma::api::{
use tracing::trace;
use url::Url;
use crate::{
config::{ClientConfig, RequestConfig},
error::HttpError,
Session,
};
use crate::{config::RequestConfig, error::HttpError, Session};
pub(crate) const DEFAULT_REQUEST_TIMEOUT: Duration = Duration::from_secs(10);
/// Abstraction around the http layer. The allows implementors to use different
/// http libraries.
@@ -203,33 +201,58 @@ impl HttpClient {
}
}
/// Build a client with the specified configuration.
pub(crate) fn client_with_config(config: &ClientConfig) -> Result<Client, HttpError> {
let http_client = reqwest::Client::builder();
#[derive(Debug)]
pub(crate) struct HttpSettings {
#[cfg(not(target_arch = "wasm32"))]
let http_client = {
let http_client = if config.disable_ssl_verification {
http_client.danger_accept_invalid_certs(true)
} else {
http_client
pub(crate) disable_ssl_verification: bool,
#[cfg(not(target_arch = "wasm32"))]
pub(crate) proxy: Option<String>,
#[cfg(not(target_arch = "wasm32"))]
pub(crate) user_agent: Option<String>,
#[cfg(not(target_arch = "wasm32"))]
pub(crate) timeout: Duration,
}
#[allow(clippy::derivable_impls)]
impl Default for HttpSettings {
fn default() -> Self {
Self {
#[cfg(not(target_arch = "wasm32"))]
disable_ssl_verification: false,
#[cfg(not(target_arch = "wasm32"))]
proxy: None,
#[cfg(not(target_arch = "wasm32"))]
user_agent: None,
#[cfg(not(target_arch = "wasm32"))]
timeout: DEFAULT_REQUEST_TIMEOUT,
}
}
}
impl HttpSettings {
/// Build a client with the specified configuration.
pub(crate) fn make_client(&self) -> Result<Client, HttpError> {
#[allow(unused_mut)]
let mut http_client = reqwest::Client::builder();
#[cfg(not(target_arch = "wasm32"))]
{
if self.disable_ssl_verification {
http_client = http_client.danger_accept_invalid_certs(true)
}
if let Some(p) = &self.proxy {
http_client = http_client.proxy(reqwest::Proxy::all(p.as_str())?);
}
let user_agent =
self.user_agent.clone().unwrap_or_else(|| "matrix-rust-sdk".to_owned());
http_client = http_client.user_agent(user_agent).timeout(self.timeout);
};
let http_client = match &config.proxy {
Some(p) => http_client.proxy(p.clone()),
None => http_client,
};
let user_agent = config.user_agent.clone().unwrap_or_else(|| "matrix-rust-sdk".to_owned());
http_client.user_agent(user_agent).timeout(config.request_config.timeout)
};
#[cfg(target_arch = "wasm32")]
#[allow(unused)]
let _ = config;
Ok(http_client.build()?)
Ok(http_client.build()?)
}
}
async fn response_to_http_response(

View File

@@ -57,7 +57,7 @@ mod sync;
pub mod encryption;
pub use account::Account;
pub use client::{Client, LoopCtrl};
pub use client::{Client, ClientBuildError, ClientBuilder, LoopCtrl};
#[cfg(feature = "image_proc")]
pub use error::ImageError;
pub use error::{Error, HttpError, HttpResult, Result};

View File

@@ -24,10 +24,10 @@
//! implementation for WebAssembly.
//!
//! Both options provide a `make_store_config` convenience method to create a
//! [`StoreConfig`] for [`ClientConfig::store_config()`].
//! [`StoreConfig`] for [`ClientBuilder::store_config()`].
//!
//! [`StoreConfig`]: crate::config::StoreConfig
//! [`ClientConfig::store_config()`]: crate::config::ClientConfig::store_config()
//! [`ClientBuilder::store_config()`]: crate::ClientBuilder::store_config
#[cfg(any(feature = "indexeddb_state_store", feature = "indexeddb_cryptostore"))]
pub use matrix_sdk_indexeddb::*;