Skip to content

Commit ef56636

Browse files
davidpdrsnjplatte
andauthored
Gracefully handle accept errors in serve (#2400)
Co-authored-by: Jonas Platte <[email protected]>
1 parent 6491655 commit ef56636

File tree

3 files changed

+49
-1
lines changed

3 files changed

+49
-1
lines changed

axum/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
# Unreleased
99

1010
- **added:** Add `axum::body::to_bytes` ([#2373])
11+
- **fixed:** Gracefully handle accept errors in `serve` ([#2400])
1112

1213
[#2373]: https://github.com/tokio-rs/axum/pull/2373
14+
[#2400]: https://github.com/tokio-rs/axum/pull/2400
1315

1416
# 0.7.1 (27. November, 2023)
1517

axum/src/macros.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,15 @@ macro_rules! all_the_tuples {
6666
$name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], T16);
6767
};
6868
}
69+
70+
#[cfg(feature = "tracing")]
71+
macro_rules! error {
72+
($($tt:tt)*) => {
73+
tracing::error!($($tt)*)
74+
};
75+
}
76+
77+
#[cfg(not(feature = "tracing"))]
78+
macro_rules! error {
79+
($($tt:tt)*) => {};
80+
}

axum/src/serve.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::{
88
net::SocketAddr,
99
pin::Pin,
1010
task::{Context, Poll},
11+
time::Duration,
1112
};
1213

1314
use axum_core::{body::Body, extract::Request, response::Response};
@@ -147,7 +148,31 @@ where
147148
} = self;
148149

149150
loop {
150-
let (tcp_stream, remote_addr) = tcp_listener.accept().await?;
151+
let (tcp_stream, remote_addr) = match tcp_listener.accept().await {
152+
Ok(conn) => conn,
153+
Err(e) => {
154+
// Connection errors can be ignored directly, continue
155+
// by accepting the next request.
156+
if is_connection_error(&e) {
157+
continue;
158+
}
159+
160+
// [From `hyper::Server` in 0.14](https://github.com/hyperium/hyper/blob/v0.14.27/src/server/tcp.rs#L186)
161+
//
162+
// > A possible scenario is that the process has hit the max open files
163+
// > allowed, and so trying to accept a new connection will fail with
164+
// > `EMFILE`. In some cases, it's preferable to just wait for some time, if
165+
// > the application will likely close some files (or connections), and try
166+
// > to accept the connection again. If this option is `true`, the error
167+
// > will be logged at the `error` level, since it is still a big deal,
168+
// > and then the listener will sleep for 1 second.
169+
//
170+
// hyper allowed customizing this but axum does not.
171+
error!("accept error: {e}");
172+
tokio::time::sleep(Duration::from_secs(1)).await;
173+
continue;
174+
}
175+
};
151176
let tcp_stream = TokioIo::new(tcp_stream);
152177

153178
poll_fn(|cx| make_service.poll_ready(cx))
@@ -187,6 +212,15 @@ where
187212
}
188213
}
189214

215+
fn is_connection_error(e: &io::Error) -> bool {
216+
matches!(
217+
e.kind(),
218+
io::ErrorKind::ConnectionRefused
219+
| io::ErrorKind::ConnectionAborted
220+
| io::ErrorKind::ConnectionReset
221+
)
222+
}
223+
190224
mod private {
191225
use std::{
192226
future::Future,

0 commit comments

Comments
 (0)