tuwunel_core/error/
response.rs1use bytes::BytesMut;
2use http::StatusCode;
3use http_body_util::Full;
4use ruma::{
5 ServerName,
6 api::{
7 OutgoingResponse,
8 client::uiaa::UiaaResponse,
9 error::{Error as RumaError, ErrorBody, ErrorKind, StandardErrorBody},
10 },
11};
12
13use super::Error;
14use crate::error;
15
16impl axum::response::IntoResponse for Error {
17 fn into_response(self) -> axum::response::Response {
18 let response: UiaaResponse = self.into();
19 response
20 .try_into_http_response::<BytesMut>()
21 .inspect_err(|e| error!("error response error: {e}"))
22 .map_or_else(
23 |_| StatusCode::INTERNAL_SERVER_ERROR.into_response(),
24 |r| {
25 r.map(BytesMut::freeze)
26 .map(Full::new)
27 .into_response()
28 },
29 )
30 }
31}
32
33impl From<Error> for UiaaResponse {
34 #[inline]
35 fn from(error: Error) -> Self {
36 if let Error::Uiaa(uiaainfo) = error {
37 return Self::AuthResponse(uiaainfo);
38 }
39
40 let status = match &error {
41 | Error::Federation(origin, remote) if !is_relayable(ruma_error_kind(remote)) =>
42 return withheld_remote_error(origin),
43
44 | Error::Federation(..) if error.status_code() == StatusCode::UNAUTHORIZED =>
46 StatusCode::BAD_REQUEST,
47
48 | _ => error.status_code(),
49 };
50
51 matrix_response(status, error.kind(), error.message())
52 }
53}
54
55fn is_relayable(kind: &ErrorKind) -> bool {
61 use ErrorKind::*;
62
63 matches!(
64 kind,
65 Forbidden
66 | NotFound
67 | UnsupportedRoomVersion
68 | IncompatibleRoomVersion(..)
69 | InviteBlocked
70 | UnableToAuthorizeJoin
71 | UnableToGrantJoin
72 )
73}
74
75fn withheld_remote_error(origin: &ServerName) -> UiaaResponse {
76 let message = format!("Request to {origin} failed.");
77
78 matrix_response(StatusCode::BAD_GATEWAY, ErrorKind::Unknown, message)
79}
80
81fn matrix_response(status: StatusCode, kind: ErrorKind, message: String) -> UiaaResponse {
82 let body = ErrorBody::Standard(StandardErrorBody { kind, message });
83
84 UiaaResponse::MatrixError(RumaError::new(status, body))
85}
86
87pub(super) fn status_code(kind: &ErrorKind, hint: StatusCode) -> StatusCode {
88 if hint == StatusCode::BAD_REQUEST {
89 bad_request_code(kind)
90 } else {
91 hint
92 }
93}
94
95pub(super) fn bad_request_code(kind: &ErrorKind) -> StatusCode {
96 use ErrorKind::*;
97
98 match kind {
99 | NotYetUploaded | ConnectionTimeout => StatusCode::GATEWAY_TIMEOUT,
101
102 | BadStatus(..) | ConnectionFailed => StatusCode::BAD_GATEWAY,
104
105 | LimitExceeded { .. } => StatusCode::TOO_MANY_REQUESTS,
107
108 | TooLarge => StatusCode::PAYLOAD_TOO_LARGE,
110
111 | CannotOverwriteMedia => StatusCode::CONFLICT,
113
114 | NotFound | NotImplemented | FeatureDisabled | Unrecognized => StatusCode::NOT_FOUND,
116
117 | GuestAccessForbidden
119 | ThreepidAuthFailed
120 | UserDeactivated
121 | ThreepidDenied
122 | InviteBlocked
123 | WrongRoomKeysVersion { .. }
124 | Forbidden => StatusCode::FORBIDDEN,
125
126 | UnknownToken { .. } | MissingToken | Unauthorized => StatusCode::UNAUTHORIZED,
128
129 | _ => StatusCode::BAD_REQUEST,
131 }
132}
133
134pub(super) fn ruma_error_message(error: &RumaError) -> String {
135 if let ErrorBody::Standard(StandardErrorBody { message, .. }) = &error.body {
136 return message.clone();
137 }
138
139 format!("{error}")
140}
141
142pub(super) fn ruma_error_kind(e: &RumaError) -> &ErrorKind {
143 e.error_kind().unwrap_or(&ErrorKind::Unknown)
144}
145
146pub(super) fn io_error_code(kind: std::io::ErrorKind) -> StatusCode {
147 use std::io::ErrorKind;
148
149 match kind {
150 | ErrorKind::InvalidInput => StatusCode::BAD_REQUEST,
151 | ErrorKind::PermissionDenied => StatusCode::FORBIDDEN,
152 | ErrorKind::NotFound => StatusCode::NOT_FOUND,
153 | ErrorKind::TimedOut => StatusCode::GATEWAY_TIMEOUT,
154 | ErrorKind::FileTooLarge => StatusCode::PAYLOAD_TOO_LARGE,
155 | ErrorKind::StorageFull => StatusCode::INSUFFICIENT_STORAGE,
156 | ErrorKind::Interrupted => StatusCode::SERVICE_UNAVAILABLE,
157 | _ => StatusCode::INTERNAL_SERVER_ERROR,
158 }
159}