matrix-sdk: Split out the http errors into a sub-enum

master
Damir Jelić 2021-01-31 18:09:03 +01:00
parent b66c666997
commit 585ca9fdf7
4 changed files with 70 additions and 61 deletions

View File

@ -118,6 +118,7 @@ use matrix_sdk_common::{
}; };
use crate::{ use crate::{
error::HttpError,
http_client::{client_with_config, HttpClient, HttpSend}, http_client::{client_with_config, HttpClient, HttpSend},
Error, OutgoingRequest, Result, Error, OutgoingRequest, Result,
}; };
@ -1451,7 +1452,7 @@ impl Client {
content_type: Some(content_type.essence_str()), content_type: Some(content_type.essence_str()),
}); });
self.http_client.upload(request).await Ok(self.http_client.upload(request).await?)
} }
/// Send an arbitrary request to the server, without updating client state. /// Send an arbitrary request to the server, without updating client state.
@ -1494,9 +1495,9 @@ impl Client {
pub async fn send<Request>(&self, request: Request) -> Result<Request::IncomingResponse> pub async fn send<Request>(&self, request: Request) -> Result<Request::IncomingResponse>
where where
Request: OutgoingRequest + Debug, Request: OutgoingRequest + Debug,
Error: From<FromHttpResponseError<Request::EndpointError>>, HttpError: From<FromHttpResponseError<Request::EndpointError>>,
{ {
self.http_client.send(request).await Ok(self.http_client.send(request).await?)
} }
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
@ -2276,7 +2277,7 @@ impl Client {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::ClientConfig; use crate::{ClientConfig, HttpError};
use super::{ use super::{
get_public_rooms, get_public_rooms_filtered, register::RegistrationKind, Client, get_public_rooms, get_public_rooms_filtered, register::RegistrationKind, Client,
@ -2471,12 +2472,12 @@ mod test {
.create(); .create();
if let Err(err) = client.login("example", "wordpass", None, None).await { if let Err(err) = client.login("example", "wordpass", None, None).await {
if let crate::Error::RumaResponse(crate::FromHttpResponseError::Http( if let crate::Error::Http(HttpError::FromHttpResponse(
crate::ServerError::Known(crate::api::Error { crate::FromHttpResponseError::Http(crate::ServerError::Known(crate::api::Error {
kind, kind,
message, message,
status_code, status_code,
}), })),
)) = err )) = err
{ {
if let crate::api::error::ErrorKind::Forbidden = kind { if let crate::api::error::ErrorKind::Forbidden = kind {
@ -2517,10 +2518,10 @@ mod test {
}); });
if let Err(err) = client.register(user).await { if let Err(err) = client.register(user).await {
if let crate::Error::UiaaError(crate::FromHttpResponseError::Http( if let crate::Error::Http(HttpError::UiaaError(crate::FromHttpResponseError::Http(
// TODO this should be a UiaaError need to investigate // TODO this should be a UiaaError need to investigate
crate::ServerError::Unknown(e), crate::ServerError::Unknown(e),
)) = err ))) = err
{ {
assert!(e.to_string().starts_with("EOF while parsing")) assert!(e.to_string().starts_with("EOF while parsing"))
} else { } else {

View File

@ -20,7 +20,7 @@ use matrix_sdk_common::{
r0::uiaa::{UiaaInfo, UiaaResponse as UiaaError}, r0::uiaa::{UiaaInfo, UiaaResponse as UiaaError},
Error as RumaClientError, Error as RumaClientError,
}, },
FromHttpResponseError as RumaResponseError, IntoHttpError as RumaIntoHttpError, ServerError, FromHttpResponseError, IntoHttpError, ServerError,
}; };
use reqwest::Error as ReqwestError; use reqwest::Error as ReqwestError;
use serde_json::Error as JsonError; use serde_json::Error as JsonError;
@ -33,9 +33,14 @@ use matrix_sdk_base::crypto::store::CryptoStoreError;
/// 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>;
/// Internal representation of errors. /// An HTTP error, representing either a connection error or an error while
/// converting the raw HTTP response into a Matrix response.
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum Error { pub enum HttpError {
/// An error at the HTTP layer.
#[error(transparent)]
Reqwest(#[from] ReqwestError),
/// Queried endpoint requires authentication but was called on an anonymous client. /// Queried endpoint requires authentication but was called on an anonymous client.
#[error("the queried endpoint requires authentication but was called before logging in")] #[error("the queried endpoint requires authentication but was called before logging in")]
AuthenticationRequired, AuthenticationRequired,
@ -44,9 +49,33 @@ pub enum Error {
#[error("the queried endpoint is not meant for clients")] #[error("the queried endpoint is not meant for clients")]
NotClientRequest, NotClientRequest,
/// An error at the HTTP layer. /// An error converting between ruma_client_api types and Hyper types.
#[error(transparent)] #[error(transparent)]
Reqwest(#[from] ReqwestError), FromHttpResponse(#[from] FromHttpResponseError<RumaClientError>),
/// An error converting between ruma_client_api types and Hyper types.
#[error(transparent)]
IntoHttp(#[from] IntoHttpError),
/// An error occurred while authenticating.
///
/// When registering or authenticating the Matrix server can send a `UiaaResponse`
/// as the error type, this is a User-Interactive Authentication API response. This
/// represents an error with information about how to authenticate the user.
#[error(transparent)]
UiaaError(#[from] FromHttpResponseError<UiaaError>),
}
/// Internal representation of errors.
#[derive(Error, Debug)]
pub enum Error {
/// Error doing an HTTP request.
#[error(transparent)]
Http(#[from] HttpError),
/// Queried endpoint requires authentication but was called on an anonymous client.
#[error("the queried endpoint requires authentication but was called before logging in")]
AuthenticationRequired,
/// An error de/serializing type for the `StateStore` /// An error de/serializing type for the `StateStore`
#[error(transparent)] #[error(transparent)]
@ -56,14 +85,6 @@ pub enum Error {
#[error(transparent)] #[error(transparent)]
IO(#[from] IoError), IO(#[from] IoError),
/// An error converting between ruma_client_api types and Hyper types.
#[error("can't parse the JSON response as a Matrix response")]
RumaResponse(RumaResponseError<RumaClientError>),
/// An error converting between ruma_client_api types and Hyper types.
#[error("can't convert between ruma_client_api and hyper types.")]
IntoHttp(RumaIntoHttpError),
/// An error occurred in the Matrix client library. /// An error occurred in the Matrix client library.
#[error(transparent)] #[error(transparent)]
MatrixError(#[from] MatrixError), MatrixError(#[from] MatrixError),
@ -76,14 +97,6 @@ pub enum Error {
/// An error occured in the state store. /// An error occured in the state store.
#[error(transparent)] #[error(transparent)]
StateStore(#[from] StoreError), StateStore(#[from] StoreError),
/// An error occurred while authenticating.
///
/// When registering or authenticating the Matrix server can send a `UiaaResponse`
/// as the error type, this is a User-Interactive Authentication API response. This
/// represents an error with information about how to authenticate the user.
#[error("User-Interactive Authentication required.")]
UiaaError(RumaResponseError<UiaaError>),
} }
impl Error { impl Error {
@ -99,9 +112,9 @@ impl Error {
/// This method is an convenience method to get to the info the server /// This method is an convenience method to get to the info the server
/// returned on the first, failed request. /// returned on the first, failed request.
pub fn uiaa_response(&self) -> Option<&UiaaInfo> { pub fn uiaa_response(&self) -> Option<&UiaaInfo> {
if let Error::UiaaError(RumaResponseError::Http(ServerError::Known( if let Error::Http(HttpError::UiaaError(FromHttpResponseError::Http(ServerError::Known(
UiaaError::AuthResponse(i), UiaaError::AuthResponse(i),
))) = self )))) = self
{ {
Some(i) Some(i)
} else { } else {
@ -110,20 +123,8 @@ impl Error {
} }
} }
impl From<RumaResponseError<UiaaError>> for Error { impl From<ReqwestError> for Error {
fn from(error: RumaResponseError<UiaaError>) -> Self { fn from(e: ReqwestError) -> Self {
Self::UiaaError(error) Error::Http(HttpError::Reqwest(e))
}
}
impl From<RumaResponseError<RumaClientError>> for Error {
fn from(error: RumaResponseError<RumaClientError>) -> Self {
Self::RumaResponse(error)
}
}
impl From<RumaIntoHttpError> for Error {
fn from(error: RumaIntoHttpError) -> Self {
Self::IntoHttp(error)
} }
} }

View File

@ -24,7 +24,7 @@ use matrix_sdk_common::{
FromHttpResponseError, FromHttpResponseError,
}; };
use crate::{ClientConfig, Error, OutgoingRequest, Result, Session}; use crate::{error::HttpError, ClientConfig, OutgoingRequest, Session};
/// Abstraction around the http layer. The allows implementors to use different /// Abstraction around the http layer. The allows implementors to use different
/// http libraries. /// http libraries.
@ -74,7 +74,7 @@ pub trait HttpSend: AsyncTraitDeps {
async fn send_request( async fn send_request(
&self, &self,
request: http::Request<Vec<u8>>, request: http::Request<Vec<u8>>,
) -> Result<http::Response<Vec<u8>>>; ) -> Result<http::Response<Vec<u8>>, HttpError>;
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -90,7 +90,7 @@ impl HttpClient {
request: Request, request: Request,
session: Arc<RwLock<Option<Session>>>, session: Arc<RwLock<Option<Session>>>,
content_type: Option<HeaderValue>, content_type: Option<HeaderValue>,
) -> Result<http::Response<Vec<u8>>> { ) -> Result<http::Response<Vec<u8>>, HttpError> {
let mut request = { let mut request = {
let read_guard; let read_guard;
let access_token = match Request::METADATA.authentication { let access_token = match Request::METADATA.authentication {
@ -100,11 +100,11 @@ impl HttpClient {
if let Some(session) = read_guard.as_ref() { if let Some(session) = read_guard.as_ref() {
Some(session.access_token.as_str()) Some(session.access_token.as_str())
} else { } else {
return Err(Error::AuthenticationRequired); return Err(HttpError::AuthenticationRequired);
} }
} }
AuthScheme::None => None, AuthScheme::None => None,
_ => return Err(Error::NotClientRequest), _ => return Err(HttpError::NotClientRequest),
}; };
request.try_into_http_request(&self.homeserver.to_string(), access_token)? request.try_into_http_request(&self.homeserver.to_string(), access_token)?
@ -124,17 +124,20 @@ impl HttpClient {
pub async fn upload( pub async fn upload(
&self, &self,
request: create_content::Request<'_>, request: create_content::Request<'_>,
) -> Result<create_content::Response> { ) -> Result<create_content::Response, HttpError> {
let response = self let response = self
.send_request(request, self.session.clone(), None) .send_request(request, self.session.clone(), None)
.await?; .await?;
Ok(create_content::Response::try_from(response)?) Ok(create_content::Response::try_from(response)?)
} }
pub async fn send<Request>(&self, request: Request) -> Result<Request::IncomingResponse> pub async fn send<Request>(
&self,
request: Request,
) -> Result<Request::IncomingResponse, HttpError>
where where
Request: OutgoingRequest, Request: OutgoingRequest + Debug,
Error: From<FromHttpResponseError<Request::EndpointError>>, HttpError: From<FromHttpResponseError<Request::EndpointError>>,
{ {
let content_type = HeaderValue::from_static("application/json"); let content_type = HeaderValue::from_static("application/json");
let response = self let response = self
@ -143,12 +146,14 @@ impl HttpClient {
trace!("Got response: {:?}", response); trace!("Got response: {:?}", response);
Ok(Request::IncomingResponse::try_from(response)?) let response = Request::IncomingResponse::try_from(response)?;
Ok(response)
} }
} }
/// Build a client with the specified configuration. /// Build a client with the specified configuration.
pub(crate) fn client_with_config(config: &ClientConfig) -> Result<Client> { pub(crate) fn client_with_config(config: &ClientConfig) -> Result<Client, HttpError> {
let http_client = reqwest::Client::builder(); let http_client = reqwest::Client::builder();
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -188,7 +193,9 @@ pub(crate) fn client_with_config(config: &ClientConfig) -> Result<Client> {
Ok(http_client.build()?) Ok(http_client.build()?)
} }
async fn response_to_http_response(mut response: Response) -> Result<http::Response<Vec<u8>>> { async fn response_to_http_response(
mut response: Response,
) -> Result<http::Response<Vec<u8>>, reqwest::Error> {
let status = response.status(); let status = response.status();
let mut http_builder = HttpResponse::builder().status(status); let mut http_builder = HttpResponse::builder().status(status);
@ -211,7 +218,7 @@ impl HttpSend for Client {
async fn send_request( async fn send_request(
&self, &self,
request: http::Request<Vec<u8>>, request: http::Request<Vec<u8>>,
) -> Result<http::Response<Vec<u8>>> { ) -> Result<http::Response<Vec<u8>>, HttpError> {
Ok( Ok(
response_to_http_response(self.execute(reqwest::Request::try_from(request)?).await?) response_to_http_response(self.execute(reqwest::Request::try_from(request)?).await?)
.await?, .await?,

View File

@ -90,7 +90,7 @@ pub use client::{Client, ClientConfig, LoopCtrl, 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, Result}; pub use error::{Error, HttpError, Result};
pub use http_client::HttpSend; pub use http_client::HttpSend;
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
#[cfg_attr(feature = "docs", doc(cfg(encryption)))] #[cfg_attr(feature = "docs", doc(cfg(encryption)))]