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 ruma::api::{
23 OutgoingRequest,
24 client::media,
25 error::{ErrorKind, FromHttpResponseError},
26 path_builder::PathBuilder,
27};
28use tracing::{error, trace};
29
30use super::super::Client;
31use crate::{
32 Error, RefreshTokenError, TransmissionProgress,
33 config::RequestConfig,
34 error::{HttpError, HttpResult},
35 http_client::{SupportedAuthScheme, SupportedPathBuilder},
36 media::MediaError,
37};
38
39#[allow(missing_debug_implementations)]
41pub struct SendRequest<R> {
42 pub(crate) client: Client,
43 pub(crate) request: R,
44 pub(crate) config: Option<RequestConfig>,
45 pub(crate) send_progress: SharedObservable<TransmissionProgress>,
46}
47
48impl<R> SendRequest<R> {
49 pub fn with_send_progress_observable(
56 mut self,
57 send_progress: SharedObservable<TransmissionProgress>,
58 ) -> Self {
59 self.send_progress = send_progress;
60 self
61 }
62
63 pub fn with_request_config(mut self, request_config: impl Into<Option<RequestConfig>>) -> Self {
66 self.config = request_config.into();
67 self
68 }
69
70 pub fn subscribe_to_send_progress(&self) -> Subscriber<TransmissionProgress> {
72 self.send_progress.subscribe()
73 }
74}
75
76impl<R> IntoFuture for SendRequest<R>
77where
78 R: OutgoingRequest + Clone + Debug + SendOutsideWasm + SyncOutsideWasm + 'static,
79 R::Authentication: SupportedAuthScheme,
80 R::PathBuilder: SupportedPathBuilder,
81 for<'a> <R::PathBuilder as PathBuilder>::Input<'a>: SendOutsideWasm + SyncOutsideWasm,
82 R::IncomingResponse: SendOutsideWasm + SyncOutsideWasm,
83 HttpError: From<FromHttpResponseError<R::EndpointError>>,
84{
85 type Output = HttpResult<R::IncomingResponse>;
86 boxed_into_future!();
87
88 fn into_future(self) -> Self::IntoFuture {
89 enum RetryRequest {
90 Yes,
91 No,
92 }
93
94 async fn handle_unknown_token_error(
97 e: &HttpError,
98 client: &Client,
99 ) -> HttpResult<RetryRequest> {
100 let Some(ErrorKind::UnknownToken(unknown_token_data)) = e.client_api_error_kind()
103 else {
104 return Ok(RetryRequest::No);
105 };
106
107 trace!("Token refresh: Unknown token error received.");
108
109 if !client.inner.auth_ctx.handle_refresh_tokens {
112 trace!("Token refresh: Automatic refresh disabled.");
113 client.broadcast_unknown_token(unknown_token_data);
114 return Ok(RetryRequest::No);
115 }
116
117 if let Err(refresh_error) = client.refresh_access_token().await {
119 match &refresh_error {
120 RefreshTokenError::RefreshTokenRequired => {
121 trace!("Token refresh: The session doesn't have a refresh token.");
122 client.broadcast_unknown_token(unknown_token_data);
125 Ok(RetryRequest::No)
126 }
127
128 RefreshTokenError::OAuth(_) | RefreshTokenError::MatrixAuth(_) => {
129 if refresh_error.is_session_rejected() {
130 error!(
131 "Token refresh: the refresh token was rejected: {refresh_error}"
132 );
133 client.broadcast_unknown_token(unknown_token_data);
136 } else {
137 trace!("Token refresh: the refresh request itself failed.");
138 }
139 Err(HttpError::RefreshToken(refresh_error))
140 }
141 }
142 } else {
143 trace!("Token refresh: Refresh succeeded, retrying request.");
144 Ok(RetryRequest::Yes)
145 }
146 }
147
148 let Self { client, request, config, send_progress } = self;
149
150 Box::pin(async move {
151 let res =
152 Box::pin(client.send_inner(request.clone(), config, send_progress.clone())).await;
153
154 if let Err(e) = &res
155 && let RetryRequest::Yes = handle_unknown_token_error(e, &client).await?
156 {
157 return Box::pin(client.send_inner(request, config, send_progress)).await;
158 }
159
160 res
161 })
162 }
163}
164
165#[allow(missing_debug_implementations)]
169pub struct SendMediaUploadRequest {
170 send_request: SendRequest<media::create_content::v3::Request>,
171}
172
173impl SendMediaUploadRequest {
174 pub fn new(request: SendRequest<media::create_content::v3::Request>) -> Self {
175 Self { send_request: request }
176 }
177
178 pub fn with_send_progress_observable(
185 mut self,
186 send_progress: SharedObservable<TransmissionProgress>,
187 ) -> Self {
188 self.send_request = self.send_request.with_send_progress_observable(send_progress);
189 self
190 }
191
192 pub fn subscribe_to_send_progress(&self) -> Subscriber<TransmissionProgress> {
194 self.send_request.send_progress.subscribe()
195 }
196}
197
198impl IntoFuture for SendMediaUploadRequest {
199 type Output = Result<media::create_content::v3::Response, Error>;
200 boxed_into_future!();
201
202 fn into_future(self) -> Self::IntoFuture {
203 let request_length = self.send_request.request.file.len();
204 let client = self.send_request.client.clone();
205 let send_request = self.send_request;
206
207 Box::pin(async move {
208 let max_upload_size = client.load_or_fetch_max_upload_size().await?;
209 let request_length = UInt::new_wrapping(request_length as u64);
210 if request_length > max_upload_size {
211 return Err(Error::Media(MediaError::MediaTooLargeToUpload {
212 max: max_upload_size,
213 current: request_length,
214 }));
215 }
216
217 send_request.into_future().await.map_err(Into::into)
218 })
219 }
220}