matrix_sdk/client/
futures.rs1#![deny(unreachable_pub)]
16
17use std::{fmt::Debug, future::IntoFuture};
18
19use eyeball::{SharedObservable, Subscriber};
20use js_int::UInt;
21use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm, boxed_into_future};
22use oauth2::{RequestTokenError, basic::BasicErrorResponseType};
23use ruma::api::{
24 OutgoingRequest,
25 client::media,
26 error::{ErrorKind, FromHttpResponseError},
27 path_builder::PathBuilder,
28};
29use tracing::{error, trace};
30
31use super::super::Client;
32use crate::{
33 Error, RefreshTokenError, TransmissionProgress,
34 authentication::oauth::OAuthError,
35 config::RequestConfig,
36 error::{HttpError, HttpResult},
37 http_client::{SupportedAuthScheme, SupportedPathBuilder},
38 media::MediaError,
39};
40
41#[allow(missing_debug_implementations)]
43pub struct SendRequest<R> {
44 pub(crate) client: Client,
45 pub(crate) request: R,
46 pub(crate) config: Option<RequestConfig>,
47 pub(crate) send_progress: SharedObservable<TransmissionProgress>,
48}
49
50impl<R> SendRequest<R> {
51 pub fn with_send_progress_observable(
58 mut self,
59 send_progress: SharedObservable<TransmissionProgress>,
60 ) -> Self {
61 self.send_progress = send_progress;
62 self
63 }
64
65 pub fn with_request_config(mut self, request_config: impl Into<Option<RequestConfig>>) -> Self {
68 self.config = request_config.into();
69 self
70 }
71
72 pub fn subscribe_to_send_progress(&self) -> Subscriber<TransmissionProgress> {
75 self.send_progress.subscribe()
76 }
77}
78
79impl<R> IntoFuture for SendRequest<R>
80where
81 R: OutgoingRequest + Clone + Debug + SendOutsideWasm + SyncOutsideWasm + 'static,
82 R::Authentication: SupportedAuthScheme,
83 R::PathBuilder: SupportedPathBuilder,
84 for<'a> <R::PathBuilder as PathBuilder>::Input<'a>: SendOutsideWasm + SyncOutsideWasm,
85 R::IncomingResponse: SendOutsideWasm + SyncOutsideWasm,
86 HttpError: From<FromHttpResponseError<R::EndpointError>>,
87{
88 type Output = HttpResult<R::IncomingResponse>;
89 boxed_into_future!();
90
91 fn into_future(self) -> Self::IntoFuture {
92 enum RetryRequest {
93 Yes,
94 No,
95 }
96
97 async fn handle_unknown_token_error(
100 e: &HttpError,
101 client: &Client,
102 ) -> HttpResult<RetryRequest> {
103 let Some(ErrorKind::UnknownToken(unknown_token_data)) = e.client_api_error_kind()
105 else {
106 return Ok(RetryRequest::No);
107 };
108
109 trace!("Token refresh: Unknown token error received.");
110
111 if !client.inner.auth_ctx.handle_refresh_tokens {
113 trace!("Token refresh: Automatic refresh disabled.");
114 client.broadcast_unknown_token(unknown_token_data);
115 return Ok(RetryRequest::No);
116 }
117
118 if let Err(refresh_error) = client.refresh_access_token().await {
120 match &refresh_error {
121 RefreshTokenError::RefreshTokenRequired => {
122 trace!("Token refresh: The session doesn't have a refresh token.");
123 client.broadcast_unknown_token(unknown_token_data);
125 Ok(RetryRequest::No)
126 }
127
128 RefreshTokenError::OAuth(oauth_error) => {
129 match &**oauth_error {
130 OAuthError::RefreshToken(RequestTokenError::ServerResponse(
131 error_response,
132 )) if *error_response.error()
133 == BasicErrorResponseType::InvalidGrant =>
134 {
135 error!(
136 "Token refresh: OAuth 2.0 refresh_token rejected \
137 with invalid grant"
138 );
139 client.broadcast_unknown_token(unknown_token_data);
141 }
142 _ => {
143 trace!("Token refresh: OAuth 2.0 refresh encountered a problem.");
144 }
147 }
148 Err(HttpError::RefreshToken(refresh_error))
149 }
150
151 _ => {
152 trace!("Token refresh: Token refresh failed.");
153 client.broadcast_unknown_token(unknown_token_data);
156 Err(HttpError::RefreshToken(refresh_error))
157 }
158 }
159 } else {
160 trace!("Token refresh: Refresh succeeded, retrying request.");
161 Ok(RetryRequest::Yes)
162 }
163 }
164
165 let Self { client, request, config, send_progress } = self;
166
167 Box::pin(async move {
168 let res =
169 Box::pin(client.send_inner(request.clone(), config, send_progress.clone())).await;
170
171 if let Err(e) = &res
172 && let RetryRequest::Yes = handle_unknown_token_error(e, &client).await?
173 {
174 return Box::pin(client.send_inner(request, config, send_progress)).await;
175 }
176
177 res
178 })
179 }
180}
181
182#[allow(missing_debug_implementations)]
186pub struct SendMediaUploadRequest {
187 send_request: SendRequest<media::create_content::v3::Request>,
188}
189
190impl SendMediaUploadRequest {
191 pub fn new(request: SendRequest<media::create_content::v3::Request>) -> Self {
192 Self { send_request: request }
193 }
194
195 pub fn with_send_progress_observable(
202 mut self,
203 send_progress: SharedObservable<TransmissionProgress>,
204 ) -> Self {
205 self.send_request = self.send_request.with_send_progress_observable(send_progress);
206 self
207 }
208
209 pub fn subscribe_to_send_progress(&self) -> Subscriber<TransmissionProgress> {
212 self.send_request.send_progress.subscribe()
213 }
214}
215
216impl IntoFuture for SendMediaUploadRequest {
217 type Output = Result<media::create_content::v3::Response, Error>;
218 boxed_into_future!();
219
220 fn into_future(self) -> Self::IntoFuture {
221 let request_length = self.send_request.request.file.len();
222 let client = self.send_request.client.clone();
223 let send_request = self.send_request;
224
225 Box::pin(async move {
226 let max_upload_size = client.load_or_fetch_max_upload_size().await?;
227 let request_length = UInt::new_wrapping(request_length as u64);
228 if request_length > max_upload_size {
229 return Err(Error::Media(MediaError::MediaTooLargeToUpload {
230 max: max_upload_size,
231 current: request_length,
232 }));
233 }
234
235 send_request.into_future().await.map_err(Into::into)
236 })
237 }
238}