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::{auth_scheme::NoAuthentication, 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::auth_scheme::SendAccessToken<'_>,
50 considering: std::borrow::Cow<'_, 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 Self::check_request_method(request.method())?;
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: ResponseBody = serde_json::from_slice(response.body().as_ref())?;
176
177 Ok(Self { url: body.url, etag, expires, last_modified })
178 }
179 }
180
181 #[cfg(feature = "server")]
182 impl ruma_common::api::OutgoingResponse for Response {
183 fn try_into_http_response<T: Default + bytes::BufMut>(
184 self,
185 ) -> Result<http::Response<T>, ruma_common::api::error::IntoHttpError> {
186 use http::header::{CACHE_CONTROL, PRAGMA};
187
188 let body = ResponseBody { url: self.url };
189 let body = ruma_common::serde::json_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, ruma_common::http_headers::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}