client: Add HttpClientExt
This commit is contained in:
parent
e94e2e7b2b
commit
c7742085a8
@ -1,6 +1,5 @@
|
|||||||
use std::{convert::TryFrom, env, process::exit};
|
use std::{convert::TryFrom, env, process::exit};
|
||||||
|
|
||||||
use http::Uri;
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::r0::{alias::get_alias, membership::join_room_by_id, message::send_message_event},
|
api::client::r0::{alias::get_alias, membership::join_room_by_id, message::send_message_event},
|
||||||
events::{room::message::MessageEventContent, AnyMessageEventContent},
|
events::{room::message::MessageEventContent, AnyMessageEventContent},
|
||||||
@ -10,7 +9,7 @@ use ruma::{
|
|||||||
type MatrixClient = ruma_client::Client<ruma_client::http_client::HyperNativeTls>;
|
type MatrixClient = ruma_client::Client<ruma_client::http_client::HyperNativeTls>;
|
||||||
|
|
||||||
async fn hello_world(
|
async fn hello_world(
|
||||||
homeserver_url: Uri,
|
homeserver_url: String,
|
||||||
username: &str,
|
username: &str,
|
||||||
password: &str,
|
password: &str,
|
||||||
room_alias: &RoomAliasId,
|
room_alias: &RoomAliasId,
|
||||||
@ -18,10 +17,10 @@ async fn hello_world(
|
|||||||
let client = MatrixClient::new(homeserver_url, None);
|
let client = MatrixClient::new(homeserver_url, None);
|
||||||
client.log_in(username, password, None, Some("ruma-example-client")).await?;
|
client.log_in(username, password, None, Some("ruma-example-client")).await?;
|
||||||
|
|
||||||
let room_id = client.request(get_alias::Request::new(room_alias)).await?.room_id;
|
let room_id = client.send_request(get_alias::Request::new(room_alias)).await?.room_id;
|
||||||
client.request(join_room_by_id::Request::new(&room_id)).await?;
|
client.send_request(join_room_by_id::Request::new(&room_id)).await?;
|
||||||
client
|
client
|
||||||
.request(send_message_event::Request::new(
|
.send_request(send_message_event::Request::new(
|
||||||
&room_id,
|
&room_id,
|
||||||
"1",
|
"1",
|
||||||
&AnyMessageEventContent::RoomMessage(MessageEventContent::text_plain("Hello World!")),
|
&AnyMessageEventContent::RoomMessage(MessageEventContent::text_plain("Hello World!")),
|
||||||
@ -45,11 +44,5 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
hello_world(
|
hello_world(homeserver_url, &username, &password, &RoomAliasId::try_from(room.as_str())?).await
|
||||||
homeserver_url.parse()?,
|
|
||||||
&username,
|
|
||||||
&password,
|
|
||||||
&RoomAliasId::try_from(room.as_str())?,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use std::{env, process::exit, time::Duration};
|
use std::{env, process::exit, time::Duration};
|
||||||
|
|
||||||
use assign::assign;
|
use assign::assign;
|
||||||
use http::Uri;
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::r0::{filter::FilterDefinition, sync::sync_events},
|
api::client::r0::{filter::FilterDefinition, sync::sync_events},
|
||||||
events::{
|
events::{
|
||||||
@ -14,14 +13,18 @@ use tokio_stream::StreamExt as _;
|
|||||||
|
|
||||||
type MatrixClient = ruma_client::Client<ruma_client::http_client::HyperNativeTls>;
|
type MatrixClient = ruma_client::Client<ruma_client::http_client::HyperNativeTls>;
|
||||||
|
|
||||||
async fn log_messages(homeserver_url: Uri, username: &str, password: &str) -> anyhow::Result<()> {
|
async fn log_messages(
|
||||||
|
homeserver_url: String,
|
||||||
|
username: &str,
|
||||||
|
password: &str,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
let client = MatrixClient::new(homeserver_url, None);
|
let client = MatrixClient::new(homeserver_url, None);
|
||||||
|
|
||||||
client.log_in(username, password, None, None).await?;
|
client.log_in(username, password, None, None).await?;
|
||||||
|
|
||||||
let filter = FilterDefinition::ignore_all().into();
|
let filter = FilterDefinition::ignore_all().into();
|
||||||
let initial_sync_response = client
|
let initial_sync_response = client
|
||||||
.request(assign!(sync_events::Request::new(), {
|
.send_request(assign!(sync_events::Request::new(), {
|
||||||
filter: Some(&filter),
|
filter: Some(&filter),
|
||||||
}))
|
}))
|
||||||
.await?;
|
.await?;
|
||||||
@ -76,6 +79,5 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let server = homeserver_url.parse()?;
|
log_messages(homeserver_url, &username, &password).await
|
||||||
log_messages(server, &username, &password).await
|
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ impl<C: HttpClient> Client<C> {
|
|||||||
initial_device_display_name: Option<&str>,
|
initial_device_display_name: Option<&str>,
|
||||||
) -> Result<login::Response, Error<C::Error, ruma_client_api::Error>> {
|
) -> Result<login::Response, Error<C::Error, ruma_client_api::Error>> {
|
||||||
let response = self
|
let response = self
|
||||||
.request(assign!(
|
.send_request(assign!(
|
||||||
login::Request::new(
|
login::Request::new(
|
||||||
LoginInfo::Password { identifier: UserIdentifier::MatrixId(user), password }
|
LoginInfo::Password { identifier: UserIdentifier::MatrixId(user), password }
|
||||||
), {
|
), {
|
||||||
@ -50,7 +50,7 @@ impl<C: HttpClient> Client<C> {
|
|||||||
&self,
|
&self,
|
||||||
) -> Result<register::Response, Error<C::Error, ruma_client_api::r0::uiaa::UiaaResponse>> {
|
) -> Result<register::Response, Error<C::Error, ruma_client_api::r0::uiaa::UiaaResponse>> {
|
||||||
let response = self
|
let response = self
|
||||||
.request(assign!(register::Request::new(), { kind: RegistrationKind::Guest }))
|
.send_request(assign!(register::Request::new(), { kind: RegistrationKind::Guest }))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
*self.0.access_token.lock().unwrap() = response.access_token.clone();
|
*self.0.access_token.lock().unwrap() = response.access_token.clone();
|
||||||
@ -71,7 +71,7 @@ impl<C: HttpClient> Client<C> {
|
|||||||
password: &str,
|
password: &str,
|
||||||
) -> Result<register::Response, Error<C::Error, ruma_client_api::r0::uiaa::UiaaResponse>> {
|
) -> Result<register::Response, Error<C::Error, ruma_client_api::r0::uiaa::UiaaResponse>> {
|
||||||
let response = self
|
let response = self
|
||||||
.request(assign!(register::Request::new(), { username, password: Some(password) }))
|
.send_request(assign!(register::Request::new(), { username, password: Some(password) }))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
*self.0.access_token.lock().unwrap() = response.access_token.clone();
|
*self.0.access_token.lock().unwrap() = response.access_token.clone();
|
||||||
@ -116,7 +116,7 @@ impl<C: HttpClient> Client<C> {
|
|||||||
try_stream! {
|
try_stream! {
|
||||||
loop {
|
loop {
|
||||||
let response = self
|
let response = self
|
||||||
.request(assign!(sync_events::Request::new(), {
|
.send_request(assign!(sync_events::Request::new(), {
|
||||||
filter,
|
filter,
|
||||||
since: Some(&since),
|
since: Some(&since),
|
||||||
set_presence,
|
set_presence,
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
//! This module contains an abstraction for HTTP clients as well as friendly-named re-exports of
|
//! This module contains an abstraction for HTTP clients as well as friendly-named re-exports of
|
||||||
//! client types that implement this trait.
|
//! client types that implement this trait.
|
||||||
|
|
||||||
|
use std::{collections::BTreeMap, future::Future, pin::Pin};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use bytes::BufMut;
|
use bytes::BufMut;
|
||||||
|
use ruma_api::{OutgoingRequest, SendAccessToken};
|
||||||
|
|
||||||
|
use crate::ResponseResult;
|
||||||
|
|
||||||
#[cfg(feature = "hyper")]
|
#[cfg(feature = "hyper")]
|
||||||
mod hyper;
|
mod hyper;
|
||||||
@ -16,9 +21,9 @@ pub use self::hyper::HyperRustls;
|
|||||||
|
|
||||||
/// An HTTP client that can be used to send requests to a Matrix homeserver.
|
/// An HTTP client that can be used to send requests to a Matrix homeserver.
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait HttpClient {
|
pub trait HttpClient: Sync {
|
||||||
/// The type to use for `try_into_http_request`.
|
/// The type to use for `try_into_http_request`.
|
||||||
type RequestBody: Default + BufMut;
|
type RequestBody: Default + BufMut + Send;
|
||||||
|
|
||||||
/// The type to use for `try_from_http_response`.
|
/// The type to use for `try_from_http_response`.
|
||||||
type ResponseBody: AsRef<[u8]>;
|
type ResponseBody: AsRef<[u8]>;
|
||||||
@ -38,3 +43,46 @@ pub trait DefaultConstructibleHttpClient: HttpClient {
|
|||||||
/// Creates a new HTTP client with default configuration.
|
/// Creates a new HTTP client with default configuration.
|
||||||
fn default() -> Self;
|
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_request<'a, R: OutgoingRequest + 'a>(
|
||||||
|
&'a self,
|
||||||
|
homeserver_url: &str,
|
||||||
|
access_token: SendAccessToken<'_>,
|
||||||
|
request: R,
|
||||||
|
) -> Pin<Box<dyn Future<Output = ResponseResult<Self, R>> + 'a>> {
|
||||||
|
Box::pin(crate::send_request_with_url_params(
|
||||||
|
self,
|
||||||
|
homeserver_url,
|
||||||
|
access_token,
|
||||||
|
None,
|
||||||
|
request,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a strongly-typed matrix request to get back a strongly-typed response.
|
||||||
|
fn send_request_with_url_params<'a, R: OutgoingRequest + 'a>(
|
||||||
|
&'a self,
|
||||||
|
homeserver_url: &str,
|
||||||
|
access_token: SendAccessToken<'_>,
|
||||||
|
extra_params: BTreeMap<String, String>,
|
||||||
|
request: R,
|
||||||
|
) -> Pin<Box<dyn Future<Output = ResponseResult<Self, R>> + 'a>> {
|
||||||
|
Box::pin(crate::send_request_with_url_params(
|
||||||
|
self,
|
||||||
|
homeserver_url,
|
||||||
|
access_token,
|
||||||
|
Some(extra_params),
|
||||||
|
request,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<T: HttpClient> HttpClientExt for T {}
|
||||||
|
@ -75,12 +75,13 @@
|
|||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
|
future::Future,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use assign::assign;
|
use assign::assign;
|
||||||
use http::uri::Uri;
|
use http::uri::Uri;
|
||||||
use ruma_api::{AuthScheme, OutgoingRequest, SendAccessToken};
|
use ruma_api::{OutgoingRequest, SendAccessToken};
|
||||||
use ruma_serde::urlencoded;
|
use ruma_serde::urlencoded;
|
||||||
|
|
||||||
// "Undo" rename from `Cargo.toml` that only serves to make `hyper-rustls` available as a Cargo
|
// "Undo" rename from `Cargo.toml` that only serves to make `hyper-rustls` available as a Cargo
|
||||||
@ -95,9 +96,15 @@ pub mod http_client;
|
|||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
error::Error,
|
error::Error,
|
||||||
http_client::{DefaultConstructibleHttpClient, HttpClient},
|
http_client::{DefaultConstructibleHttpClient, HttpClient, HttpClientExt},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The result of sending the request `R` with the http client `C`.
|
||||||
|
pub type ResponseResult<C, R> = Result<
|
||||||
|
<R as OutgoingRequest>::IncomingResponse,
|
||||||
|
Error<<C as HttpClient>::Error, <R as OutgoingRequest>::EndpointError>,
|
||||||
|
>;
|
||||||
|
|
||||||
/// A client for the Matrix client-server API.
|
/// A client for the Matrix client-server API.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Client<C>(Arc<ClientData<C>>);
|
pub struct Client<C>(Arc<ClientData<C>>);
|
||||||
@ -106,7 +113,7 @@ pub struct Client<C>(Arc<ClientData<C>>);
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ClientData<C> {
|
struct ClientData<C> {
|
||||||
/// The URL of the homeserver to connect to.
|
/// The URL of the homeserver to connect to.
|
||||||
homeserver_url: Uri,
|
homeserver_url: String,
|
||||||
|
|
||||||
/// The underlying HTTP client.
|
/// The underlying HTTP client.
|
||||||
http_client: C,
|
http_client: C,
|
||||||
@ -121,7 +128,7 @@ impl<C> Client<C> {
|
|||||||
/// This allows the user to configure the details of HTTP as desired.
|
/// This allows the user to configure the details of HTTP as desired.
|
||||||
pub fn with_http_client(
|
pub fn with_http_client(
|
||||||
http_client: C,
|
http_client: C,
|
||||||
homeserver_url: Uri,
|
homeserver_url: String,
|
||||||
access_token: Option<String>,
|
access_token: Option<String>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self(Arc::new(ClientData {
|
Self(Arc::new(ClientData {
|
||||||
@ -141,7 +148,7 @@ impl<C> Client<C> {
|
|||||||
|
|
||||||
impl<C: DefaultConstructibleHttpClient> Client<C> {
|
impl<C: DefaultConstructibleHttpClient> Client<C> {
|
||||||
/// Creates a new client based on a default-constructed hyper HTTP client.
|
/// Creates a new client based on a default-constructed hyper HTTP client.
|
||||||
pub fn new(homeserver_url: Uri, access_token: Option<String>) -> Self {
|
pub fn new(homeserver_url: String, access_token: Option<String>) -> Self {
|
||||||
Self(Arc::new(ClientData {
|
Self(Arc::new(ClientData {
|
||||||
homeserver_url,
|
homeserver_url,
|
||||||
http_client: DefaultConstructibleHttpClient::default(),
|
http_client: DefaultConstructibleHttpClient::default(),
|
||||||
@ -152,48 +159,81 @@ impl<C: DefaultConstructibleHttpClient> Client<C> {
|
|||||||
|
|
||||||
impl<C: HttpClient> Client<C> {
|
impl<C: HttpClient> Client<C> {
|
||||||
/// Makes a request to a Matrix API endpoint.
|
/// Makes a request to a Matrix API endpoint.
|
||||||
pub async fn request<Request: OutgoingRequest>(
|
pub async fn send_request<R: OutgoingRequest>(
|
||||||
&self,
|
&self,
|
||||||
request: Request,
|
request: R,
|
||||||
) -> Result<Request::IncomingResponse, Error<C::Error, Request::EndpointError>> {
|
) -> Result<R::IncomingResponse, Error<C::Error, R::EndpointError>> {
|
||||||
self.request_with_url_params(request, None).await
|
let access_token = self.access_token();
|
||||||
|
let send_access_token = match access_token.as_deref() {
|
||||||
|
Some(at) => SendAccessToken::IfRequired(at),
|
||||||
|
None => SendAccessToken::None,
|
||||||
|
};
|
||||||
|
|
||||||
|
send_request_with_url_params(
|
||||||
|
&self.0.http_client,
|
||||||
|
&self.0.homeserver_url,
|
||||||
|
send_access_token,
|
||||||
|
None,
|
||||||
|
request,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Makes a request to a Matrix API endpoint including additional URL parameters.
|
/// Makes a request to a Matrix API endpoint including additional URL parameters.
|
||||||
pub async fn request_with_url_params<Request: OutgoingRequest>(
|
pub async fn send_request_with_url_params<R: OutgoingRequest>(
|
||||||
&self,
|
&self,
|
||||||
request: Request,
|
extra_params: BTreeMap<String, String>,
|
||||||
extra_params: Option<BTreeMap<String, String>>,
|
request: R,
|
||||||
) -> Result<Request::IncomingResponse, Error<C::Error, Request::EndpointError>> {
|
) -> Result<R::IncomingResponse, Error<C::Error, R::EndpointError>> {
|
||||||
let client = self.0.clone();
|
let access_token = self.access_token();
|
||||||
let mut http_request = {
|
let send_access_token = match access_token.as_deref() {
|
||||||
let lock;
|
Some(at) => SendAccessToken::IfRequired(at),
|
||||||
let access_token = if Request::METADATA.authentication == AuthScheme::AccessToken {
|
None => SendAccessToken::None,
|
||||||
lock = client.access_token.lock().unwrap();
|
};
|
||||||
if let Some(access_token) = &*lock {
|
|
||||||
SendAccessToken::IfRequired(access_token.as_str())
|
send_request_with_url_params(
|
||||||
} else {
|
&self.0.http_client,
|
||||||
return Err(Error::AuthenticationRequired);
|
&self.0.homeserver_url,
|
||||||
}
|
send_access_token,
|
||||||
} else {
|
Some(extra_params),
|
||||||
SendAccessToken::None
|
request,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_request_with_url_params<'a, C, Request>(
|
||||||
|
http_client: &'a C,
|
||||||
|
homeserver_url: &str,
|
||||||
|
send_access_token: SendAccessToken<'_>,
|
||||||
|
extra_params: Option<BTreeMap<String, String>>,
|
||||||
|
request: Request,
|
||||||
|
) -> impl Future<Output = Result<Request::IncomingResponse, Error<C::Error, Request::EndpointError>>>
|
||||||
|
+ Send
|
||||||
|
+ 'a
|
||||||
|
where
|
||||||
|
C: HttpClient + ?Sized,
|
||||||
|
Request: OutgoingRequest,
|
||||||
|
{
|
||||||
|
let res = request.try_into_http_request(homeserver_url, send_access_token);
|
||||||
|
|
||||||
|
async move {
|
||||||
|
let mut http_request = res?;
|
||||||
|
|
||||||
|
if let Some(extra_params) = extra_params {
|
||||||
|
let extra_params = urlencoded::to_string(extra_params).unwrap();
|
||||||
|
let uri = http_request.uri_mut();
|
||||||
|
let new_path_and_query = match uri.query() {
|
||||||
|
Some(params) => format!("{}?{}&{}", uri.path(), params, extra_params),
|
||||||
|
None => format!("{}?{}", uri.path(), extra_params),
|
||||||
};
|
};
|
||||||
|
*uri = Uri::from_parts(assign!(uri.clone().into_parts(), {
|
||||||
request.try_into_http_request(&client.homeserver_url.to_string(), access_token)?
|
path_and_query: Some(new_path_and_query.parse()?),
|
||||||
};
|
}))?;
|
||||||
|
}
|
||||||
let extra_params = urlencoded::to_string(extra_params).unwrap();
|
|
||||||
let uri = http_request.uri_mut();
|
|
||||||
let new_path_and_query = match uri.query() {
|
|
||||||
Some(params) => format!("{}?{}&{}", uri.path(), params, extra_params),
|
|
||||||
None => format!("{}?{}", uri.path(), extra_params),
|
|
||||||
};
|
|
||||||
*uri = Uri::from_parts(assign!(uri.clone().into_parts(), {
|
|
||||||
path_and_query: Some(new_path_and_query.parse()?),
|
|
||||||
}))?;
|
|
||||||
|
|
||||||
let http_response =
|
let http_response =
|
||||||
client.http_client.send_http_request(http_request).await.map_err(Error::Response)?;
|
http_client.send_http_request(http_request).await.map_err(Error::Response)?;
|
||||||
Ok(ruma_api::IncomingResponse::try_from_http_response(http_response)?)
|
Ok(ruma_api::IncomingResponse::try_from_http_response(http_response)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user