fix(sdk): Use a pure HTTP error for methods that don't touch local state

This commit is contained in:
Damir Jelić 2021-09-09 10:30:46 +02:00
parent afc8597d3b
commit 6e4a57046e
7 changed files with 65 additions and 34 deletions

View file

@ -145,7 +145,7 @@ use crate::{
verification::{QrVerification, SasVerification, Verification, VerificationRequest}, verification::{QrVerification, SasVerification, Verification, VerificationRequest},
}; };
use crate::{ use crate::{
error::HttpError, error::{HttpError, HttpResult},
event_handler::{EventHandler, EventHandlerData, EventHandlerResult, EventKind, SyncEvent}, event_handler::{EventHandler, EventHandlerData, EventHandlerResult, EventKind, SyncEvent},
http_client::{client_with_config, HttpClient, HttpSend}, http_client::{client_with_config, HttpClient, HttpSend},
room, Error, Result, room, Error, Result,
@ -645,7 +645,7 @@ impl Client {
Ok(result) Ok(result)
} }
async fn discover_homeserver(&self) -> Result<discover_homeserver::Response> { async fn discover_homeserver(&self) -> HttpResult<discover_homeserver::Response> {
self.send(discover_homeserver::Request::new(), Some(RequestConfig::new().disable_retry())) self.send(discover_homeserver::Request::new(), Some(RequestConfig::new().disable_retry()))
.await .await
} }
@ -660,7 +660,7 @@ impl Client {
*homeserver = homeserver_url; *homeserver = homeserver_url;
} }
async fn get_supported_versions(&self) -> Result<get_supported_versions::Response> { async fn get_supported_versions(&self) -> HttpResult<get_supported_versions::Response> {
self.send( self.send(
get_supported_versions::Request::new(), get_supported_versions::Request::new(),
Some(RequestConfig::new().disable_retry()), Some(RequestConfig::new().disable_retry()),
@ -1123,7 +1123,7 @@ impl Client {
/// ///
/// This should be the first step when trying to login so you can call the /// This should be the first step when trying to login so you can call the
/// appropriate method for the next step. /// appropriate method for the next step.
pub async fn get_login_types(&self) -> Result<get_login_types::Response> { pub async fn get_login_types(&self) -> HttpResult<get_login_types::Response> {
let request = get_login_types::Request::new(); let request = get_login_types::Request::new();
self.send(request, None).await self.send(request, None).await
} }
@ -1540,7 +1540,7 @@ impl Client {
pub async fn register( pub async fn register(
&self, &self,
registration: impl Into<register::Request<'_>>, registration: impl Into<register::Request<'_>>,
) -> Result<register::Response> { ) -> HttpResult<register::Response> {
info!("Registering to {}", self.homeserver().await); info!("Registering to {}", self.homeserver().await);
let config = if self.appservice_mode { let config = if self.appservice_mode {
@ -1630,7 +1630,7 @@ impl Client {
/// # Arguments /// # Arguments
/// ///
/// * `room_id` - The `RoomId` of the room to be joined. /// * `room_id` - The `RoomId` of the room to be joined.
pub async fn join_room_by_id(&self, room_id: &RoomId) -> Result<join_room_by_id::Response> { pub async fn join_room_by_id(&self, room_id: &RoomId) -> HttpResult<join_room_by_id::Response> {
let request = join_room_by_id::Request::new(room_id); let request = join_room_by_id::Request::new(room_id);
self.send(request, None).await self.send(request, None).await
} }
@ -1648,7 +1648,7 @@ impl Client {
&self, &self,
alias: &RoomIdOrAliasId, alias: &RoomIdOrAliasId,
server_names: &[Box<ServerName>], server_names: &[Box<ServerName>],
) -> Result<join_room_by_id_or_alias::Response> { ) -> HttpResult<join_room_by_id_or_alias::Response> {
let request = assign!(join_room_by_id_or_alias::Request::new(alias), { let request = assign!(join_room_by_id_or_alias::Request::new(alias), {
server_name: server_names, server_name: server_names,
}); });
@ -1691,7 +1691,7 @@ impl Client {
limit: Option<u32>, limit: Option<u32>,
since: Option<&str>, since: Option<&str>,
server: Option<&ServerName>, server: Option<&ServerName>,
) -> Result<get_public_rooms::Response> { ) -> HttpResult<get_public_rooms::Response> {
let limit = limit.map(UInt::from); let limit = limit.map(UInt::from);
let request = assign!(get_public_rooms::Request::new(), { let request = assign!(get_public_rooms::Request::new(), {
@ -1732,7 +1732,7 @@ impl Client {
pub async fn create_room( pub async fn create_room(
&self, &self,
room: impl Into<create_room::Request<'_>>, room: impl Into<create_room::Request<'_>>,
) -> Result<create_room::Response> { ) -> HttpResult<create_room::Response> {
let request = room.into(); let request = room.into();
self.send(request, None).await self.send(request, None).await
} }
@ -1772,7 +1772,7 @@ impl Client {
pub async fn public_rooms_filtered( pub async fn public_rooms_filtered(
&self, &self,
room_search: impl Into<get_public_rooms_filtered::Request<'_>>, room_search: impl Into<get_public_rooms_filtered::Request<'_>>,
) -> Result<get_public_rooms_filtered::Response> { ) -> HttpResult<get_public_rooms_filtered::Response> {
let request = room_search.into(); let request = room_search.into();
self.send(request, None).await self.send(request, None).await
} }
@ -1906,7 +1906,7 @@ impl Client {
let txn_id = txn_id.unwrap_or_else(Uuid::new_v4).to_string(); let txn_id = txn_id.unwrap_or_else(Uuid::new_v4).to_string();
let request = send_message_event::Request::new(room_id, &txn_id, &content); let request = send_message_event::Request::new(room_id, &txn_id, &content);
self.send(request, None).await Ok(self.send(request, None).await?)
} }
} }
@ -1954,7 +1954,7 @@ impl Client {
&self, &self,
request: Request, request: Request,
config: Option<RequestConfig>, config: Option<RequestConfig>,
) -> Result<Request::IncomingResponse> ) -> HttpResult<Request::IncomingResponse>
where where
Request: OutgoingRequest + Debug, Request: OutgoingRequest + Debug,
HttpError: From<FromHttpResponseError<Request::EndpointError>>, HttpError: From<FromHttpResponseError<Request::EndpointError>>,
@ -1966,8 +1966,9 @@ impl Client {
pub(crate) async fn send_to_device( pub(crate) async fn send_to_device(
&self, &self,
request: &ToDeviceRequest, request: &ToDeviceRequest,
) -> Result<ToDeviceResponse> { ) -> HttpResult<ToDeviceResponse> {
let txn_id_string = request.txn_id_string(); let txn_id_string = request.txn_id_string();
let request = RumaToDeviceRequest::new_raw( let request = RumaToDeviceRequest::new_raw(
request.event_type.as_str(), request.event_type.as_str(),
&txn_id_string, &txn_id_string,
@ -2000,7 +2001,7 @@ impl Client {
/// } /// }
/// # }); /// # });
/// ``` /// ```
pub async fn devices(&self) -> Result<get_devices::Response> { pub async fn devices(&self) -> HttpResult<get_devices::Response> {
let request = get_devices::Request::new(); let request = get_devices::Request::new();
self.send(request, None).await self.send(request, None).await
@ -2057,7 +2058,7 @@ impl Client {
&self, &self,
devices: &[DeviceIdBox], devices: &[DeviceIdBox],
auth_data: Option<AuthData<'_>>, auth_data: Option<AuthData<'_>>,
) -> Result<delete_devices::Response> { ) -> HttpResult<delete_devices::Response> {
let mut request = delete_devices::Request::new(devices); let mut request = delete_devices::Request::new(devices);
request.auth = auth_data; request.auth = auth_data;
@ -2965,7 +2966,7 @@ impl Client {
} }
/// Gets information about the owner of a given access token. /// Gets information about the owner of a given access token.
pub async fn whoami(&self) -> Result<whoami::Response> { pub async fn whoami(&self) -> HttpResult<whoami::Response> {
let request = whoami::Request::new(); let request = whoami::Request::new();
self.send(request, None).await self.send(request, None).await
} }
@ -3414,12 +3415,8 @@ mod test {
}); });
if let Err(err) = client.register(user).await { if let Err(err) = client.register(user).await {
if let crate::Error::Http(HttpError::UiaaError(FromHttpResponseError::Http( if let HttpError::UiaaError(FromHttpResponseError::Http(ServerError::Known(
ServerError::Known(UiaaResponse::MatrixError(client_api::Error { UiaaResponse::MatrixError(client_api::Error { kind, message, status_code }),
kind,
message,
status_code,
})),
))) = err ))) = err
{ {
if let client_api::error::ErrorKind::Forbidden = kind { if let client_api::error::ErrorKind::Forbidden = kind {

View file

@ -40,6 +40,9 @@ use url::ParseError as UrlParseError;
/// Result type of the rust-sdk. /// Result type of the rust-sdk.
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
/// Result type of a pure HTTP request.
pub type HttpResult<T> = std::result::Result<T, HttpError>;
/// An HTTP error, representing either a connection error or an error while /// An HTTP error, representing either a connection error or an error while
/// converting the raw HTTP response into a Matrix response. /// converting the raw HTTP response into a Matrix response.
#[derive(Error, Debug)] #[derive(Error, Debug)]
@ -182,6 +185,30 @@ pub enum RoomKeyImportError {
Export(#[from] KeyExportError), Export(#[from] KeyExportError),
} }
impl HttpError {
/// Try to destructure the error into an universal interactive auth info.
///
/// Some requests require universal interactive auth, doing such a request
/// will always fail the first time with a 401 status code, the response
/// body will contain info how the client can authenticate.
///
/// The request will need to be retried, this time containing additional
/// authentication data.
///
/// This method is an convenience method to get to the info the server
/// returned on the first, failed request.
pub fn uiaa_response(&self) -> Option<&UiaaInfo> {
if let HttpError::UiaaError(FromHttpResponseError::Http(ServerError::Known(
UiaaError::AuthResponse(i),
))) = self
{
Some(i)
} else {
None
}
}
}
impl Error { impl Error {
/// Try to destructure the error into an universal interactive auth info. /// Try to destructure the error into an universal interactive auth info.
/// ///

View file

@ -107,7 +107,7 @@ pub use client::{Client, ClientConfig, LoopCtrl, RequestConfig, SyncSettings};
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
#[cfg_attr(feature = "docs", doc(cfg(encryption)))] #[cfg_attr(feature = "docs", doc(cfg(encryption)))]
pub use device::Device; pub use device::Device;
pub use error::{Error, HttpError, Result}; pub use error::{Error, HttpError, HttpResult, Result};
pub use http_client::HttpSend; pub use http_client::HttpSend;
pub use room_member::RoomMember; pub use room_member::RoomMember;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]

View file

@ -14,6 +14,7 @@ use ruma::{
}; };
use crate::{ use crate::{
error::HttpResult,
media::{MediaFormat, MediaRequest, MediaType}, media::{MediaFormat, MediaRequest, MediaType},
room::RoomType, room::RoomType,
BaseRoom, Client, Result, RoomMember, BaseRoom, Client, Result, RoomMember,
@ -143,7 +144,7 @@ impl Common {
pub async fn messages( pub async fn messages(
&self, &self,
request: impl Into<get_message_events::Request<'_>>, request: impl Into<get_message_events::Request<'_>>,
) -> Result<get_message_events::Response> { ) -> HttpResult<get_message_events::Response> {
let request = request.into(); let request = request.into();
self.client.send(request, None).await self.client.send(request, None).await
} }

View file

@ -46,7 +46,7 @@ use ruma::{
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
use tracing::instrument; use tracing::instrument;
use crate::{room::Common, BaseRoom, Client, Error, Result, RoomType}; use crate::{error::HttpResult, room::Common, BaseRoom, Client, HttpError, Result, RoomType};
const TYPING_NOTICE_TIMEOUT: Duration = Duration::from_secs(4); const TYPING_NOTICE_TIMEOUT: Duration = Duration::from_secs(4);
const TYPING_NOTICE_RESEND_TIMEOUT: Duration = Duration::from_secs(3); const TYPING_NOTICE_RESEND_TIMEOUT: Duration = Duration::from_secs(3);
@ -562,7 +562,7 @@ impl Joined {
&self, &self,
content: impl Into<AnyStateEventContent>, content: impl Into<AnyStateEventContent>,
state_key: &str, state_key: &str,
) -> Result<send_state_event::Response> { ) -> HttpResult<send_state_event::Response> {
let content = content.into(); let content = content.into();
let request = send_state_event::Request::new(self.inner.room_id(), state_key, &content); let request = send_state_event::Request::new(self.inner.room_id(), state_key, &content);
@ -606,7 +606,7 @@ impl Joined {
event_id: &EventId, event_id: &EventId,
reason: Option<&str>, reason: Option<&str>,
txn_id: Option<Uuid>, txn_id: Option<Uuid>,
) -> Result<redact_event::Response> { ) -> HttpResult<redact_event::Response> {
let txn_id = txn_id.unwrap_or_else(Uuid::new_v4).to_string(); let txn_id = txn_id.unwrap_or_else(Uuid::new_v4).to_string();
let request = let request =
assign!(redact_event::Request::new(self.inner.room_id(), event_id, &txn_id), { assign!(redact_event::Request::new(self.inner.room_id(), event_id, &txn_id), {
@ -642,8 +642,8 @@ impl Joined {
/// room.set_tag("u.work", tag_info ); /// room.set_tag("u.work", tag_info );
/// # }) /// # })
/// ``` /// ```
pub async fn set_tag(&self, tag: &str, tag_info: TagInfo) -> Result<create_tag::Response> { pub async fn set_tag(&self, tag: &str, tag_info: TagInfo) -> HttpResult<create_tag::Response> {
let user_id = self.client.user_id().await.ok_or(Error::AuthenticationRequired)?; let user_id = self.client.user_id().await.ok_or(HttpError::AuthenticationRequired)?;
let request = create_tag::Request::new(&user_id, self.inner.room_id(), tag, tag_info); let request = create_tag::Request::new(&user_id, self.inner.room_id(), tag, tag_info);
self.client.send(request, None).await self.client.send(request, None).await
} }
@ -654,8 +654,8 @@ impl Joined {
/// ///
/// # Arguments /// # Arguments
/// * `tag` - The tag to remove. /// * `tag` - The tag to remove.
pub async fn remove_tag(&self, tag: &str) -> Result<delete_tag::Response> { pub async fn remove_tag(&self, tag: &str) -> HttpResult<delete_tag::Response> {
let user_id = self.client.user_id().await.ok_or(Error::AuthenticationRequired)?; let user_id = self.client.user_id().await.ok_or(HttpError::AuthenticationRequired)?;
let request = delete_tag::Request::new(&user_id, self.inner.room_id(), tag); let request = delete_tag::Request::new(&user_id, self.inner.room_id(), tag);
self.client.send(request, None).await self.client.send(request, None).await
} }

View file

@ -87,3 +87,9 @@ impl From<warp::Rejection> for Error {
Self::WarpRejection(format!("{:?}", rejection)) Self::WarpRejection(format!("{:?}", rejection))
} }
} }
impl From<matrix_sdk::HttpError> for Error {
fn from(e: matrix_sdk::HttpError) -> Self {
matrix_sdk::Error::from(e).into()
}
}

View file

@ -94,7 +94,7 @@ use matrix_sdk::{
bytes::Bytes, bytes::Bytes,
event_handler::{EventHandler, EventHandlerResult, SyncEvent}, event_handler::{EventHandler, EventHandlerResult, SyncEvent},
reqwest::Url, reqwest::Url,
Client, ClientConfig, HttpError, Session, Client, ClientConfig, Session,
}; };
use regex::Regex; use regex::Regex;
use ruma::{ use ruma::{
@ -402,9 +402,9 @@ impl AppService {
match client.register(request).await { match client.register(request).await {
Ok(_) => (), Ok(_) => (),
Err(error) => match error { Err(error) => match error {
matrix_sdk::Error::Http(HttpError::UiaaError(FromHttpResponseError::Http( matrix_sdk::HttpError::UiaaError(FromHttpResponseError::Http(
ServerError::Known(UiaaResponse::MatrixError(ref matrix_error)), ServerError::Known(UiaaResponse::MatrixError(ref matrix_error)),
))) => { )) => {
match matrix_error.kind { match matrix_error.kind {
ErrorKind::UserInUse => { ErrorKind::UserInUse => {
// TODO: persist the fact that we registered that user // TODO: persist the fact that we registered that user