ruma_client_api/rendezvous/
create_rendezvous_session.rs1#[cfg(feature = "unstable-msc4108")]
6pub mod unstable_msc4108 {
7 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 #[derive(Debug, Default, Clone)]
36 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
37 pub struct Request {
38 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 Self { content } = self;
57
58 let url = Self::make_endpoint_url(considering, base_url, &[], "")?;
59 let content_length = content.len();
60
61 Ok(http::Request::builder()
62 .method(Self::METHOD)
63 .uri(url)
64 .header(CONTENT_TYPE, TEXT_PLAIN)
65 .header(CONTENT_LENGTH, content_length)
66 .body(BytesBody(content.into()))?)
67 }
68 }
69
70 #[cfg(feature = "server")]
71 impl ruma_common::api::IncomingRequest for Request {
72 type EndpointError = Error;
73 type OutgoingResponse = Response;
74
75 fn try_from_http_request_inner(
76 request: http::Request<&[u8]>,
77 _path_args: &[&str],
78 ) -> Result<Self, DeserializationError> {
79 let content_type = request
80 .headers()
81 .get(CONTENT_TYPE)
82 .ok_or(HeaderDeserializationError::MissingHeader(CONTENT_TYPE.to_string()))?;
83
84 if content_type != TEXT_PLAIN {
85 Err(HeaderDeserializationError::InvalidHeaderValue {
86 header: CONTENT_TYPE.to_string(),
87 expected: TEXT_PLAIN
88 .to_str()
89 .expect("expected content type should be a valid static string")
90 .to_owned(),
91 unexpected: content_type.to_str()?.to_owned(),
92 }
93 .into())
94 } else {
95 let body = request.into_body().to_vec();
96 let content = String::from_utf8(body)
97 .map_err(|e| DeserializationError::Utf8(e.utf8_error()))?;
98
99 Ok(Self { content })
100 }
101 }
102 }
103
104 impl Request {
105 pub fn new(content: String) -> Self {
107 Self { content }
108 }
109 }
110
111 #[derive(Debug, Clone)]
113 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
114 pub struct Response {
115 pub url: Url,
117
118 pub etag: String,
121
122 pub expires: SystemTime,
125
126 pub last_modified: SystemTime,
129 }
130
131 #[doc(hidden)]
132 #[derive(ruma_common::serde::_FakeDeriveSerde)]
133 #[cfg_attr(feature = "server", derive(serde::Serialize, ruma_common::api::OutgoingBodyJson))]
134 #[cfg_attr(feature = "client", derive(serde::Deserialize))]
135 pub struct ResponseBody {
136 url: Url,
137 }
138
139 #[cfg(feature = "client")]
140 impl ruma_common::api::IncomingResponse for Response {
141 type EndpointError = Error;
142
143 fn try_from_http_response_inner(
144 response: http::Response<&[u8]>,
145 ) -> Result<Self, DeserializationError> {
146 let get_date = |header: http::HeaderName| -> Result<SystemTime, DeserializationError> {
147 let date = response
148 .headers()
149 .get(&header)
150 .ok_or_else(|| HeaderDeserializationError::MissingHeader(header.to_string()))?;
151
152 let date = ruma_common::http_headers::http_date_to_system_time(date)?;
153
154 Ok(date)
155 };
156
157 let etag = response
158 .headers()
159 .get(ETAG)
160 .ok_or(HeaderDeserializationError::MissingHeader(ETAG.to_string()))?
161 .to_str()?
162 .to_owned();
163 let expires = get_date(EXPIRES)?;
164 let last_modified = get_date(LAST_MODIFIED)?;
165
166 let ResponseBody { url } = serde_json::from_slice(response.body())?;
167
168 Ok(Self { url, etag, expires, last_modified })
169 }
170 }
171
172 #[cfg(feature = "server")]
173 impl ruma_common::api::OutgoingResponse for Response {
174 type Body = ResponseBody;
175
176 fn try_into_http_response_inner(
177 self,
178 ) -> Result<http::Response<Self::Body>, ruma_common::api::error::IntoHttpError> {
179 use http::header::{CACHE_CONTROL, PRAGMA};
180 use ruma_common::http_headers::system_time_to_http_date;
181
182 let Self { url, etag, expires, last_modified } = self;
183
184 let body = ResponseBody { url };
185
186 let expires = system_time_to_http_date(&expires)?;
187 let last_modified = system_time_to_http_date(&last_modified)?;
188
189 Ok(http::Response::builder()
190 .status(http::StatusCode::OK)
191 .header(PRAGMA, "no-cache")
192 .header(CACHE_CONTROL, "no-store")
193 .header(ETAG, etag)
194 .header(EXPIRES, expires)
195 .header(LAST_MODIFIED, last_modified)
196 .body(body)?)
197 }
198 }
199}
200
201#[cfg(feature = "unstable-msc4388")]
202pub mod unstable_msc4388 {
203 use std::time::Duration;
207
208 use ruma_common::{
209 api::{auth_scheme::AccessTokenOptional, request, response},
210 metadata,
211 };
212
213 metadata! {
214 method: POST,
215 rate_limited: true,
216 authentication: AccessTokenOptional,
217 history: {
218 unstable => "/_matrix/client/unstable/io.element.msc4388/rendezvous",
219 }
220 }
221
222 #[request]
224 pub struct Request {
225 pub data: String,
227 }
228
229 impl Request {
230 pub fn new(data: String) -> Self {
232 Self { data }
233 }
234 }
235
236 #[response]
238 pub struct Response {
239 pub id: String,
241
242 pub sequence_token: String,
244
245 #[serde(with = "ruma_common::serde::duration::ms", rename = "expires_in_ms")]
247 pub expires_in: Duration,
248 }
249
250 impl Response {
251 pub fn new(id: String, sequence_token: String, expires_in: Duration) -> Self {
253 Self { id, sequence_token, expires_in }
254 }
255 }
256}