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