pub mod v3 {
use ruma_common::{
api::{auth_scheme::NoAccessToken, request},
metadata,
};
use crate::uiaa::AuthType;
metadata! {
method: GET,
rate_limited: false,
authentication: NoAccessToken,
history: {
1.0 => "/_matrix/client/r0/auth/{auth_type}/fallback/web",
1.1 => "/_matrix/client/v3/auth/{auth_type}/fallback/web",
}
}
#[request]
pub struct Request {
#[ruma_api(path)]
pub auth_type: AuthType,
#[ruma_api(query)]
pub session: String,
}
impl Request {
pub fn new(auth_type: AuthType, session: String) -> Self {
Self { auth_type, session }
}
}
#[derive(Debug, Clone)]
#[allow(clippy::exhaustive_enums)]
pub enum Response {
Redirect(Redirect),
Html(HtmlPage),
}
#[derive(Debug, Clone)]
#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
pub struct Redirect {
pub url: String,
}
#[derive(Debug, Clone)]
#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
pub struct HtmlPage {
pub body: Vec<u8>,
}
impl Response {
pub fn html(body: Vec<u8>) -> Self {
Self::Html(HtmlPage { body })
}
pub fn redirect(url: String) -> Self {
Self::Redirect(Redirect { url })
}
}
#[doc(hidden)]
#[allow(clippy::exhaustive_enums)]
pub enum ResponseBody {
Redirect,
Html(HtmlPage),
}
#[cfg(feature = "server")]
impl ruma_common::api::OutgoingBody for ResponseBody {
type Error = ruma_common::api::error::IntoHttpError;
fn content_type(&self) -> Option<http::HeaderValue> {
match self {
Self::Redirect => None,
Self::Html(_) => Some(ruma_common::http_headers::TEXT_HTML_UTF8),
}
}
fn try_into_buf<T: Default + bytes::BufMut + AsRef<[u8]>>(self) -> Result<T, Self::Error> {
let body = match self {
Self::Redirect => Vec::new(),
Self::Html(HtmlPage { body }) => body,
};
Ok(ruma_common::api::BytesBody(body).try_into_buf()?)
}
}
#[cfg(feature = "server")]
impl ruma_common::api::OutgoingResponse for Response {
type Body = ResponseBody;
fn try_into_http_response_inner(
self,
) -> Result<http::Response<Self::Body>, ruma_common::api::error::IntoHttpError> {
match self {
Response::Redirect(Redirect { url }) => Ok(http::Response::builder()
.status(http::StatusCode::FOUND)
.header(http::header::LOCATION, url)
.body(ResponseBody::Redirect)?),
Response::Html(html) => Ok(http::Response::builder()
.status(http::StatusCode::OK)
.body(ResponseBody::Html(html))?),
}
}
}
#[cfg(feature = "client")]
impl ruma_common::api::IncomingResponse for Response {
type EndpointError = ruma_common::api::error::Error;
fn try_from_http_response_inner(
response: http::Response<&[u8]>,
) -> Result<Self, ruma_common::api::error::DeserializationError> {
use ruma_common::api::error::HeaderDeserializationError;
if response.status() == http::StatusCode::FOUND {
let Some(location) = response.headers().get(http::header::LOCATION) else {
return Err(HeaderDeserializationError::MissingHeader(
http::header::LOCATION.to_string(),
)
.into());
};
let url = location.to_str()?;
return Ok(Self::Redirect(Redirect { url: url.to_owned() }));
}
let body = response.into_body().to_owned();
Ok(Self::Html(HtmlPage { body }))
}
}
#[cfg(all(test, feature = "client"))]
mod tests_client {
use assert_matches2::assert_let;
use http::header::{CONTENT_TYPE, LOCATION};
use ruma_common::api::IncomingResponseExt as _;
use super::Response;
#[test]
fn incoming_redirect() {
use super::Redirect;
let http_response = http::Response::builder()
.status(http::StatusCode::FOUND)
.header(LOCATION, "http://localhost/redirect")
.body(b"".as_slice())
.unwrap();
let response = Response::try_from_http_response(http_response).unwrap();
assert_let!(Response::Redirect(Redirect { url }) = response);
assert_eq!(url, "http://localhost/redirect");
}
#[test]
fn incoming_html() {
use super::HtmlPage;
let http_response = http::Response::builder()
.status(http::StatusCode::OK)
.header(CONTENT_TYPE, ruma_common::http_headers::TEXT_HTML_UTF8)
.body(b"<h1>My Page</h1>".as_slice())
.unwrap();
let response = Response::try_from_http_response(http_response).unwrap();
assert_let!(Response::Html(HtmlPage { body }) = response);
assert_eq!(body, b"<h1>My Page</h1>");
}
}
#[cfg(all(test, feature = "server"))]
mod tests_server {
use http::header::{CONTENT_TYPE, LOCATION};
use ruma_common::api::OutgoingResponseExt as _;
use super::Response;
#[test]
fn outgoing_redirect() {
let response = Response::redirect("http://localhost/redirect".to_owned());
let http_response = response.try_into_http_response::<Vec<u8>>().unwrap();
assert_eq!(http_response.status(), http::StatusCode::FOUND);
assert_eq!(
http_response.headers().get(LOCATION).unwrap().to_str().unwrap(),
"http://localhost/redirect"
);
assert!(http_response.into_body().is_empty());
}
#[test]
fn outgoing_html() {
let response = Response::html(b"<h1>My Page</h1>".to_vec());
let http_response = response.try_into_http_response::<Vec<u8>>().unwrap();
assert_eq!(http_response.status(), http::StatusCode::OK);
assert_eq!(
http_response.headers().get(CONTENT_TYPE).unwrap(),
ruma_common::http_headers::TEXT_HTML_UTF8
);
assert_eq!(http_response.into_body(), b"<h1>My Page</h1>");
}
}
}