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