ruma_client_api/uiaa/
get_uiaa_fallback_page.rs1pub mod v3 {
6 use ruma_common::{api::request, metadata};
11
12 use crate::uiaa::AuthType;
13
14 metadata! {
15 method: GET,
16 rate_limited: false,
17 authentication: NoAuthentication,
18 history: {
19 1.0 => "/_matrix/client/r0/auth/{auth_type}/fallback/web",
20 1.1 => "/_matrix/client/v3/auth/{auth_type}/fallback/web",
21 }
22 }
23
24 #[request(error = crate::Error)]
26 pub struct Request {
27 #[ruma_api(path)]
29 pub auth_type: AuthType,
30
31 #[ruma_api(query)]
33 pub session: String,
34 }
35
36 impl Request {
37 pub fn new(auth_type: AuthType, session: String) -> Self {
39 Self { auth_type, session }
40 }
41 }
42
43 #[derive(Debug, Clone)]
45 #[allow(clippy::exhaustive_enums)]
46 pub enum Response {
47 Redirect(Redirect),
49
50 Html(HtmlPage),
52 }
53
54 #[derive(Debug, Clone)]
56 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
57 pub struct Redirect {
58 pub url: String,
60 }
61
62 #[derive(Debug, Clone)]
64 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
65 pub struct HtmlPage {
66 pub body: Vec<u8>,
68 }
69
70 impl Response {
71 pub fn html(body: Vec<u8>) -> Self {
73 Self::Html(HtmlPage { body })
74 }
75
76 pub fn redirect(url: String) -> Self {
78 Self::Redirect(Redirect { url })
79 }
80 }
81
82 #[cfg(feature = "server")]
83 impl ruma_common::api::OutgoingResponse for Response {
84 fn try_into_http_response<T: Default + bytes::BufMut>(
85 self,
86 ) -> Result<http::Response<T>, ruma_common::api::error::IntoHttpError> {
87 match self {
88 Response::Redirect(Redirect { url }) => Ok(http::Response::builder()
89 .status(http::StatusCode::FOUND)
90 .header(http::header::LOCATION, url)
91 .body(T::default())?),
92 Response::Html(HtmlPage { body }) => Ok(http::Response::builder()
93 .status(http::StatusCode::OK)
94 .header(http::header::CONTENT_TYPE, "text/html; charset=utf-8")
95 .body(ruma_common::serde::slice_to_buf(&body))?),
96 }
97 }
98 }
99
100 #[cfg(feature = "client")]
101 impl ruma_common::api::IncomingResponse for Response {
102 type EndpointError = crate::Error;
103
104 fn try_from_http_response<T: AsRef<[u8]>>(
105 response: http::Response<T>,
106 ) -> Result<Self, ruma_common::api::error::FromHttpResponseError<Self::EndpointError>>
107 {
108 use ruma_common::api::{
109 error::{DeserializationError, FromHttpResponseError, HeaderDeserializationError},
110 EndpointError,
111 };
112
113 if response.status().as_u16() >= 400 {
114 return Err(FromHttpResponseError::Server(
115 Self::EndpointError::from_http_response(response),
116 ));
117 }
118
119 if response.status() == http::StatusCode::FOUND {
120 let Some(location) = response.headers().get(http::header::LOCATION) else {
121 return Err(DeserializationError::Header(
122 HeaderDeserializationError::MissingHeader(
123 http::header::LOCATION.to_string(),
124 ),
125 )
126 .into());
127 };
128
129 let url = location.to_str()?;
130 return Ok(Self::Redirect(Redirect { url: url.to_owned() }));
131 }
132
133 let body = response.into_body().as_ref().to_owned();
134 Ok(Self::Html(HtmlPage { body }))
135 }
136 }
137
138 #[cfg(all(test, any(feature = "client", feature = "server")))]
139 mod tests {
140 use assert_matches2::assert_matches;
141 use http::header::{CONTENT_TYPE, LOCATION};
142 #[cfg(feature = "client")]
143 use ruma_common::api::IncomingResponse;
144 #[cfg(feature = "server")]
145 use ruma_common::api::OutgoingResponse;
146
147 use super::Response;
148
149 #[cfg(feature = "client")]
150 #[test]
151 fn incoming_redirect() {
152 use super::Redirect;
153
154 let http_response = http::Response::builder()
155 .status(http::StatusCode::FOUND)
156 .header(LOCATION, "http://localhost/redirect")
157 .body(Vec::<u8>::new())
158 .unwrap();
159
160 let response = Response::try_from_http_response(http_response).unwrap();
161 assert_matches!(response, Response::Redirect(Redirect { url }));
162 assert_eq!(url, "http://localhost/redirect");
163 }
164
165 #[cfg(feature = "client")]
166 #[test]
167 fn incoming_html() {
168 use super::HtmlPage;
169
170 let http_response = http::Response::builder()
171 .status(http::StatusCode::OK)
172 .header(CONTENT_TYPE, "text/html; charset=utf-8")
173 .body(b"<h1>My Page</h1>")
174 .unwrap();
175
176 let response = Response::try_from_http_response(http_response).unwrap();
177 assert_matches!(response, Response::Html(HtmlPage { body }));
178 assert_eq!(body, b"<h1>My Page</h1>");
179 }
180
181 #[cfg(feature = "server")]
182 #[test]
183 fn outgoing_redirect() {
184 let response = Response::redirect("http://localhost/redirect".to_owned());
185
186 let http_response = response.try_into_http_response::<Vec<u8>>().unwrap();
187
188 assert_eq!(http_response.status(), http::StatusCode::FOUND);
189 assert_eq!(
190 http_response.headers().get(LOCATION).unwrap().to_str().unwrap(),
191 "http://localhost/redirect"
192 );
193 assert!(http_response.into_body().is_empty());
194 }
195
196 #[cfg(feature = "server")]
197 #[test]
198 fn outgoing_html() {
199 let response = Response::html(b"<h1>My Page</h1>".to_vec());
200
201 let http_response = response.try_into_http_response::<Vec<u8>>().unwrap();
202
203 assert_eq!(http_response.status(), http::StatusCode::OK);
204 assert_eq!(
205 http_response.headers().get(CONTENT_TYPE).unwrap().to_str().unwrap(),
206 "text/html; charset=utf-8"
207 );
208 assert_eq!(http_response.into_body(), b"<h1>My Page</h1>");
209 }
210 }
211}