Skip to content

Commit 8981af3

Browse files
better error renderer
1 parent acfe2b9 commit 8981af3

File tree

1 file changed

+59
-46
lines changed

1 file changed

+59
-46
lines changed

src/main.rs

+59-46
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,17 @@ use std::{
1212

1313
use askama::Template;
1414
use axum::{
15-
extract::{Path, Query, State},
15+
body::Body,
16+
extract::{Path, Query, Request, State},
1617
handler::Handler,
1718
http::{
18-
header::{CACHE_CONTROL, CONTENT_SECURITY_POLICY, CONTENT_TYPE},
19+
header::{ACCEPT, CACHE_CONTROL, CONTENT_SECURITY_POLICY, CONTENT_TYPE},
1920
HeaderName, HeaderValue, StatusCode,
2021
},
22+
middleware::Next,
2123
response::{IntoResponse, Redirect, Response},
2224
routing::get,
23-
Router,
25+
Extension, Router,
2426
};
2527
use axum_extra::routing::RouterExt;
2628
use base64::{prelude::BASE64_STANDARD, Engine};
@@ -94,6 +96,7 @@ async fn main() {
9496
let noindex = SetResponseHeaderLayer::overriding(ROBOTS_NAME.clone(), ROBOTS_VALUE.clone());
9597
let csp = SetResponseHeaderLayer::overriding(CONTENT_SECURITY_POLICY, CSP_VALUE.clone());
9698
let clacks = SetResponseHeaderLayer::overriding(CLACKS_NAME.clone(), CLACKS_VALUE.clone());
99+
let error_handler = axum::middleware::from_fn_with_state(state.clone(), error_middleware);
97100

98101
let serve_dir_raw = ServeDir::new(&asset_dir)
99102
.append_index_html_on_directories(true)
@@ -127,7 +130,12 @@ async fn main() {
127130
)
128131
.fallback_service(serve_dir)
129132
.merge(api)
130-
.layer(ServiceBuilder::new().layer(csp).layer(clacks))
133+
.layer(
134+
ServiceBuilder::new()
135+
.layer(csp)
136+
.layer(clacks)
137+
.layer(error_handler),
138+
)
131139
.with_state(state);
132140

133141
let socket_address = SocketAddr::from((Ipv4Addr::UNSPECIFIED, port));
@@ -227,15 +235,10 @@ pub struct PingPageTemplate {
227235
async fn ping_page(
228236
State(state): State<AppState>,
229237
Path((edition, hostname)): Path<(String, String)>,
230-
) -> Result<PingPageTemplate, ErrorTemplate> {
238+
) -> Result<PingPageTemplate, Failure> {
231239
match edition.as_str() {
232240
"java" | "bedrock" => {}
233-
_ => {
234-
return Err(ErrorTemplate::from_failure(
235-
&Failure::UnknownEdition,
236-
&state,
237-
))
238-
}
241+
_ => return Err(Failure::UnknownEdition),
239242
}
240243
Ok(PingPageTemplate {
241244
svc_status: *state.svc_response.read(),
@@ -268,10 +271,8 @@ pub struct PingFrameTemplate {
268271
async fn ping_frame(
269272
State(state): State<AppState>,
270273
Path((edition, hostname)): Path<(String, String)>,
271-
) -> Result<PingFrameTemplate, ErrorTemplate> {
272-
let ping = ping_generic(&edition, hostname.clone())
273-
.await
274-
.map_err(|v| v.as_error_template(&state))?;
274+
) -> Result<PingFrameTemplate, Failure> {
275+
let ping = ping_generic(&edition, hostname.clone()).await?;
275276
Ok(PingFrameTemplate {
276277
ping,
277278
root_url: state.root_url,
@@ -294,10 +295,8 @@ pub struct PingElementTemplate {
294295
async fn ping_markup(
295296
State(state): State<AppState>,
296297
Path((edition, hostname)): Path<(String, String)>,
297-
) -> Result<PingElementTemplate, ErrorTemplate> {
298-
let ping = ping_generic(&edition, hostname.clone())
299-
.await
300-
.map_err(|v| v.as_error_template(&state))?;
298+
) -> Result<PingElementTemplate, Failure> {
299+
let ping = ping_generic(&edition, hostname.clone()).await?;
301300
Ok(PingElementTemplate {
302301
ping,
303302
bd: state.bust_dir,
@@ -377,17 +376,7 @@ impl IntoResponse for Failure {
377376
Self::NoHostname | Self::UnknownEdition => StatusCode::BAD_REQUEST,
378377
};
379378
error!(error = ?self, "Error processing request");
380-
let ser = ErrorSerialization {
381-
error: self.to_string(),
382-
};
383-
(status, Json(ser)).into_response()
384-
}
385-
}
386-
387-
impl Failure {
388-
#[must_use]
389-
pub fn as_error_template(&self, state: &AppState) -> ErrorTemplate {
390-
ErrorTemplate::from_failure(self, state)
379+
(status, Extension(Arc::new(self)), Body::empty()).into_response()
391380
}
392381
}
393382

@@ -404,27 +393,23 @@ pub struct ErrorTemplate {
404393
root_url: Arc<str>,
405394
}
406395

407-
impl ErrorTemplate {
408-
fn from_failure(failure: &Failure, state: &AppState) -> Self {
409-
Self {
410-
root_url: state.root_url.clone(),
411-
bd: state.bust_dir.clone(),
412-
error: failure.to_string(),
413-
}
414-
}
415-
}
396+
static HTML_CTYPE: HeaderValue = HeaderValue::from_static("text/html;charset=utf-8");
416397

417398
pub struct Json<T: Serialize>(pub T);
418399

400+
static JSON_CTYPE: HeaderValue = HeaderValue::from_static("application/json;charset=utf-8");
401+
402+
fn infallible_json_serialize<T: Serialize>(data: &T) -> Vec<u8> {
403+
serde_json::to_vec_pretty(data).unwrap_or_else(|_| {
404+
r#"{"error": "JSON Serialization failed, please make a bug report"}"#
405+
.as_bytes()
406+
.to_vec()
407+
})
408+
}
409+
419410
impl<T: Serialize> IntoResponse for Json<T> {
420411
fn into_response(self) -> Response {
421-
static JSON_CTYPE: HeaderValue = HeaderValue::from_static("application/json;charset=utf-8");
422-
423-
let body = serde_json::to_vec_pretty(&self.0).unwrap_or_else(|_| {
424-
r#"{"error": "JSON Serialization failed, please make a bug report"}"#
425-
.as_bytes()
426-
.to_vec()
427-
});
412+
let body = infallible_json_serialize(&self.0);
428413
(
429414
StatusCode::INTERNAL_SERVER_ERROR,
430415
[(CONTENT_TYPE, JSON_CTYPE.clone())],
@@ -434,6 +419,34 @@ impl<T: Serialize> IntoResponse for Json<T> {
434419
}
435420
}
436421

422+
async fn error_middleware(State(state): State<AppState>, req: Request, next: Next) -> Response {
423+
let json = req
424+
.headers()
425+
.get(ACCEPT)
426+
.is_some_and(|v| v.to_str().is_ok_and(|v| v.contains("application/json")));
427+
let mut resp = next.run(req).await;
428+
if let Some(failure) = resp.extensions().get::<Arc<Failure>>().cloned() {
429+
let error = failure.to_string();
430+
if json {
431+
resp.headers_mut().insert(CONTENT_TYPE, JSON_CTYPE.clone());
432+
let error = ErrorSerialization { error };
433+
let json = infallible_json_serialize(&error);
434+
*resp.body_mut() = Body::from(json);
435+
} else {
436+
resp.headers_mut().insert(CONTENT_TYPE, HTML_CTYPE.clone());
437+
let error = ErrorTemplate {
438+
error,
439+
bd: state.bust_dir,
440+
root_url: state.root_url,
441+
}
442+
.render()
443+
.unwrap_or_else(|e| format!("error rendering template: {e}"));
444+
*resp.body_mut() = Body::from(error);
445+
};
446+
}
447+
resp
448+
}
449+
437450
pub struct Png(pub Vec<u8>);
438451

439452
impl IntoResponse for Png {

0 commit comments

Comments
 (0)