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 const METADATA: Metadata = metadata! {
25 method: POST,
26 rate_limited: true,
27 authentication: None,
28 history: {
29 unstable => "/_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 const METADATA: Metadata = METADATA;
46
47 fn try_into_http_request<T: Default + bytes::BufMut>(
48 self,
49 base_url: &str,
50 _: ruma_common::api::SendAccessToken<'_>,
51 considering_versions: &'_ [ruma_common::api::MatrixVersion],
52 ) -> Result<http::Request<T>, ruma_common::api::error::IntoHttpError> {
53 let url = METADATA.make_endpoint_url(considering_versions, base_url, &[], "")?;
54 let body = self.content.as_bytes();
55 let content_length = body.len();
56
57 Ok(http::Request::builder()
58 .method(METADATA.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 const METADATA: Metadata = METADATA;
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 let content_type = request
85 .headers()
86 .get(CONTENT_TYPE)
87 .ok_or(HeaderDeserializationError::MissingHeader(CONTENT_TYPE.to_string()))?;
88
89 let content_type = content_type.to_str()?;
90
91 if content_type != EXPECTED_CONTENT_TYPE {
92 Err(HeaderDeserializationError::InvalidHeaderValue {
93 header: CONTENT_TYPE.to_string(),
94 expected: EXPECTED_CONTENT_TYPE.to_owned(),
95 unexpected: content_type.to_owned(),
96 }
97 .into())
98 } else {
99 let body = request.into_body().as_ref().to_vec();
100 let content = String::from_utf8(body)
101 .map_err(|e| DeserializationError::Utf8(e.utf8_error()))?;
102
103 Ok(Self { content })
104 }
105 }
106 }
107
108 impl Request {
109 pub fn new(content: String) -> Self {
111 Self { content }
112 }
113 }
114
115 #[derive(Debug, Clone)]
117 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
118 pub struct Response {
119 pub url: Url,
121
122 pub etag: String,
125
126 pub expires: SystemTime,
129
130 pub last_modified: SystemTime,
133 }
134
135 #[derive(Serialize, Deserialize)]
136 struct ResponseBody {
137 url: Url,
138 }
139
140 #[cfg(feature = "client")]
141 impl ruma_common::api::IncomingResponse for Response {
142 type EndpointError = crate::Error;
143
144 fn try_from_http_response<T: AsRef<[u8]>>(
145 response: http::Response<T>,
146 ) -> Result<Self, FromHttpResponseError<Self::EndpointError>> {
147 use ruma_common::api::EndpointError;
148
149 if response.status().as_u16() >= 400 {
150 return Err(FromHttpResponseError::Server(
151 Self::EndpointError::from_http_response(response),
152 ));
153 }
154
155 let get_date = |header: HeaderName| -> Result<SystemTime, FromHttpResponseError<Self::EndpointError>> {
156 let date = response
157 .headers()
158 .get(&header)
159 .ok_or_else(|| HeaderDeserializationError::MissingHeader(header.to_string()))?;
160
161 let date = crate::http_headers::http_date_to_system_time(date)?;
162
163 Ok(date)
164 };
165
166 let etag = response
167 .headers()
168 .get(ETAG)
169 .ok_or(HeaderDeserializationError::MissingHeader(ETAG.to_string()))?
170 .to_str()?
171 .to_owned();
172 let expires = get_date(EXPIRES)?;
173 let last_modified = get_date(LAST_MODIFIED)?;
174
175 let body = response.into_body();
176 let body: ResponseBody = serde_json::from_slice(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.clone() };
190 let body = serde_json::to_vec(&body)?;
191 let body = ruma_common::serde::slice_to_buf(&body);
192
193 let expires = crate::http_headers::system_time_to_http_date(&self.expires)?;
194 let last_modified = crate::http_headers::system_time_to_http_date(&self.last_modified)?;
195
196 Ok(http::Response::builder()
197 .status(http::StatusCode::OK)
198 .header(CONTENT_TYPE, "application/json")
199 .header(PRAGMA, "no-cache")
200 .header(CACHE_CONTROL, "no-store")
201 .header(ETAG, self.etag)
202 .header(EXPIRES, expires)
203 .header(LAST_MODIFIED, last_modified)
204 .body(body)?)
205 }
206 }
207}