matrix_sdk/client/
futures.rs1#![deny(unreachable_pub)]
16
17use std::{fmt::Debug, future::IntoFuture};
18
19use eyeball::SharedObservable;
20#[cfg(not(target_arch = "wasm32"))]
21use eyeball::Subscriber;
22#[cfg(feature = "experimental-oidc")]
23use mas_oidc_client::{
24 error::{
25 Error as OidcClientError, ErrorBody as OidcErrorBody, HttpError as OidcHttpError,
26 TokenRefreshError, TokenRequestError,
27 },
28 types::errors::ClientErrorCode,
29};
30use matrix_sdk_common::boxed_into_future;
31use ruma::api::{client::error::ErrorKind, error::FromHttpResponseError, OutgoingRequest};
32#[cfg(feature = "experimental-oidc")]
33use tracing::error;
34use tracing::trace;
35
36use super::super::Client;
37#[cfg(feature = "experimental-oidc")]
38use crate::authentication::oidc::OidcError;
39use crate::{
40 config::RequestConfig,
41 error::{HttpError, HttpResult},
42 RefreshTokenError, TransmissionProgress,
43};
44
45#[allow(missing_debug_implementations)]
47pub struct SendRequest<R> {
48 pub(crate) client: Client,
49 pub(crate) homeserver_override: Option<String>,
50 pub(crate) request: R,
51 pub(crate) config: Option<RequestConfig>,
52 pub(crate) send_progress: SharedObservable<TransmissionProgress>,
53}
54
55impl<R> SendRequest<R> {
56 pub fn with_send_progress_observable(
63 mut self,
64 send_progress: SharedObservable<TransmissionProgress>,
65 ) -> Self {
66 self.send_progress = send_progress;
67 self
68 }
69
70 pub fn with_homeserver_override(mut self, homeserver_override: Option<String>) -> Self {
75 self.homeserver_override = homeserver_override;
76 self
77 }
78
79 pub fn with_request_config(mut self, request_config: impl Into<Option<RequestConfig>>) -> Self {
82 self.config = request_config.into();
83 self
84 }
85
86 #[cfg(not(target_arch = "wasm32"))]
89 pub fn subscribe_to_send_progress(&self) -> Subscriber<TransmissionProgress> {
90 self.send_progress.subscribe()
91 }
92}
93
94impl<R> IntoFuture for SendRequest<R>
95where
96 R: OutgoingRequest + Clone + Debug + Send + Sync + 'static,
97 R::IncomingResponse: Send + Sync,
98 HttpError: From<FromHttpResponseError<R::EndpointError>>,
99{
100 type Output = HttpResult<R::IncomingResponse>;
101 boxed_into_future!();
102
103 fn into_future(self) -> Self::IntoFuture {
104 let Self { client, request, config, send_progress, homeserver_override } = self;
105
106 Box::pin(async move {
107 let res = Box::pin(client.send_inner(
108 request.clone(),
109 config,
110 homeserver_override.clone(),
111 send_progress.clone(),
112 ))
113 .await;
114
115 if let Err(Some(ErrorKind::UnknownToken { soft_logout })) =
117 res.as_ref().map_err(HttpError::client_api_error_kind)
118 {
119 trace!("Token refresh: Unknown token error received.");
120
121 if !client.inner.auth_ctx.handle_refresh_tokens {
123 trace!("Token refresh: Automatic refresh disabled.");
124 client.broadcast_unknown_token(soft_logout);
125 return res;
126 }
127
128 if let Err(refresh_error) = client.refresh_access_token().await {
130 match &refresh_error {
131 RefreshTokenError::RefreshTokenRequired => {
132 trace!("Token refresh: The session doesn't have a refresh token.");
133 client.broadcast_unknown_token(soft_logout);
135 }
136
137 #[cfg(feature = "experimental-oidc")]
138 RefreshTokenError::Oidc(oidc_error) => {
139 match **oidc_error {
140 OidcError::Oidc(OidcClientError::TokenRefresh(
141 TokenRefreshError::Token(TokenRequestError::Http(
142 OidcHttpError {
143 body:
144 Some(OidcErrorBody {
145 error: ClientErrorCode::InvalidGrant,
146 ..
147 }),
148 ..
149 },
150 )),
151 )) => {
152 error!("Token refresh: OIDC refresh_token rejected with invalid grant");
153 client.broadcast_unknown_token(soft_logout);
155 }
156 _ => {
157 trace!("Token refresh: OIDC refresh encountered a problem.");
158 }
161 };
162 return Err(HttpError::RefreshToken(refresh_error));
163 }
164
165 _ => {
166 trace!("Token refresh: Token refresh failed.");
167 client.broadcast_unknown_token(soft_logout);
170 return Err(HttpError::RefreshToken(refresh_error));
171 }
172 }
173 } else {
174 trace!("Token refresh: Refresh succeeded, retrying request.");
175 return Box::pin(client.send_inner(
176 request,
177 config,
178 homeserver_override,
179 send_progress,
180 ))
181 .await;
182 }
183 }
184
185 res
186 })
187 }
188}