Skip to main content

ruma_client_api/rendezvous/
create_rendezvous_session.rs

1//! `POST /_matrix/client/*/rendezvous/`
2//!
3//! Create a rendezvous session.
4
5#[cfg(feature = "unstable-msc4108")]
6pub mod unstable_msc4108 {
7    //! `unstable/org.matrix.msc4108` ([MSC])
8    //!
9    //! [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/4108
10
11    use http::header::{CONTENT_TYPE, ETAG, EXPIRES, LAST_MODIFIED};
12    #[cfg(feature = "client")]
13    use ruma_common::api::BytesBody;
14    use ruma_common::{
15        api::{
16            auth_scheme::NoAccessToken,
17            error::{DeserializationError, Error, HeaderDeserializationError},
18        },
19        http_headers::TEXT_PLAIN,
20        metadata,
21    };
22    use url::Url;
23    use web_time::SystemTime;
24
25    metadata! {
26        method: POST,
27        rate_limited: true,
28        authentication: NoAccessToken,
29        history: {
30            unstable("org.matrix.msc4108") => "/_matrix/client/unstable/org.matrix.msc4108/rendezvous",
31        }
32    }
33
34    /// Request type for the `POST` `rendezvous` endpoint from the 2024 version of MSC4108.
35    #[derive(Debug, Default, Clone)]
36    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
37    pub struct Request {
38        /// Any data up to maximum size allowed by the server.
39        pub content: String,
40    }
41
42    #[cfg(feature = "client")]
43    impl ruma_common::api::OutgoingRequest for Request {
44        type Body = BytesBody;
45        type EndpointError = Error;
46        type IncomingResponse = Response;
47
48        fn try_into_http_request_inner(
49            self,
50            base_url: &str,
51            considering: std::borrow::Cow<'_, ruma_common::api::SupportedVersions>,
52        ) -> Result<http::Request<BytesBody>, ruma_common::api::error::IntoHttpError> {
53            use http::header::CONTENT_LENGTH;
54            use ruma_common::api::Metadata;
55
56            let url = Self::make_endpoint_url(considering, base_url, &[], "")?;
57            let content_length = self.content.len();
58
59            Ok(http::Request::builder()
60                .method(Self::METHOD)
61                .uri(url)
62                .header(CONTENT_TYPE, TEXT_PLAIN)
63                .header(CONTENT_LENGTH, content_length)
64                .body(BytesBody(self.content.into()))?)
65        }
66    }
67
68    #[cfg(feature = "server")]
69    impl ruma_common::api::IncomingRequest for Request {
70        type EndpointError = Error;
71        type OutgoingResponse = Response;
72
73        fn try_from_http_request_inner(
74            request: http::Request<&[u8]>,
75            _path_args: &[&str],
76        ) -> Result<Self, DeserializationError> {
77            let content_type = request
78                .headers()
79                .get(CONTENT_TYPE)
80                .ok_or(HeaderDeserializationError::MissingHeader(CONTENT_TYPE.to_string()))?;
81
82            if content_type != TEXT_PLAIN {
83                Err(HeaderDeserializationError::InvalidHeaderValue {
84                    header: CONTENT_TYPE.to_string(),
85                    expected: TEXT_PLAIN
86                        .to_str()
87                        .expect("expected content type should be a valid static string")
88                        .to_owned(),
89                    unexpected: content_type.to_str()?.to_owned(),
90                }
91                .into())
92            } else {
93                let body = request.into_body().to_vec();
94                let content = String::from_utf8(body)
95                    .map_err(|e| DeserializationError::Utf8(e.utf8_error()))?;
96
97                Ok(Self { content })
98            }
99        }
100    }
101
102    impl Request {
103        /// Creates a new `Request` with the given content.
104        pub fn new(content: String) -> Self {
105            Self { content }
106        }
107    }
108
109    /// Response type for the `POST` `rendezvous` endpoint from the 2024 version of MSC4108.
110    #[derive(Debug, Clone)]
111    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
112    pub struct Response {
113        /// The absolute URL of the rendezvous session.
114        pub url: Url,
115
116        /// ETag for the current payload at the rendezvous session as
117        /// per [RFC7232](https://httpwg.org/specs/rfc7232.html#header.etag).
118        pub etag: String,
119
120        /// The expiry time of the rendezvous as per
121        /// [RFC7234](https://httpwg.org/specs/rfc7234.html#header.expires).
122        pub expires: SystemTime,
123
124        /// The last modified date of the payload as
125        /// per [RFC7232](https://httpwg.org/specs/rfc7232.html#header.last-modified)
126        pub last_modified: SystemTime,
127    }
128
129    #[doc(hidden)]
130    #[derive(ruma_common::serde::_FakeDeriveSerde)]
131    #[cfg_attr(feature = "server", derive(serde::Serialize, ruma_common::api::OutgoingBodyJson))]
132    #[cfg_attr(feature = "client", derive(serde::Deserialize))]
133    pub struct ResponseBody {
134        url: Url,
135    }
136
137    #[cfg(feature = "client")]
138    impl ruma_common::api::IncomingResponse for Response {
139        type EndpointError = Error;
140
141        fn try_from_http_response_inner(
142            response: http::Response<&[u8]>,
143        ) -> Result<Self, DeserializationError> {
144            let get_date = |header: http::HeaderName| -> Result<SystemTime, DeserializationError> {
145                let date = response
146                    .headers()
147                    .get(&header)
148                    .ok_or_else(|| HeaderDeserializationError::MissingHeader(header.to_string()))?;
149
150                let date = ruma_common::http_headers::http_date_to_system_time(date)?;
151
152                Ok(date)
153            };
154
155            let etag = response
156                .headers()
157                .get(ETAG)
158                .ok_or(HeaderDeserializationError::MissingHeader(ETAG.to_string()))?
159                .to_str()?
160                .to_owned();
161            let expires = get_date(EXPIRES)?;
162            let last_modified = get_date(LAST_MODIFIED)?;
163
164            let body: ResponseBody = serde_json::from_slice(response.body())?;
165
166            Ok(Self { url: body.url, etag, expires, last_modified })
167        }
168    }
169
170    #[cfg(feature = "server")]
171    impl ruma_common::api::OutgoingResponse for Response {
172        type Body = ResponseBody;
173
174        fn try_into_http_response_inner(
175            self,
176        ) -> Result<http::Response<Self::Body>, ruma_common::api::error::IntoHttpError> {
177            use http::header::{CACHE_CONTROL, PRAGMA};
178            use ruma_common::http_headers::system_time_to_http_date;
179
180            let body = ResponseBody { url: self.url };
181
182            let expires = system_time_to_http_date(&self.expires)?;
183            let last_modified = system_time_to_http_date(&self.last_modified)?;
184
185            Ok(http::Response::builder()
186                .status(http::StatusCode::OK)
187                .header(PRAGMA, "no-cache")
188                .header(CACHE_CONTROL, "no-store")
189                .header(ETAG, self.etag)
190                .header(EXPIRES, expires)
191                .header(LAST_MODIFIED, last_modified)
192                .body(body)?)
193        }
194    }
195}
196
197#[cfg(feature = "unstable-msc4388")]
198pub mod unstable_msc4388 {
199    //! `unstable/io.element.msc4388` ([MSC])
200    //!
201    //! [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/4388
202    use std::time::Duration;
203
204    use ruma_common::{
205        api::{auth_scheme::AccessTokenOptional, request, response},
206        metadata,
207    };
208
209    metadata! {
210        method: POST,
211        rate_limited: true,
212        authentication: AccessTokenOptional,
213        history: {
214            unstable => "/_matrix/client/unstable/io.element.msc4388/rendezvous",
215        }
216    }
217
218    /// Request type for the `POST` `rendezvous` endpoint.
219    #[request]
220    pub struct Request {
221        /// Data up to maximum size allowed by the server.
222        pub data: String,
223    }
224
225    impl Request {
226        /// Creates a new `Request` with the given content.
227        pub fn new(data: String) -> Self {
228            Self { data }
229        }
230    }
231
232    /// Response type for the `POST` `rendezvous` endpoint.
233    #[response]
234    pub struct Response {
235        /// The ID of the created rendezvous session.
236        pub id: String,
237
238        /// The initial sequence token for the session.
239        pub sequence_token: String,
240
241        /// The time remaining in milliseconds until the session expires.
242        #[serde(with = "ruma_common::serde::duration::ms", rename = "expires_in_ms")]
243        pub expires_in: Duration,
244    }
245
246    impl Response {
247        /// Creates a new `Response` with the given content.
248        pub fn new(id: String, sequence_token: String, expires_in: Duration) -> Self {
249            Self { id, sequence_token, expires_in }
250        }
251    }
252}