How to implement TLS Acceptor as `axum_server::Accept` properly?

Hi,

I'm trying to integrate tokio-openssl with axum-server, which seems possible by implement axum_server::accept::Accept. However, the following implementation doesn't work and I don't understand why (full source code here: hyper-openssl + axum-server · GitHub).

By "it doesn't work" I mean: the server is listening on the specified port, but curl -k https://127.0.0.1:9443 fails with error "OpenSSL SSL_connect: SSL_ERROR_SYSCALL". Also, tcpdump shows that the server sends FIN before then client sends TLS Client Hello.

I can confirm that the OpenSSL part is configured correctly, since the serve_tls_loop version works just fine.

Did I miss anything or implement the trait in the wrong way?

impl<IO, Service> Accept<IO, Service> for TLSAcceptor
where
    IO: AsyncRead + AsyncWrite + Unpin,
{
    type Stream = SslStream<IO>;
    type Service = Service;
    type Future = AcceptFuture<IO, Service>;

    fn accept(&self, stream: IO, service: Service) -> Self::Future {
        AcceptFuture {
            tls_ctx: self.ctx.clone(),
            tcp_stream: Some((stream, service)),
        }
    }
}

pin_project! {
    pub struct AcceptFuture<IO, Service> {
        tls_ctx: Arc<SslContext>,
        tcp_stream: Option<(IO, Service)>,
    }
}

impl<IO, Service> Future for AcceptFuture<IO, Service>
where
    IO: AsyncRead + AsyncWrite + Unpin,
{
    type Output = io::Result<(SslStream<IO>, Service)>;

    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
        let pin = self.project();
        let (tcp_stream, service) = pin.tcp_stream.take().expect("must not be None");
        let mut tls_future = Box::pin(accept_stream(&pin.tls_ctx, tcp_stream));

        Pin::new(&mut tls_future)
            .poll(ctx)
            .map_ok(|stream| (stream, service))
            .map_err(|err| io::Error::new(io::ErrorKind::Other, format!("TLS Error: {:?}", err)))
    }
}

Thanks to Programatik on the Tokio Discord (Discord), I've modified accept() to return a BoxFuture, so that I don't need to implement the Future trait by myself.

fn accept(&self, stream: IO, service: Service) -> Self::Future {
    let ctx = self.ctx.clone();

    let future = async move {
        accept_stream(&ctx, stream)
            .await
            .map(|stream| (stream, service))
            .map_err(|err| {
                let msg = format!("TLS Error: {}", err);
                error!("failed to accept connection: {}", msg);
                io::Error::new(io::ErrorKind::Other, msg)
            })
    };

    Box::pin(future)
}
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.