Open
Description
- I have looked for existing issues (including closed) about this
Feature Request
Motivation
Some kinds of listener, like TLS listeners, will want to perform work after the connection has been accepted, but before HTTP requests can actually be served. This logic can be moved into the listener itself via complex multiplexing logic – for example, the TlsListener
crate uses FuturesUnordered
to do this – however I think that ideally it would be performed on the same task that serves the connection.
Proposal
Modify the Listener
trait as follows:
pub trait Listener: Send + 'static {
type Io: AsyncRead + AsyncWrite + Unpin + Send + 'static;
type Addr: Send;
fn accept(&mut self) -> impl Future<Output = impl Future<Output = Option<(Self::Io, Self::Addr)>> + Send + 'static> + Send;
fn local_addr(&self) -> io::Result<Self::Addr>;
}
The outer future of accept
will resolve when a connection has been accepted, while the inner future runs on the spawned task. Option<(Self::Io, Self::Addr)>
is used to account for errors: if None
is returned, then the task is aborted.
Alternatives
- Use a
type Error: Into<Box<dyn Error + Send + Sync>>;
andResult<(Self::Io, Self::Addr), Self::Error>
instead ofOption
. This might be more convenient for the user, as they would have to handle the errors less themselves.- A significant disadvantage of this is it makes composability much more difficult, as now errors have to compose. So I’m leaning toward
Option
.
- A significant disadvantage of this is it makes composability much more difficult, as now errors have to compose. So I’m leaning toward
- Say that users should define their own “lazy TLS stream” type to handle this case. It would be an
enum
over the handshake future and the underlying TLS stream, and its implementation ofAsyncRead
/AsyncWrite
would first drive the handshake to completion before performing I/O (or report EOF should the handshake fail). I can’t particularly think of practical reasons to avoid this other than “it feels weird”. - Say that users who want TLS in this way should not use
axum::serve
. That function is, however, a very convenient abstraction, taking care of graceful shutdown and constructing theserver::conn::auto::Builder
with the right configuration.
Metadata
Metadata
Assignees
Labels
No labels