ruma_client_api/uiaa/
get_uiaa_fallback_page.rs1pub mod v3 {
6 use ruma_common::{
11 api::{auth_scheme::NoAccessToken, request},
12 metadata,
13 };
14
15 use crate::uiaa::AuthType;
16
17 metadata! {
18 method: GET,
19 rate_limited: false,
20 authentication: NoAccessToken,
21 history: {
22 1.0 => "/_matrix/client/r0/auth/{auth_type}/fallback/web",
23 1.1 => "/_matrix/client/v3/auth/{auth_type}/fallback/web",
24 }
25 }
26
27 #[request]
29 pub struct Request {
30 #[ruma_api(path)]
32 pub auth_type: AuthType,
33
34 #[ruma_api(query)]
36 pub session: String,
37 }
38
39 impl Request {
40 pub fn new(auth_type: AuthType, session: String) -> Self {
42 Self { auth_type, session }
43 }
44 }
45
46 #[derive(Debug, Clone)]
48 #[allow(clippy::exhaustive_enums)]
49 pub enum Response {
50 Redirect(Redirect),
52
53 Html(HtmlPage),
55 }
56
57 #[derive(Debug, Clone)]
59 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
60 pub struct Redirect {
61 pub url: String,
63 }
64
65 #[derive(Debug, Clone)]
67 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
68 pub struct HtmlPage {
69 pub body: Vec<u8>,
71 }
72
73 impl Response {
74 pub fn html(body: Vec<u8>) -> Self {
76 Self::Html(HtmlPage { body })
77 }
78
79 pub fn redirect(url: String) -> Self {
81 Self::Redirect(Redirect { url })
82 }
83 }
84
85 #[doc(hidden)]
86 #[allow(clippy::exhaustive_enums)]
87 pub enum ResponseBody {
88 Redirect,
89 Html(HtmlPage),
90 }
91
92 #[cfg(feature = "server")]
93 impl ruma_common::api::OutgoingBody for ResponseBody {
94 type Error = ruma_common::api::error::IntoHttpError;
95
96 fn content_type(&self) -> Option<http::HeaderValue> {
97 match self {
98 Self::Redirect => None,
99 Self::Html(_) => Some(ruma_common::http_headers::TEXT_HTML_UTF8),
100 }
101 }
102
103 fn try_into_buf<T: Default + bytes::BufMut + AsRef<[u8]>>(self) -> Result<T, Self::Error> {
104 let body = match self {
105 Self::Redirect => Vec::new(),
106 Self::Html(HtmlPage { body }) => body,
107 };
108
109 Ok(ruma_common::api::BytesBody(body).try_into_buf()?)
110 }
111 }
112
113 #[cfg(feature = "server")]
114 impl ruma_common::api::OutgoingResponse for Response {
115 type Body = ResponseBody;
116
117 fn try_into_http_response_inner(
118 self,
119 ) -> Result<http::Response<Self::Body>, ruma_common::api::error::IntoHttpError> {
120 match self {
121 Response::Redirect(Redirect { url }) => Ok(http::Response::builder()
122 .status(http::StatusCode::FOUND)
123 .header(http::header::LOCATION, url)
124 .body(ResponseBody::Redirect)?),
125 Response::Html(html) => Ok(http::Response::builder()
126 .status(http::StatusCode::OK)
127 .body(ResponseBody::Html(html))?),
128 }
129 }
130 }
131
132 #[cfg(feature = "client")]
133 impl ruma_common::api::IncomingResponse for Response {
134 type EndpointError = ruma_common::api::error::Error;
135
136 fn try_from_http_response_inner(
137 response: http::Response<&[u8]>,
138 ) -> Result<Self, ruma_common::api::error::DeserializationError> {
139 use ruma_common::api::error::HeaderDeserializationError;
140
141 if response.status() == http::StatusCode::FOUND {
142 let Some(location) = response.headers().get(http::header::LOCATION) else {
143 return Err(HeaderDeserializationError::MissingHeader(
144 http::header::LOCATION.to_string(),
145 )
146 .into());
147 };
148
149 let url = location.to_str()?;
150 return Ok(Self::Redirect(Redirect { url: url.to_owned() }));
151 }
152
153 let body = response.into_body().to_owned();
154 Ok(Self::Html(HtmlPage { body }))
155 }
156 }
157
158 #[cfg(all(test, feature = "client"))]
159 mod tests_client {
160 use assert_matches2::assert_let;
161 use http::header::{CONTENT_TYPE, LOCATION};
162 use ruma_common::api::IncomingResponseExt as _;
163
164 use super::Response;
165
166 #[test]
167 fn incoming_redirect() {
168 use super::Redirect;
169
170 let http_response = http::Response::builder()
171 .status(http::StatusCode::FOUND)
172 .header(LOCATION, "http://localhost/redirect")
173 .body(b"".as_slice())
174 .unwrap();
175
176 let response = Response::try_from_http_response(http_response).unwrap();
177 assert_let!(Response::Redirect(Redirect { url }) = response);
178 assert_eq!(url, "http://localhost/redirect");
179 }
180
181 #[test]
182 fn incoming_html() {
183 use super::HtmlPage;
184
185 let http_response = http::Response::builder()
186 .status(http::StatusCode::OK)
187 .header(CONTENT_TYPE, ruma_common::http_headers::TEXT_HTML_UTF8)
188 .body(b"<h1>My Page</h1>".as_slice())
189 .unwrap();
190
191 let response = Response::try_from_http_response(http_response).unwrap();
192 assert_let!(Response::Html(HtmlPage { body }) = response);
193 assert_eq!(body, b"<h1>My Page</h1>");
194 }
195 }
196
197 #[cfg(all(test, feature = "server"))]
198 mod tests_server {
199 use http::header::{CONTENT_TYPE, LOCATION};
200 use ruma_common::api::OutgoingResponseExt as _;
201
202 use super::Response;
203
204 #[test]
205 fn outgoing_redirect() {
206 let response = Response::redirect("http://localhost/redirect".to_owned());
207
208 let http_response = response.try_into_http_response::<Vec<u8>>().unwrap();
209
210 assert_eq!(http_response.status(), http::StatusCode::FOUND);
211 assert_eq!(
212 http_response.headers().get(LOCATION).unwrap().to_str().unwrap(),
213 "http://localhost/redirect"
214 );
215 assert!(http_response.into_body().is_empty());
216 }
217
218 #[test]
219 fn outgoing_html() {
220 let response = Response::html(b"<h1>My Page</h1>".to_vec());
221
222 let http_response = response.try_into_http_response::<Vec<u8>>().unwrap();
223
224 assert_eq!(http_response.status(), http::StatusCode::OK);
225 assert_eq!(
226 http_response.headers().get(CONTENT_TYPE).unwrap(),
227 ruma_common::http_headers::TEXT_HTML_UTF8
228 );
229 assert_eq!(http_response.into_body(), b"<h1>My Page</h1>");
230 }
231 }
232}