

A high-performance, modular server implementation built on hyper, designed to
work seamlessly with axum, tonic, tower, and other tower-compatible
frameworks.
Features
- HTTP/1 and HTTP/2 support
- TLS/HTTPS through rustls
- High performance leveraging hyper 1.0
- Modular architecture based on
tokio::net::TcpListener, tokio-rustls::Acceptor, and hyper::server::conn::auto
- Flexible integration with tower the ecosystem, supporting various backends:
- axum
- tonic
- Any
hyper::service::Service, tower::Service, or tower::Layer composition
Installation
Add this to your Cargo.toml:
[dependencies]
postel = "0.7.2"
Usage
Here's an example of how to use postel with a simple tower lambda service via TCP/TLS/HTTP2 transport:
use std::convert::Infallible;
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use bytes::Bytes;
use http_body_util::Full;
use hyper::{Request, Response};
use hyper::body::Incoming;
use hyper_util::rt::TokioExecutor;
use hyper_util::server::conn::auto::Builder as HttpConnectionBuilder;
use hyper_util::service::TowerToHyperService;
use rustls::ServerConfig;
use tokio::net::TcpListener;
use tokio_stream::wrappers::TcpListenerStream;
use tower::{Layer, ServiceBuilder};
use tracing::{trace, debug, info};
use postel::{load_certs, load_private_key, serve_http_with_shutdown};
async fn hello(_: Request<Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
Ok(Response::new(Full::new(Bytes::from("Hello, World!"))))
}
struct AddHeaderLayer;
impl<S> Layer<S> for AddHeaderLayer {
type Service = AddHeaderService<S>;
fn layer(&self, service: S) -> Self::Service {
AddHeaderService { inner: service }
}
}
#[derive(Clone)]
struct AddHeaderService<S> {
inner: S,
}
impl<S, B> tower::Service<Request<B>> for AddHeaderService<S>
where
S: tower::Service<Request<B>, Response=Response<Full<Bytes>>>,
S::Future: Send + 'static,
{
type Response = S::Response;
type Error = S::Error;
type Future = std::pin::Pin<
Box<dyn std::future::Future<Output=Result<Self::Response, Self::Error>> + Send>,
>;
fn poll_ready(
&mut self,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, req: Request<B>) -> Self::Future {
trace!("Adding custom header to response");
let future = self.inner.call(req);
Box::pin(async move {
let mut resp = future.await?;
resp.headers_mut()
.insert("X-Custom-Header", "Hello from middleware!".parse().unwrap());
Ok(resp)
})
}
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let addr = SocketAddr::from(([127, 0, 0, 1], 8443));
let listener = TcpListener::bind(addr).await?;
info!("Listening on https://{}", addr);
let incoming = TcpListenerStream::new(listener);
let builder = HttpConnectionBuilder::new(TokioExecutor::new());
let svc = tower::service_fn(hello);
let svc = ServiceBuilder::new()
.layer(AddHeaderLayer) .service(svc);
let svc = TowerToHyperService::new(svc);
let certs = load_certs("examples/sample.pem")?;
let key = load_private_key("examples/sample.rsa")?;
let mut config = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certs, key)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec(), b"http/1.0".to_vec()];
let tls_config = Arc::new(config);
let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel::<()>();
tokio::spawn(async move {
tokio::time::sleep(Duration::from_secs(1)).await;
let _ = shutdown_tx.send(());
debug!("Shutdown signal sent");
});
info!("Starting HTTPS server...");
serve_http_with_shutdown(
svc,
incoming,
builder,
Some(tls_config),
Some(async {
shutdown_rx.await.ok();
info!("Shutdown signal received, starting graceful shutdown");
}),
)
.await?;
info!("Server has shut down");
Ok(())
}
For more advanced usage and examples, please refer to, or contribute to,
the examples directory.
Architecture
postel provides a layered, composable architecture:
- TCP Listening:
tokio::net::TcpListener
- TLS (optional):
rustls::TlsAcceptor
- HTTP:
hyper_util::server::conn::auto
- Service:
hyper::service::Service
- Middleware:
tower::Service
- Application: Your choice of tower-compatible framework (axum, tonic, etc.) or custom service implementation
This structure allows for easy customization and extension at each layer. You
can integrate your own implementations at any level of the stack, providing
maximum flexibility for your specific use case.
Security
postel takes security seriously. We use rustls for TLS support, which
provides modern, secure defaults. However, please ensure that you configure
your server appropriately for your use case, especially when deploying in
production environments.
If you discover any security-related issues, please email team@warlock.xyz
instead of using the issue tracker.
API
For detailed API documentation, please refer to the API docs on docs.rs.
Minimum Supported Rust Version
postel's MSRV is 1.80.
Contributing
We welcome contributions to postel! Our contributing guidelines are
inspired by the Rule of St. Benedict, emphasizing humility, listening,
and community. Before contributing, please familiarize yourself with these
principles at The Rule of St. Benedict.
Key points for contributors:
- Listen first, speak second (Chapter 6)
- Be humble in your contributions (Chapter 7)
- Work diligently and carefully (Chapter 48)
- Treat all code and ideas with respect (Chapter 72)
License
This project is licensed under the MIT License — see
the LICENSE file for details.