Skip to main content

axum_server_maybetlsacceptor/
lib.rs

1//! axum-server-maybetlsacceptor is an [axum-server]-compatible enum that lets
2//! you easily accepts connections with or without TLS. The main goals were:
3//! - no duplication of routes, layers or other serving code to add the acceptor
4//! - no dynamic dispatch
5//! - ability to choose TLS backend (rustls, openssl)
6//!
7//! [pin-project] was chosen instead of [pin-project-lite] because it does not
8//! play well with other attributes such as `cfg`.
9//!
10//! ## Features
11//!
12//! By default, no feature is enabled, meaning no TLS backend is available.
13//!
14//! - `rustls`: enable the rustls TLS backend
15//! - `openssl`: enable the openssl TLS backend
16//!
17//! ## Compatibility
18//!
19//! Version `0.8.x` is compatible with [axum-server] version `0.8.y`.
20//!
21//! This crate's versioning will try to follow [axum-server]'s major versioning
22//! (or minor while being unstable `0.x.y`).
23//!
24//! ## Usage example
25//!
26//! You can find examples in [repository].
27//!
28//! [repository]: https://github.com/X2A-LIMITED/axum-server-maybetlsacceptor
29//! [axum-server]: https://crates.io/crates/axum-server
30//! [pin-project]: https://github.com/taiki-e/pin-project
31//! [pin-project-lite]: https://github.com/taiki-e/pin-project-lite
32
33// Copyright 2025 X2A Holding SAS, SIREN 990970519, France
34//
35// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
36// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
37// http://opensource.org/licenses/MIT>, at your option. This file may not be
38// copied, modified, or distributed except according to those terms.
39
40#![cfg_attr(docsrs, feature(doc_cfg))]
41
42use axum_server::accept::{Accept, DefaultAcceptor};
43#[cfg(feature = "openssl")]
44use axum_server::tls_openssl::OpenSSLAcceptor;
45#[cfg(feature = "rustls")]
46use axum_server::tls_rustls::RustlsAcceptor;
47use pin_project::pin_project;
48use tokio::io::{AsyncRead, AsyncWrite};
49
50#[derive(Clone)]
51pub enum MaybeTlsAcceptor {
52    Default(DefaultAcceptor),
53    #[cfg(feature = "rustls")]
54    Rustls(RustlsAcceptor),
55    #[cfg(feature = "openssl")]
56    Openssl(OpenSSLAcceptor),
57}
58
59impl<I, S> Accept<I, S> for MaybeTlsAcceptor
60where
61    I: AsyncRead + AsyncWrite + Unpin,
62{
63    type Stream = MaybeTlsAcceptorStream<I, S>;
64    type Service = S;
65    type Future = MaybeTlsAcceptorFuture<I, S>;
66
67    fn accept(&self, stream: I, service: S) -> Self::Future {
68        match self {
69            MaybeTlsAcceptor::Default(inner) => {
70                MaybeTlsAcceptorFuture::Default(inner.accept(stream, service))
71            }
72            #[cfg(feature = "rustls")]
73            MaybeTlsAcceptor::Rustls(inner) => {
74                MaybeTlsAcceptorFuture::Rustls(inner.accept(stream, service))
75            }
76            #[cfg(feature = "openssl")]
77            MaybeTlsAcceptor::Openssl(inner) => {
78                MaybeTlsAcceptorFuture::Openssl(inner.accept(stream, service))
79            }
80        }
81    }
82}
83
84impl Default for MaybeTlsAcceptor {
85    fn default() -> Self {
86        Self::Default(DefaultAcceptor::default())
87    }
88}
89
90#[pin_project(project = MaybeTlsAcceptorFutureProj)]
91pub enum MaybeTlsAcceptorFuture<I, S>
92where
93    I: AsyncRead + AsyncWrite + Unpin,
94{
95    Default(#[pin] <DefaultAcceptor as Accept<I, S>>::Future),
96    #[cfg(feature = "rustls")]
97    Rustls(#[pin] <RustlsAcceptor as Accept<I, S>>::Future),
98    #[cfg(feature = "openssl")]
99    Openssl(#[pin] <OpenSSLAcceptor as Accept<I, S>>::Future),
100}
101
102impl<I, S> Future for MaybeTlsAcceptorFuture<I, S>
103where
104    I: AsyncRead + AsyncWrite + Unpin,
105{
106    type Output = std::io::Result<(MaybeTlsAcceptorStream<I, S>, S)>;
107
108    fn poll(
109        self: std::pin::Pin<&mut Self>,
110        cx: &mut std::task::Context<'_>,
111    ) -> std::task::Poll<Self::Output> {
112        let this = self.project();
113
114        match this {
115            MaybeTlsAcceptorFutureProj::Default(future) => future
116                .poll(cx)
117                .map_ok(|(stream, service)| (MaybeTlsAcceptorStream::Default(stream), service)),
118            #[cfg(feature = "rustls")]
119            MaybeTlsAcceptorFutureProj::Rustls(future) => future
120                .poll(cx)
121                .map_ok(|(stream, service)| (MaybeTlsAcceptorStream::Rustls(stream), service)),
122            #[cfg(feature = "openssl")]
123            MaybeTlsAcceptorFutureProj::Openssl(future) => future
124                .poll(cx)
125                .map_ok(|(stream, service)| (MaybeTlsAcceptorStream::Openssl(stream), service)),
126        }
127    }
128}
129
130#[pin_project(project = MaybeTlsAcceptorStreamProj)]
131pub enum MaybeTlsAcceptorStream<I, S>
132where
133    I: AsyncRead + AsyncWrite + Unpin,
134{
135    Default(#[pin] <DefaultAcceptor as Accept<I, S>>::Stream),
136    #[cfg(feature = "rustls")]
137    Rustls(#[pin] <RustlsAcceptor as Accept<I, S>>::Stream),
138    #[cfg(feature = "openssl")]
139    Openssl(#[pin] <OpenSSLAcceptor as Accept<I, S>>::Stream),
140}
141
142impl<I, S> AsyncRead for MaybeTlsAcceptorStream<I, S>
143where
144    I: AsyncRead + AsyncWrite + Unpin,
145{
146    fn poll_read(
147        self: std::pin::Pin<&mut Self>,
148        cx: &mut std::task::Context<'_>,
149        buf: &mut tokio::io::ReadBuf<'_>,
150    ) -> std::task::Poll<std::io::Result<()>> {
151        let this = self.project();
152
153        match this {
154            MaybeTlsAcceptorStreamProj::Default(stream) => stream.poll_read(cx, buf),
155            #[cfg(feature = "rustls")]
156            MaybeTlsAcceptorStreamProj::Rustls(stream) => stream.poll_read(cx, buf),
157            #[cfg(feature = "openssl")]
158            MaybeTlsAcceptorStreamProj::Openssl(stream) => stream.poll_read(cx, buf),
159        }
160    }
161}
162
163impl<I, S> AsyncWrite for MaybeTlsAcceptorStream<I, S>
164where
165    I: AsyncRead + AsyncWrite + Unpin,
166{
167    fn poll_write(
168        self: std::pin::Pin<&mut Self>,
169        cx: &mut std::task::Context<'_>,
170        buf: &[u8],
171    ) -> std::task::Poll<Result<usize, std::io::Error>> {
172        let this = self.project();
173
174        match this {
175            MaybeTlsAcceptorStreamProj::Default(stream) => stream.poll_write(cx, buf),
176            #[cfg(feature = "rustls")]
177            MaybeTlsAcceptorStreamProj::Rustls(stream) => stream.poll_write(cx, buf),
178            #[cfg(feature = "openssl")]
179            MaybeTlsAcceptorStreamProj::Openssl(stream) => stream.poll_write(cx, buf),
180        }
181    }
182
183    fn poll_flush(
184        self: std::pin::Pin<&mut Self>,
185        cx: &mut std::task::Context<'_>,
186    ) -> std::task::Poll<Result<(), std::io::Error>> {
187        let this = self.project();
188
189        match this {
190            MaybeTlsAcceptorStreamProj::Default(stream) => stream.poll_flush(cx),
191            #[cfg(feature = "rustls")]
192            MaybeTlsAcceptorStreamProj::Rustls(stream) => stream.poll_flush(cx),
193            #[cfg(feature = "openssl")]
194            MaybeTlsAcceptorStreamProj::Openssl(stream) => stream.poll_flush(cx),
195        }
196    }
197
198    fn poll_shutdown(
199        self: std::pin::Pin<&mut Self>,
200        cx: &mut std::task::Context<'_>,
201    ) -> std::task::Poll<Result<(), std::io::Error>> {
202        let this = self.project();
203
204        match this {
205            MaybeTlsAcceptorStreamProj::Default(stream) => stream.poll_shutdown(cx),
206            #[cfg(feature = "rustls")]
207            MaybeTlsAcceptorStreamProj::Rustls(stream) => stream.poll_shutdown(cx),
208            #[cfg(feature = "openssl")]
209            MaybeTlsAcceptorStreamProj::Openssl(stream) => stream.poll_shutdown(cx),
210        }
211    }
212}