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::error::FromHttpResponseError;
14 use ruma_common::{
15 api::{
16 auth_scheme::NoAccessToken,
17 error::{Error, HeaderDeserializationError},
18 },
19 metadata,
20 };
21 use serde::{Deserialize, Serialize};
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 EndpointError = Error;
45 type IncomingResponse = Response;
46
47 fn try_into_http_request<T: Default + bytes::BufMut>(
48 self,
49 base_url: &str,
50 _: ruma_common::api::auth_scheme::SendAccessToken<'_>,
51 considering: std::borrow::Cow<'_, ruma_common::api::SupportedVersions>,
52 ) -> Result<http::Request<T>, 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 body = self.content.as_bytes();
58 let content_length = body.len();
59
60 Ok(http::Request::builder()
61 .method(Self::METHOD)
62 .uri(url)
63 .header(CONTENT_TYPE, "text/plain")
64 .header(CONTENT_LENGTH, content_length)
65 .body(ruma_common::serde::slice_to_buf(body))?)
66 }
67 }
68
69 #[cfg(feature = "server")]
70 impl ruma_common::api::IncomingRequest for Request {
71 type EndpointError = Error;
72 type OutgoingResponse = Response;
73
74 fn try_from_http_request<B, S>(
75 request: http::Request<B>,
76 _path_args: &[S],
77 ) -> Result<Self, ruma_common::api::error::FromHttpRequestError>
78 where
79 B: AsRef<[u8]>,
80 S: AsRef<str>,
81 {
82 const EXPECTED_CONTENT_TYPE: &str = "text/plain";
83
84 use ruma_common::api::error::DeserializationError;
85
86 Self::check_request_method(request.method())?;
87
88 let content_type = request
89 .headers()
90 .get(CONTENT_TYPE)
91 .ok_or(HeaderDeserializationError::MissingHeader(CONTENT_TYPE.to_string()))?;
92
93 let content_type = content_type.to_str()?;
94
95 if content_type != EXPECTED_CONTENT_TYPE {
96 Err(HeaderDeserializationError::InvalidHeaderValue {
97 header: CONTENT_TYPE.to_string(),
98 expected: EXPECTED_CONTENT_TYPE.to_owned(),
99 unexpected: content_type.to_owned(),
100 }
101 .into())
102 } else {
103 let body = request.into_body().as_ref().to_vec();
104 let content = String::from_utf8(body)
105 .map_err(|e| DeserializationError::Utf8(e.utf8_error()))?;
106
107 Ok(Self { content })
108 }
109 }
110 }
111
112 impl Request {
113 pub fn new(content: String) -> Self {
115 Self { content }
116 }
117 }
118
119 #[derive(Debug, Clone)]
121 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
122 pub struct Response {
123 pub url: Url,
125
126 pub etag: String,
129
130 pub expires: SystemTime,
133
134 pub last_modified: SystemTime,
137 }
138
139 #[derive(Serialize, Deserialize)]
140 struct ResponseBody {
141 url: Url,
142 }
143
144 #[cfg(feature = "client")]
145 impl ruma_common::api::IncomingResponse for Response {
146 type EndpointError = Error;
147
148 fn try_from_http_response<T: AsRef<[u8]>>(
149 response: http::Response<T>,
150 ) -> Result<Self, FromHttpResponseError<Self::EndpointError>> {
151 use ruma_common::api::EndpointError;
152
153 if response.status().as_u16() >= 400 {
154 return Err(FromHttpResponseError::Server(
155 Self::EndpointError::from_http_response(response),
156 ));
157 }
158
159 let get_date = |header: http::HeaderName| -> Result<SystemTime, FromHttpResponseError<Self::EndpointError>> {
160 let date = response
161 .headers()
162 .get(&header)
163 .ok_or_else(|| HeaderDeserializationError::MissingHeader(header.to_string()))?;
164
165 let date = ruma_common::http_headers::http_date_to_system_time(date)?;
166
167 Ok(date)
168 };
169
170 let etag = response
171 .headers()
172 .get(ETAG)
173 .ok_or(HeaderDeserializationError::MissingHeader(ETAG.to_string()))?
174 .to_str()?
175 .to_owned();
176 let expires = get_date(EXPIRES)?;
177 let last_modified = get_date(LAST_MODIFIED)?;
178
179 let body: ResponseBody = serde_json::from_slice(response.body().as_ref())?;
180
181 Ok(Self { url: body.url, etag, expires, last_modified })
182 }
183 }
184
185 #[cfg(feature = "server")]
186 impl ruma_common::api::OutgoingResponse for Response {
187 fn try_into_http_response<T: Default + bytes::BufMut>(
188 self,
189 ) -> Result<http::Response<T>, ruma_common::api::error::IntoHttpError> {
190 use http::header::{CACHE_CONTROL, PRAGMA};
191 use ruma_common::http_headers::system_time_to_http_date;
192
193 let body = ResponseBody { url: self.url };
194 let body = ruma_common::serde::json_to_buf(&body)?;
195
196 let expires = system_time_to_http_date(&self.expires)?;
197 let last_modified = system_time_to_http_date(&self.last_modified)?;
198
199 Ok(http::Response::builder()
200 .status(http::StatusCode::OK)
201 .header(CONTENT_TYPE, ruma_common::http_headers::APPLICATION_JSON)
202 .header(PRAGMA, "no-cache")
203 .header(CACHE_CONTROL, "no-store")
204 .header(ETAG, self.etag)
205 .header(EXPIRES, expires)
206 .header(LAST_MODIFIED, last_modified)
207 .body(body)?)
208 }
209 }
210}
211
212#[cfg(feature = "unstable-msc4388")]
213pub mod unstable_msc4388 {
214 use std::time::Duration;
218
219 use ruma_common::{
220 api::{auth_scheme::AccessTokenOptional, request, response},
221 metadata,
222 };
223
224 metadata! {
225 method: POST,
226 rate_limited: true,
227 authentication: AccessTokenOptional,
228 history: {
229 unstable => "/_matrix/client/unstable/io.element.msc4388/rendezvous",
230 }
231 }
232
233 #[request]
235 pub struct Request {
236 pub data: String,
238 }
239
240 impl Request {
241 pub fn new(data: String) -> Self {
243 Self { data }
244 }
245 }
246
247 #[response]
249 pub struct Response {
250 pub id: String,
252
253 pub sequence_token: String,
255
256 #[serde(with = "ruma_common::serde::duration::ms", rename = "expires_in_ms")]
258 pub expires_in: Duration,
259 }
260
261 impl Response {
262 pub fn new(id: String, sequence_token: String, expires_in: Duration) -> Self {
264 Self { id, sequence_token, expires_in }
265 }
266 }
267}