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 _ = status
52 .is_server_error()
53 .then(|| error!(?error, "request failed"));
54
55 matrix_response(status, error.kind(), error.sanitized_message())
56 }
57}
58
59fn is_relayable(kind: &ErrorKind) -> bool {
65 use ErrorKind::*;
66
67 matches!(
68 kind,
69 Forbidden
70 | NotFound
71 | UnsupportedRoomVersion
72 | IncompatibleRoomVersion(..)
73 | InviteBlocked
74 | UnableToAuthorizeJoin
75 | UnableToGrantJoin
76 )
77}
78
79fn withheld_remote_error(origin: &ServerName) -> UiaaResponse {
80 let message = format!("Request to {origin} failed.");
81
82 matrix_response(StatusCode::BAD_GATEWAY, ErrorKind::Unknown, message)
83}
84
85fn matrix_response(status: StatusCode, kind: ErrorKind, message: String) -> UiaaResponse {
86 let body = ErrorBody::Standard(StandardErrorBody { kind, message });
87
88 UiaaResponse::MatrixError(RumaError::new(status, body))
89}
90
91pub(super) fn status_code(kind: &ErrorKind, hint: StatusCode) -> StatusCode {
92 if hint == StatusCode::BAD_REQUEST {
93 bad_request_code(kind)
94 } else {
95 hint
96 }
97}
98
99pub(super) fn bad_request_code(kind: &ErrorKind) -> StatusCode {
100 use ErrorKind::*;
101
102 match kind {
103 | NotYetUploaded | ConnectionTimeout => StatusCode::GATEWAY_TIMEOUT,
105
106 | BadStatus(..) | ConnectionFailed => StatusCode::BAD_GATEWAY,
108
109 | LimitExceeded { .. } => StatusCode::TOO_MANY_REQUESTS,
111
112 | TooLarge => StatusCode::PAYLOAD_TOO_LARGE,
114
115 | CannotOverwriteMedia => StatusCode::CONFLICT,
117
118 | NotFound | NotImplemented | FeatureDisabled | Unrecognized => StatusCode::NOT_FOUND,
120
121 | GuestAccessForbidden
123 | ThreepidAuthFailed
124 | UserDeactivated
125 | UserSuspended
126 | ThreepidDenied
127 | InviteBlocked
128 | CannotLeaveServerNoticeRoom
129 | WrongRoomKeysVersion { .. }
130 | Forbidden => StatusCode::FORBIDDEN,
131
132 | UnknownToken { .. } | MissingToken | Unauthorized | UserLocked =>
134 StatusCode::UNAUTHORIZED,
135
136 | _ => StatusCode::BAD_REQUEST,
138 }
139}
140
141pub(super) fn ruma_error_message(error: &RumaError) -> String {
142 if let ErrorBody::Standard(StandardErrorBody { message, .. }) = &error.body {
143 return message.clone();
144 }
145
146 format!("{error}")
147}
148
149pub(super) fn ruma_error_kind(e: &RumaError) -> &ErrorKind {
150 e.error_kind().unwrap_or(&ErrorKind::Unknown)
151}
152
153pub(super) fn io_error_code(kind: std::io::ErrorKind) -> StatusCode {
154 use std::io::ErrorKind;
155
156 match kind {
157 | ErrorKind::InvalidInput => StatusCode::BAD_REQUEST,
158 | ErrorKind::PermissionDenied => StatusCode::FORBIDDEN,
159 | ErrorKind::NotFound => StatusCode::NOT_FOUND,
160 | ErrorKind::TimedOut => StatusCode::GATEWAY_TIMEOUT,
161 | ErrorKind::FileTooLarge => StatusCode::PAYLOAD_TOO_LARGE,
162 | ErrorKind::StorageFull => StatusCode::INSUFFICIENT_STORAGE,
163 | ErrorKind::Interrupted => StatusCode::SERVICE_UNAVAILABLE,
164 | _ => StatusCode::INTERNAL_SERVER_ERROR,
165 }
166}