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, error::DeserializationError};
14 use ruma_common::{
15 api::{
16 auth_scheme::NoAccessToken,
17 error::{Error, HeaderDeserializationError},
18 },
19 metadata,
20 };
21 use url::Url;
22 use web_time::SystemTime;
23
24 metadata! {
25 method: POST,
26 rate_limited: true,
27 authentication: NoAccessToken,
28 history: {
29 unstable("org.matrix.msc4108") => "/_matrix/client/unstable/org.matrix.msc4108/rendezvous",
30 }
31 }
32
33 #[derive(Debug, Default, Clone)]
35 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
36 pub struct Request {
37 pub content: String,
39 }
40
41 #[cfg(feature = "client")]
42 impl ruma_common::api::OutgoingRequest for Request {
43 type Body = BytesBody;
44 type EndpointError = Error;
45 type IncomingResponse = Response;
46
47 fn try_into_http_request_inner(
48 self,
49 base_url: &str,
50 considering: std::borrow::Cow<'_, ruma_common::api::SupportedVersions>,
51 ) -> Result<http::Request<BytesBody>, ruma_common::api::error::IntoHttpError> {
52 use http::header::CONTENT_LENGTH;
53 use ruma_common::api::Metadata;
54
55 let url = Self::make_endpoint_url(considering, base_url, &[], "")?;
56 let content_length = self.content.len();
57
58 Ok(http::Request::builder()
59 .method(Self::METHOD)
60 .uri(url)
61 .header(CONTENT_TYPE, "text/plain")
62 .header(CONTENT_LENGTH, content_length)
63 .body(BytesBody(self.content.into()))?)
64 }
65 }
66
67 #[cfg(feature = "server")]
68 impl ruma_common::api::IncomingRequest for Request {
69 type EndpointError = Error;
70 type OutgoingResponse = Response;
71
72 fn try_from_http_request<B, S>(
73 request: http::Request<B>,
74 _path_args: &[S],
75 ) -> Result<Self, ruma_common::api::error::FromHttpRequestError>
76 where
77 B: AsRef<[u8]>,
78 S: AsRef<str>,
79 {
80 const EXPECTED_CONTENT_TYPE: &str = "text/plain";
81
82 use ruma_common::api::error::DeserializationError;
83
84 Self::check_request_method(request.method())?;
85
86 let content_type = request
87 .headers()
88 .get(CONTENT_TYPE)
89 .ok_or(HeaderDeserializationError::MissingHeader(CONTENT_TYPE.to_string()))?;
90
91 let content_type = content_type.to_str()?;
92
93 if content_type != EXPECTED_CONTENT_TYPE {
94 Err(HeaderDeserializationError::InvalidHeaderValue {
95 header: CONTENT_TYPE.to_string(),
96 expected: EXPECTED_CONTENT_TYPE.to_owned(),
97 unexpected: content_type.to_owned(),
98 }
99 .into())
100 } else {
101 let body = request.into_body().as_ref().to_vec();
102 let content = String::from_utf8(body)
103 .map_err(|e| DeserializationError::Utf8(e.utf8_error()))?;
104
105 Ok(Self { content })
106 }
107 }
108 }
109
110 impl Request {
111 pub fn new(content: String) -> Self {
113 Self { content }
114 }
115 }
116
117 #[derive(Debug, Clone)]
119 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
120 pub struct Response {
121 pub url: Url,
123
124 pub etag: String,
127
128 pub expires: SystemTime,
131
132 pub last_modified: SystemTime,
135 }
136
137 #[doc(hidden)]
138 #[derive(ruma_common::serde::_FakeDeriveSerde)]
139 #[cfg_attr(feature = "server", derive(serde::Serialize, ruma_common::api::OutgoingBodyJson))]
140 #[cfg_attr(feature = "client", derive(serde::Deserialize))]
141 pub struct ResponseBody {
142 url: Url,
143 }
144
145 #[cfg(feature = "client")]
146 impl ruma_common::api::IncomingResponse for Response {
147 type EndpointError = Error;
148
149 fn try_from_http_response_inner(
150 response: http::Response<&[u8]>,
151 ) -> Result<Self, DeserializationError> {
152 let get_date = |header: http::HeaderName| -> Result<SystemTime, DeserializationError> {
153 let date = response
154 .headers()
155 .get(&header)
156 .ok_or_else(|| HeaderDeserializationError::MissingHeader(header.to_string()))?;
157
158 let date = ruma_common::http_headers::http_date_to_system_time(date)?;
159
160 Ok(date)
161 };
162
163 let etag = response
164 .headers()
165 .get(ETAG)
166 .ok_or(HeaderDeserializationError::MissingHeader(ETAG.to_string()))?
167 .to_str()?
168 .to_owned();
169 let expires = get_date(EXPIRES)?;
170 let last_modified = get_date(LAST_MODIFIED)?;
171
172 let body: ResponseBody = serde_json::from_slice(response.body())?;
173
174 Ok(Self { url: body.url, etag, expires, last_modified })
175 }
176 }
177
178 #[cfg(feature = "server")]
179 impl ruma_common::api::OutgoingResponse for Response {
180 type Body = ResponseBody;
181
182 fn try_into_http_response_inner(
183 self,
184 ) -> Result<http::Response<Self::Body>, ruma_common::api::error::IntoHttpError> {
185 use http::header::{CACHE_CONTROL, PRAGMA};
186 use ruma_common::http_headers::system_time_to_http_date;
187
188 let body = ResponseBody { url: self.url };
189
190 let expires = system_time_to_http_date(&self.expires)?;
191 let last_modified = system_time_to_http_date(&self.last_modified)?;
192
193 Ok(http::Response::builder()
194 .status(http::StatusCode::OK)
195 .header(CONTENT_TYPE, ruma_common::http_headers::APPLICATION_JSON)
196 .header(PRAGMA, "no-cache")
197 .header(CACHE_CONTROL, "no-store")
198 .header(ETAG, self.etag)
199 .header(EXPIRES, expires)
200 .header(LAST_MODIFIED, last_modified)
201 .body(body)?)
202 }
203 }
204}
205
206#[cfg(feature = "unstable-msc4388")]
207pub mod unstable_msc4388 {
208 use std::time::Duration;
212
213 use ruma_common::{
214 api::{auth_scheme::AccessTokenOptional, request, response},
215 metadata,
216 };
217
218 metadata! {
219 method: POST,
220 rate_limited: true,
221 authentication: AccessTokenOptional,
222 history: {
223 unstable => "/_matrix/client/unstable/io.element.msc4388/rendezvous",
224 }
225 }
226
227 #[request]
229 pub struct Request {
230 pub data: String,
232 }
233
234 impl Request {
235 pub fn new(data: String) -> Self {
237 Self { data }
238 }
239 }
240
241 #[response]
243 pub struct Response {
244 pub id: String,
246
247 pub sequence_token: String,
249
250 #[serde(with = "ruma_common::serde::duration::ms", rename = "expires_in_ms")]
252 pub expires_in: Duration,
253 }
254
255 impl Response {
256 pub fn new(id: String, sequence_token: String, expires_in: Duration) -> Self {
258 Self { id, sequence_token, expires_in }
259 }
260 }
261}