ruma_client_api/rendezvous/
create_rendezvous_session.rs1pub mod unstable {
6 use http::{
11 header::{CONTENT_LENGTH, CONTENT_TYPE, ETAG, EXPIRES, LAST_MODIFIED},
12 HeaderName,
13 };
14 #[cfg(feature = "client")]
15 use ruma_common::api::error::FromHttpResponseError;
16 use ruma_common::{
17 api::{error::HeaderDeserializationError, Metadata},
18 metadata,
19 };
20 use serde::{Deserialize, Serialize};
21 use url::Url;
22 use web_time::SystemTime;
23
24 metadata! {
25 method: POST,
26 rate_limited: true,
27 authentication: NoAuthentication,
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 EndpointError = crate::Error;
44 type IncomingResponse = Response;
45
46 fn try_into_http_request<T: Default + bytes::BufMut>(
47 self,
48 base_url: &str,
49 _: ruma_common::api::SendAccessToken<'_>,
50 considering: &'_ ruma_common::api::SupportedVersions,
51 ) -> Result<http::Request<T>, ruma_common::api::error::IntoHttpError> {
52 let url = Self::make_endpoint_url(considering, base_url, &[], "")?;
53 let body = self.content.as_bytes();
54 let content_length = body.len();
55
56 Ok(http::Request::builder()
57 .method(Self::METHOD)
58 .uri(url)
59 .header(CONTENT_TYPE, "text/plain")
60 .header(CONTENT_LENGTH, content_length)
61 .body(ruma_common::serde::slice_to_buf(body))?)
62 }
63 }
64
65 #[cfg(feature = "server")]
66 impl ruma_common::api::IncomingRequest for Request {
67 type EndpointError = crate::Error;
68 type OutgoingResponse = Response;
69
70 fn try_from_http_request<B, S>(
71 request: http::Request<B>,
72 _path_args: &[S],
73 ) -> Result<Self, ruma_common::api::error::FromHttpRequestError>
74 where
75 B: AsRef<[u8]>,
76 S: AsRef<str>,
77 {
78 const EXPECTED_CONTENT_TYPE: &str = "text/plain";
79
80 use ruma_common::api::error::DeserializationError;
81
82 let content_type = request
83 .headers()
84 .get(CONTENT_TYPE)
85 .ok_or(HeaderDeserializationError::MissingHeader(CONTENT_TYPE.to_string()))?;
86
87 let content_type = content_type.to_str()?;
88
89 if content_type != EXPECTED_CONTENT_TYPE {
90 Err(HeaderDeserializationError::InvalidHeaderValue {
91 header: CONTENT_TYPE.to_string(),
92 expected: EXPECTED_CONTENT_TYPE.to_owned(),
93 unexpected: content_type.to_owned(),
94 }
95 .into())
96 } else {
97 let body = request.into_body().as_ref().to_vec();
98 let content = String::from_utf8(body)
99 .map_err(|e| DeserializationError::Utf8(e.utf8_error()))?;
100
101 Ok(Self { content })
102 }
103 }
104 }
105
106 impl Request {
107 pub fn new(content: String) -> Self {
109 Self { content }
110 }
111 }
112
113 #[derive(Debug, Clone)]
115 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
116 pub struct Response {
117 pub url: Url,
119
120 pub etag: String,
123
124 pub expires: SystemTime,
127
128 pub last_modified: SystemTime,
131 }
132
133 #[derive(Serialize, Deserialize)]
134 struct ResponseBody {
135 url: Url,
136 }
137
138 #[cfg(feature = "client")]
139 impl ruma_common::api::IncomingResponse for Response {
140 type EndpointError = crate::Error;
141
142 fn try_from_http_response<T: AsRef<[u8]>>(
143 response: http::Response<T>,
144 ) -> Result<Self, FromHttpResponseError<Self::EndpointError>> {
145 use ruma_common::api::EndpointError;
146
147 if response.status().as_u16() >= 400 {
148 return Err(FromHttpResponseError::Server(
149 Self::EndpointError::from_http_response(response),
150 ));
151 }
152
153 let get_date = |header: HeaderName| -> Result<SystemTime, FromHttpResponseError<Self::EndpointError>> {
154 let date = response
155 .headers()
156 .get(&header)
157 .ok_or_else(|| HeaderDeserializationError::MissingHeader(header.to_string()))?;
158
159 let date = crate::http_headers::http_date_to_system_time(date)?;
160
161 Ok(date)
162 };
163
164 let etag = response
165 .headers()
166 .get(ETAG)
167 .ok_or(HeaderDeserializationError::MissingHeader(ETAG.to_string()))?
168 .to_str()?
169 .to_owned();
170 let expires = get_date(EXPIRES)?;
171 let last_modified = get_date(LAST_MODIFIED)?;
172
173 let body = response.into_body();
174 let body: ResponseBody = serde_json::from_slice(body.as_ref())?;
175
176 Ok(Self { url: body.url, etag, expires, last_modified })
177 }
178 }
179
180 #[cfg(feature = "server")]
181 impl ruma_common::api::OutgoingResponse for Response {
182 fn try_into_http_response<T: Default + bytes::BufMut>(
183 self,
184 ) -> Result<http::Response<T>, ruma_common::api::error::IntoHttpError> {
185 use http::header::{CACHE_CONTROL, PRAGMA};
186
187 let body = ResponseBody { url: self.url.clone() };
188 let body = serde_json::to_vec(&body)?;
189 let body = ruma_common::serde::slice_to_buf(&body);
190
191 let expires = crate::http_headers::system_time_to_http_date(&self.expires)?;
192 let last_modified = crate::http_headers::system_time_to_http_date(&self.last_modified)?;
193
194 Ok(http::Response::builder()
195 .status(http::StatusCode::OK)
196 .header(CONTENT_TYPE, "application/json")
197 .header(PRAGMA, "no-cache")
198 .header(CACHE_CONTROL, "no-store")
199 .header(ETAG, self.etag)
200 .header(EXPIRES, expires)
201 .header(LAST_MODIFIED, last_modified)
202 .body(body)?)
203 }
204 }
205}