Skip to content

Commit 1dac121

Browse files
committed
fix: respond with 500 when middleware fails
1 parent 8dfbb2b commit 1dac121

File tree

6 files changed

+65
-9
lines changed

6 files changed

+65
-9
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ tokio = { version = "1", features = ["fs", "rt-multi-thread", "signal", "macros"
4444
tokio-rustls = "0.22"
4545
toml = "0.5"
4646
serde = { version = "1", features = ["derive"] }
47+
serde_json = "1.0.64"
4748
structopt = { version = "0.3", default-features = false }
4849

4950
[dev-dependencies]

src/server/middleware/gzip.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
1-
use http::{Request, Response};
1+
use http::{Request, Response, StatusCode};
22
use hyper::Body;
33
use std::sync::Arc;
44
use tokio::sync::Mutex;
55

66
use crate::addon::compression::gzip::compress_http_response;
7+
use crate::utils::error::make_http_error_response;
78

89
use super::MiddlewareAfter;
910

1011
pub fn make_gzip_compression_middleware() -> MiddlewareAfter {
1112
Box::new(
1213
move |request: Arc<Request<Body>>, response: Arc<Mutex<Response<Body>>>| {
13-
Box::pin(async move { compress_http_response(request, response).await })
14+
Box::pin(async move {
15+
compress_http_response(request, response)
16+
.await
17+
.map_err(|err| {
18+
make_http_error_response(
19+
StatusCode::INTERNAL_SERVER_ERROR,
20+
&err.to_string(),
21+
)
22+
})
23+
})
1424
},
1525
)
1626
}

src/server/middleware/mod.rs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
pub mod cors;
22
pub mod gzip;
33

4-
use anyhow::{Error, Result};
4+
use anyhow::Error;
55
use futures::Future;
66
use http::{Request, Response};
77
use hyper::Body;
@@ -15,15 +15,34 @@ use crate::config::Config;
1515
use self::cors::make_cors_middleware;
1616
use self::gzip::make_gzip_compression_middleware;
1717

18-
pub type MiddlewareBefore = Box<dyn Fn(&mut Request<Body>) + Send + Sync>;
18+
/// Middleware chain `Result` which specifies the `Err` variant as a
19+
/// HTTP response.
20+
pub type Result = std::result::Result<(), Response<Body>>;
21+
22+
/// Middleware chain to execute before the main handler digests the
23+
/// HTTP request. No HTTP response is available at this point.
24+
pub type MiddlewareBefore = Box<
25+
dyn Fn(&mut Request<Body>) -> Pin<Box<dyn Future<Output = Result> + Send + Sync>> + Send + Sync,
26+
>;
27+
28+
/// Middleware chain to execute after the main handler digests the
29+
/// HTTP request. The HTTP response is created by the handler and
30+
/// consumed by every middleware after chain.
1931
pub type MiddlewareAfter = Box<
2032
dyn Fn(
2133
Arc<Request<Body>>,
2234
Arc<Mutex<Response<Body>>>,
23-
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + Sync>>
35+
) -> Pin<Box<dyn Future<Output = Result> + Send + Sync>>
2436
+ Send
2537
+ Sync,
2638
>;
39+
40+
/// The main handler for the HTTP request, a HTTP response is created
41+
/// as a result of this handler.
42+
///
43+
/// This handler will be executed against the HTTP request after every
44+
/// "Middleware Before" chain is executed but before any "Middleware After"
45+
/// chain is executed
2746
pub type Handler = Box<
2847
dyn Fn(Arc<Request<Body>>) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + Sync>>
2948
+ Send
@@ -57,16 +76,18 @@ impl Middleware {
5776
/// with the HTTP Response from the handler
5877
pub async fn handle(&self, mut request: Request<Body>, handler: Handler) -> Response<Body> {
5978
for fx in self.before.iter() {
60-
fx(&mut request);
79+
if let Err(err) = fx(&mut request).await {
80+
return err;
81+
}
6182
}
6283

6384
let request = Arc::new(request);
6485
let response = handler(Arc::clone(&request)).await;
6586
let response = Arc::new(Mutex::new(response));
6687

6788
for fx in self.after.iter() {
68-
if let Err(error) = fx(Arc::clone(&request), Arc::clone(&response)).await {
69-
eprintln!("{:?}", error);
89+
if let Err(err) = fx(Arc::clone(&request), Arc::clone(&response)).await {
90+
return err;
7091
}
7192
}
7293

@@ -88,7 +109,7 @@ impl Default for Middleware {
88109
impl TryFrom<Arc<Config>> for Middleware {
89110
type Error = Error;
90111

91-
fn try_from(config: Arc<Config>) -> Result<Self, Self::Error> {
112+
fn try_from(config: Arc<Config>) -> std::result::Result<Self, Self::Error> {
92113
let mut middleware = Middleware::default();
93114

94115
if let Some(cors_config) = config.cors() {

src/utils/error.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use http::{Response, StatusCode};
2+
use hyper::Body;
3+
use serde::Serialize;
4+
5+
#[derive(Debug, Serialize)]
6+
struct ErrorResponseBody {
7+
status_code: u16,
8+
message: String,
9+
}
10+
11+
pub fn make_http_error_response(status: StatusCode, message: &str) -> Response<Body> {
12+
Response::builder()
13+
.status(status)
14+
.body(Body::from(
15+
serde_json::ser::to_string(&ErrorResponseBody {
16+
status_code: status.as_u16(),
17+
message: message.to_string(),
18+
})
19+
.unwrap(),
20+
))
21+
.unwrap()
22+
}

src/utils/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
pub mod error;
12
pub mod signal;

0 commit comments

Comments
 (0)