programatik29 / axum-server Goto Github PK
View Code? Open in Web Editor NEWHigh level server designed to be used with axum framework.
License: MIT License
High level server designed to be used with axum framework.
License: MIT License
It's more a discussion rather than an issue. What do you think about redesigning a library to be similar to the hyper::Server
?
It will lead to the same experience with graceful_shutdown
.
For example,
#[tokio::main]
async fn main() -> anyhow::Result<()> {
...
let tls_config = ...;
axum_server::bind_rustls(addr, tls_config)
.handle(...)
.serve(...)
.await?;
// vs
axum::Server::bind(&addr)
.serve(...)
.with_graceful_shutdown(shutdown_signal())
.await?;
Ok(())
}
async fn shutdown_signal() {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
}
Now, I need to get an handle
object and control it in a separate task. Is it worth changing it to the same behavior as in hyper::Server
? What do you think?
I have an HTTPS server (:wink:) that accepts two forms of traffic, unauthenticated and authenticated, and would like to use AllowAnyAnonymousOrAuthenticatedClient or similar to support both. However, I don't see a way to tell if client authentication was successful at all and/or call peer_certificates
.
I looked into into_make_service_with_connect_info
but that accesses an &AddrStream
before the TLS handshake.
Any thoughts on how mTLS could be made optional while letting the tower service know the client authentication status?
From the network perspective, I'd like:
On the Axum side, I'd like the following:
Router::new()
.route("/idea1", axum::routing::get(move |
ConnectInfo(addr): ConnectInfo<SocketAddr>,
AcceptInfo(certs): AcceptInfo<Option<Vec<Certificate>>> // i.e. from peer_certificates
| { if let Some(certs) && check_certs(certs) { } else { ... } })
.route("/idea2", axum::routing::get(move |
ConnectInfo(addr): ConnectInfo<SocketAddr>,
AcceptInfo(auth): AcceptInfo<ClientAuthentication> // enum ClientAuthentication { Authenticated, Anonymous }
| { if auth.is_authenticated() { ... } else { ... } })
*the name AcceptInfo
is just bikeshedding
Edit: I no longer need this. Feel free to close it as not planned if you don't find it useful.
It would be nice to know when server starts listening and being able to shutdown or gracefully shutdown it.
I really do like the easy of working with axum and axum-server. It is straightforward to set up TLS and reload it, which is awesome when used with certificates that need to renewed often.
I would be genuinely interested in binding a server to multiple sockets of different types. For example, to accept an arbitrary list of TCP and unix domain sockets via systemd socket activation:
let tls_config = RustlsConfig::from_pem_file(
"examples/self-signed-certs/cert.pem",
"examples/self-signed-certs/key.pem",
)
.await?;
let server = axum_server::new();
let mut lfd = ListenFd::from_env();
if lfd.len() > 0 {
for idx in 0..lfd.len() {
if let Ok(Some(lst)) = lfd.take_tcp_listener(idx) {
server = server.from_tcp_rustls(lst, tls_config);
} else if let Ok(Some(lst)) = lfd.take_unix_listener(idx) {
server = server.from_uds(lst);
} else {
warn!("Unsupported socket type at index {:?}", idx)
}
}
} else {
server = server.bind("[::]:3000".parse()?)
}
let app = Router::new().route("/", get(|| async { "Hello World" }));
server.serve(app.into_make_service()).await?;
Having the same app listening on multiple sockets greatly improves usability and integration with many systems, such as systemd. Unix domain sockets are often used to provide a service to other local services, such as reverse proxies or load balancers. A second TCP listener is often spawned e.g. for debugging or monitoring purposes.
Does this feature sound reasonable for axum-server?
I seem to be having trouble incorporating the example code into a toy project I am playing with to learn a bit of rust.
Not sure what he cause is, but I'm on 1.64 on fedora if that makes much of a difference
Compiling axum-server v0.4.2
error: no rules expected the token `;`
--> /home/fejfighter/.cargo/registry/src/github.com-1ecc6299db9ec823/axum-server-0.4.2/src/handle.rs:94:23
|
94 | biased;
| ^ no rules expected this token in macro call
error: no rules expected the token `;`
--> /home/fejfighter/.cargo/registry/src/github.com-1ecc6299db9ec823/axum-server-0.4.2/src/server.rs:175:27
|
175 | biased;
| ^ no rules expected this token in macro call
error: no rules expected the token `;`
--> /home/fejfighter/.cargo/registry/src/github.com-1ecc6299db9ec823/axum-server-0.4.2/src/server.rs:204:35
|
204 | ... biased;
| ^ no rules expected this token in macro call
error: no rules expected the token `;`
--> /home/fejfighter/.cargo/registry/src/github.com-1ecc6299db9ec823/axum-server-0.4.2/src/server.rs:214:19
|
214 | biased;
| ^ no rules expected this token in macro call
error: could not compile `axum-server` due to 4 previous errors
Hi there,
I wrote a custom acceptor that enforces client hostname checking during mTLS. I noticed in serve
that errors from the acceptor are ignored, leading the the socket being closed with out reporting an error back to the client. It would be helpful to report these errors back to the client before closing the connection.
serve in server.rs
tokio::spawn(async move {
if let Ok((stream, send_service)) = acceptor.accept(addr_stream, service).await
// ^^^^ maybe match on the result and write the error down the socket before closing the connection.
.....
An util
module with optional util
feature can be created to provide higher level features.
Some possible high level features:
hi, Eray, i've tested your recent posted rewrite-minimal redesign version, it works! but i noticed one problem: there is no way to make a single server to provide https and http dual protocol like version 0.2.5. This is a must-have feature, please please conside it.
for use axum::extract::ConnectInfo
, we need into_make_service_with_connect_info.
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(
app.into_make_service_with_connect_info::<SocketAddr, _>()
)
.await
.expect("server failed");
which is not supported in axum-server for now.
use axum::{
handler::get,
Router,
};
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(|| async { "Hello, World!" }));
axum_server::bind("127.0.0.1:3000")
.serve(app.into_make_service_with_connect_info::<SocketAddr, _>())
.await
.unwrap();
}
we got
error[E0271]: type mismatch resolving `<IntoMakeServiceWithConnectInfo<Route<axum::handler::OnMethod<[closure@src\main.rs:10:44: 10:72], _, (), EmptyRouter>, EmptyRouter<_>>, SocketAddr> as tower_service::Service<Request<axum::body::Body>>>::Response == Response<_>`
--> src\main.rs:13:10
|
10 | let app = Router::new().route("/", get(|| async { "Hello, World!" }));
| ----------------------------
| | |
| | the expected `async` block
| the expected closure
...
13 | .serve(app.into_make_service_with_connect_info::<SocketAddr, _>())
| ^^^^^ expected struct `AddExtension`, found struct `Response`
|
::: C:\Users\25386\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\core\src\future\mod.rs:61:43
|
61 | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
| ------------------------------- the expected opaque type
|
= note: expected struct `AddExtension<Route<axum::handler::OnMethod<[closure@src\main.rs:10:44: 10:72], _, (), EmptyRouter>, EmptyRouter<_>>, ConnectInfo<_>>`
found struct `Response<_>`
error[E0277]: the trait bound `IntoMakeServiceWithConnectInfo<Route<axum::handler::OnMethod<[closure@src\main.rs:10:44: 10:72], _, (), EmptyRouter>, EmptyRouter<_>>, SocketAddr>: Clone` is not satisfied
--> src\main.rs:13:16
|
13 | .serve(app.into_make_service_with_connect_info::<SocketAddr, _>())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `IntoMakeServiceWithConnectInfo<Route<axum::handler::OnMethod<[closure@src\main.rs:10:44: 10:72], _, (), EmptyRouter>, EmptyRouter<_>>, SocketAddr>`
error[E0277]: the trait bound `SocketAddr: Connected<Request<axum::body::Body>>` is not satisfied
--> src\main.rs:13:16
|
13 | .serve(app.into_make_service_with_connect_info::<SocketAddr, _>())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Connected<Request<axum::body::Body>>` is not implemented for `SocketAddr`
|
= help: the following implementations were found:
<SocketAddr as Connected<&hyper::server::tcp::addr_stream::AddrStream>>
= note: required because of the requirements on the impl of `tower_service::Service<Request<axum::body::Body>>` for `IntoMakeServiceWithConnectInfo<Route<axum::handler::OnMethod<[closure@src\main.rs:10:44: 10:72], _, (), EmptyRouter>, EmptyRouter<_>>, SocketAddr>`
error: aborting due to 3 previous errors
The way the accept loop is currently written, it seems as though accepting is done in serial
Lines 173 to 178 in 04bbd9f
For HTTPS, it seems as though the SSL handshake is done in the acceptor:
axum-server/src/tls_rustls/mod.rs
Lines 69 to 72 in dfafcbd
This implies that SSL handshakes cannot take place concurrently, which can cause unavailability if one user's SSL handshake is taking significant time.
I wrote an Accept
or that allows handling of HTTP and HTTPS on the same port.
The implementation is fairly minimal and simple. Is it in scope for axum-server
to add something like this? Happy to start a PR of course.
My use-case is hosting a HTTPS server on a custom port, unlike the example, there is only one port. So connecting with HTTP to that server errors out.
The idea is if I accept both protocols, I can design a middleware that handles the redirect.
Hello,
I use rustls-acme to create automatically Let's Encrypt certificates like in https://github.com/FlorianUekermann/rustls-acme/blob/main/examples/low_level_axum.rs . It works very well, but the server seems not to respond with HTTP/2, even if the http2 feature is enabled in axum.
How can I enable HTTP/2 ?
Thanks and best regards.
Currently there is no way to reload private key and certificates while server is running. I plan to add it.
How does that API sound:
use axum::{
handler::get,
Router,
};
use axum_server::TlsLoader;
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(|| async { "Hello, World!" }));
let loader = TlsLoader::builder()
.private_key_file("certs/key.pem")
.certificate_file("certs/cert.pem")
.build()
.await
.unwrap();
tokio::spawn(reload_every_day(loader.clone()));
axum_server::bind_rustls("127.0.0.1:3000")
.tls_loader(loader)
.serve(app)
.await
.unwrap();
}
async fn reload_every_day(mut loader: TlsLoader) {
loop {
// Sleep first since certificates are loaded after loader is built.
sleep(Duration::from_secs(3600 * 24)).await;
// Can be loaded with recent settings.
// For example: Read previously provided file contents again.
loader.load().await.unwrap();
// Can overwrite settings and load.
loader
.private_key_file("certs/private_key.pem")
.certificate_file("certs/fullchain.pem")
.load()
.await
.unwrap();
}
}
On master, it seems that rustls version has been bumped but changes haven't been made. I get this when compiling:
error[E0432]: unresolved imports `rustls::Certificate`, `rustls::PrivateKey`
--> src/tls_rustls/mod.rs:36:14
|
36 | use rustls::{Certificate, PrivateKey, ServerConfig};
| ^^^^^^^^^^^ ^^^^^^^^^^ no `PrivateKey` in the root
| |
| no `Certificate` in the root
|
= help: consider importing one of these items instead:
rustls::HandshakeType::Certificate
rustls::internal::msgs::handshake::HandshakePayload::Certificate
error[E0277]: the trait bound `TlsAcceptor: From<Arc<rustls::ServerConfig>>` is not satisfied
--> src/tls_rustls/future.rs:84:44
|
84 | ... let acceptor = TlsAcceptor::from(server_config);
| ^^^^^^^^^^^ the trait `From<Arc<rustls::ServerConfig>>` is not implemented for `TlsAcceptor`
|
= help: the trait `From<Arc<tokio_rustls::rustls::ServerConfig>>` is implemented for `TlsAcceptor`
= help: for that trait implementation, expected `tokio_rustls::rustls::ServerConfig`, found `rustls::ServerConfig`
error[E0599]: no method named `with_safe_defaults` found for struct `rustls::ConfigBuilder` in the current scope
--> src/tls_rustls/mod.rs:273:10
|
272 | let mut config = ServerConfig::builder()
| ______________________-
273 | | .with_safe_defaults()
| | -^^^^^^^^^^^^^^^^^^ method not found in `ConfigBuilder<ServerConfig, WantsVerifier>`
| |_________|
|
error[E0277]: the `?` operator can only be applied to values that implement `Try`
--> src/tls_rustls/mod.rs:286:16
|
286 | let cert = rustls_pemfile::certs(&mut cert.as_ref())?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `?` operator cannot be applied to type `impl Iterator<Item = Result<rustls_pki_types::CertificateDer<'static>, std::io::Error>> + '_`
|
= help: the trait `Try` is not implemented for `impl Iterator<Item = Result<rustls_pki_types::CertificateDer<'static>, std::io::Error>> + '_`
error[E0277]: the `?` operator can only be applied to values that implement `Try`
--> src/tls_rustls/mod.rs:288:37
|
288 | let mut key_vec: Vec<Vec<u8>> = rustls_pemfile::read_all(&mut key.as_ref())?
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `?` operator cannot be applied to type `impl Iterator<Item = Result<Item, std::io::Error>> + '_`
|
= help: the trait `Try` is not implemented for `impl Iterator<Item = Result<Item, std::io::Error>> + '_`
error[E0599]: no variant or associated item named `RSAKey` found for enum `Item` in the current scope
--> src/tls_rustls/mod.rs:291:19
|
291 | Item::RSAKey(key) | Item::PKCS8Key(key) | Item::ECKey(key) => Some(key),
| ^^^^^^ variant or associated item not found in `Item`
error[E0599]: no variant or associated item named `PKCS8Key` found for enum `Item` in the current scope
--> src/tls_rustls/mod.rs:291:39
|
291 | Item::RSAKey(key) | Item::PKCS8Key(key) | Item::ECKey(key) => Some(key),
| ^^^^^^^^
| |
| variant or associated item not found in `Item`
| help: there is a variant with a similar name (notice the capitalization): `Pkcs8Key`
error[E0599]: no variant or associated item named `ECKey` found for enum `Item` in the current scope
--> src/tls_rustls/mod.rs:291:61
|
291 | Item::RSAKey(key) | Item::PKCS8Key(key) | Item::ECKey(key) => Some(key),
| ^^^^^ variant or associated item not found in `Item`
I'm getting the following error when I try to pass a SocketAddr
type into bind
/ bind_rustls
:
axum_server::bind_rustls(addr)
^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::vec::IntoIter`, found struct `std::option::IntoIter`
Details:
I'm trying to implement TLS on my axum server. I have a variable addr
of type SocketAddr
. I thought I would be able to pass this directly into bind
/ bind_rustls
because it accepts A: ToSocketAddrs
and there's an impl ToSocketAddrs for SocketAddr
.
I see that impl ToSocketAddrs for String
uses std::vec::IntoIter
while impl ToSocketAddrs for SocketAddr
uses std::option::IntoIter
(possibly because it's a single address). I tried forking the repo and changing std::vec::IntoIter
to std::iter::Iterable
to support both, but seems like that was a bit naive. I get another error "doesn't have a size known at compile-time" because of the BoxedToSocketAddrs
type.
For now, I've gotten past it by doing addr.to_string()
. Happy to submit a PR if I get some guidance on how to make this work. Thanks!
How do I enable support for mTLS?
We are using a PEM file that contains both the CERTIFICATE and PRIVATE KEY in one file. The file looks like this
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
And I load it though the from_pem_file
similar to this
let path = "/path/to/combo.pem";
let tls_config = RustlsConfig::from_pem_file(path, path);
This breaks however if the combo PEM file places the certificates above the key with a private key format not supported
error. I believe the problem stems from here which assumes the key will always be the first section. I understand this is because you expect two separate files to be passed in and I'm cheating a bit by passing in the same file for both.
Our systems don't easily allow us to edit the PEM files and I would like to avoid reading in the file and manually splitting it into separate byte arrays to pass to the from_pem
method.
Please consider supporting this functionality. Maybe with a from_pem_combo_file
or iterating over the whole file for a key rather than just the first section.
Thank you
Hi there!
I was interested to add support for this library to use openssl as an alternative to rustls. Is that something you'd be interested in if I were to contribute that?
Thanks :)
I would like to wrap a user supplied app, like a Router
, for that purpose I would like to use ServiceBuilder
.
The issue is that Server::serve()
needs a service factory, like what can be produced with Router::into_make_service()
, which isn't available for ServiceBuilder
.
The solution I found is wrapping it in Shared
. I believe some documentation in axum-server
would help.
See tokio-rs/axum#1203 for the original problem.
axum got updated and tls examples aren't working
https://github.com/tokio-rs/axum/blob/2e963cea9d35cd6b2583670146d25842db1f3618/examples/tls-rustls/src/main.rs#L30
Hi,
I'm building a server which auto updates, but when it restarts I get "address in use" exceptions. As far as I can tell from hyperium/hyper#1575 I need to set reuse_addr, but I can't seem to find a way to do it through axum_server. Any chance it could be added? (I'm also using rustls).
I was a bit confused by this, since None
might indicate an immediate shutdown, or it might indicate an indefinite grace period.
I recently decided to port my application to the axum
framework, and intend for my Rust binary to do its own SSL. This crate seems like the best way to achieve that (especially since it supports hot-swapping certificates 🎉). However, one of the issues I had with a previous web framework was severe, persistent TCP connection leaks, due in part to a lack of a timeout on accepting SSL connections (and also during http2 upgrade, but that's outside the scope of this crate). When I took a look at the source code, I couldn't help but notice that no timeouts or sleeps were incorporated into the relevant Future
s. This leads me to believe that, were I to deploy my application in production, I would see TCP connection counts steadily climb as real clients have a habit of silently hanging up their connection at the worst time.
So my questions are:
I am trying to build a hybrid HTTP + gRPC server using Axum and Tonic following this example. I am struggling a bit however on how to combine this with axum-server to have both over TLS:
let app = /* axum app */;
let grpc_service = tonic::transport::Server::builder().add_service(/* some gRPC service */).into_service();
let hybrid_make_service = hybrid(app.into_make_service(), grpc_service);
// This works fine
axum::Server::bind(&sc.bind_address).serve(hybrid_make_service).await?;
// This I can't get right...
axum_server::bind_rustls(sc.bind_address.to_string())
.private_key_file(tls_key_path)
.certificate_file(tls_cert_path)
.serve(hybrid_make_service)
.await?;
The issue seems to be that axum_server requires a 'Service' whereas the regular 'bind' accepts some sort of 'IntoService' (and the hybrid
function, as well as grpc_service, create this). Any ideas?
Lately Axum has modified the internal implementation of Router
and they have started boxing the routers by default (https://github.com/tokio-rs/axum). Unfortunately these new changes don't work with axum_server
.
I get following compilation error:
error[E0277]: `(dyn axum::clone_box_service::CloneService<axum::http::Request<axum::body::Body>, Response = axum::http::Response<http_body::combinators::box_body::UnsyncBoxBody<bytes::Bytes, axum::Error>>, Error = Infallible, Future = Pin<Box<(dyn futures_util::Future<Output = Result<axum::http::Response<http_body::combinators::box_body::UnsyncBoxBody<bytes::Bytes, axum::Error>>, Infallible>> + std::marker::Send + 'static)>>> + std::marker::Send + 'static)` cannot be shared between threads safely
I have a use-case where I implemented my own Accept
or. Additionally I would like to add more configuration options, but currently it's not possible to modify the acceptor once wrapped in Server
. My proposal is to add a method that can return a mutable reference to the Acceptor
called Server::acceptor_mut()
, bikeshedding welcome.
The alternative is to use the Server::acceptor()
to add a pre-configured Accept
or, but the user-ergonomics are slightly worse that way.
Happy to make a PR if this is approved.
from_pem_file() calls eventually to config_from_pem() which restricts the keys to be of type Some(Item::RSAKey(key))
or Some(Item::PKCS8Key(key))
and this rejects a lot of EC keys. One either needs to convert it from SEC1 to PKCS8 or directly encode into DER and load the files by hand to call from_der()
instead.
Adding proper support for EC keys should be as simple as adding Some(Item::ECKey(key))
to the match clause of config_from_pem()
. I'd be happy to open a pull request for it.
My use case is I need to use different server configs based on the SNI value (multiple domains each with different certificates and client certificate authentication requirements). Rustls offers an acceptor for this use case. I can see that there is an Accept trait for similiar use case. However looking at the code the default RustlsAcceptor uses a RustlsAcceptorFuture which actually setups the rustls. Should I make my own Acceptor if so do I need to replicate the behaviour of RustlsAcceptorFuture ?
More functionality on following types can be exposed:
I am curious what the main benefit to running this server instead of the default that axum crate uses.
Why should someone consider using this crate in their web API?
I would like to load TLS certificates to be used for the request based on the hostname.
A method of doing this is specifying something that implements the ResolvesServerCert trait in the cert_resolver
attribute of the rustls
ServerConfig. This is nice and easy to do but requires the resolve
method to be synchronous.
As discussed in this issue, the correct method of asynchronously resolving certificates based on the hostname would be to use the rustls
Acceptor to handle the connection before the configuration is finalized. One would then be able to asynchronously load the certificates, build the config
, and generate a ServerConnection with it.
I see that axum-server
has the ability to provide a custom Acceptor for a server and has an example of it.
I am, however, unable to get this to asynchronously resolve certificates for the connection. I don't know if this is because I just can't figure out how to do it correctly or if axum-server
does not currently support this functionality.
Is this possible in axum-server
? If not, I am more than happy to create a PR to do this; I will need some hints on what needs to be done though.
Currently there is no way to shut a connection down except signaling a global shutdown.
Having this ability can be useful to detect slow clients and prevent some attacks.
Would be really nice if interested people test master
branch time to time with new features.
I am doing some tests locally but it is never enough.
I’ve read the reload example which is fantastic for TLS certificate renewal, and it made me wondering if we can do something similar for the binding addr? (In case of configuration change)
When i switched from tls-openssl
to tls-rustls
i noticed that the RustlsConfig
doesn't implement from_pem_chain_file
like the OpenSSLConfig
.
I managed to solve this in my Project by using rustls's ServerConfig::builder()
and rustls_pemfile
.
I think it would be good if axum_server
implemented this functionality because working with such pem files is common Practice and it is already implemented for RustlsConfig
. This would also decouple the fn from the Feature used.
I would Expect a function with the following Signature:
fn from_pem_chain_file<A: AsRef<Path>, B: AsRef<Path>>(
chain: A,
key: B,
) -> Result<RustlsConfig, RustlsError>;
Since the OpenSSLConfig has this Implementation.
My Current Solution:
use axum_server::tls_rustls::RustlsConfig;
use miette::{Diagnostic, Result};
use rustls::{Certificate, ServerConfig};
use rustls_pemfile::Item;
use std::{fs::File, io::BufReader, path::Path, sync::Arc};
use thiserror::Error;
pub fn rustls_from_pem_chain_file<A: AsRef<Path>, B: AsRef<Path>>(
chain: A,
key: B,
) -> Result<RustlsConfig, RustlsConfigError> {
let mut chain_file: BufReader<File> = BufReader::new(File::open(chain)?);
let chain_cert: Vec<Certificate> = rustls_pemfile::certs(&mut chain_file)?
.into_iter()
.map(rustls::Certificate)
.collect();
let mut key_file: BufReader<File> = BufReader::new(File::open(key)?);
let key_cert: rustls::PrivateKey = match rustls_pemfile::read_one(&mut key_file)?
.ok_or_else(|| RustlsConfigError::ParseError())?
{
Item::PKCS8Key(cert) => Ok(rustls::PrivateKey(cert)),
x => Err(RustlsConfigError::PemError(x)),
}?;
let server_config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(chain_cert, key_cert)?;
Ok(RustlsConfig::from_config(Arc::new(server_config)))
}
#[derive(Error, Debug, Diagnostic)]
pub enum RustlsConfigError {
#[error("Could not load pem file")]
#[diagnostic(code(rustls_file_error))]
FileError(#[from] std::io::Error),
#[error("Could parse pem file")]
#[diagnostic(code(rustls_parse_error))]
ParseError(),
#[error("Invalid TLS certificate format, recieved: {0:?}")]
#[diagnostic(code(rustls_pem_error))]
PemError(Item),
#[error("Invalid TLS certificate")]
#[diagnostic(code(rustls_error))]
RustlsError(#[from] rustls::Error),
}
As a developer of an axum-server application, which runs behind a layer 3 load balancing technology, I need to extract the clients IP, which is passed through with the PROXY Protocol:
https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
I'd like to have axum-server optionally decode PROXY from the tcp stream and add it as an X-Forwarded-For
or Forwarded
header to the HTTP request.
I already started an implementation in axum-server which intercepts the tcp stream to check for PROXY header as a solution for my case.
I like the simplicity and functionality axum-server provides, and it was more straightforward for me to implement in the axum-server codebase than to write a lower level implementation from scratch using tokio to get access to the tcp stream.
I would like to know the receptiveness of the axum-server team for me putting forward a PR for this feature.
Additional context
https://www.haproxy.com/blog/use-the-proxy-protocol-to-preserve-a-clients-ip-address
https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#client-ip-preservation
Hi,
due to performance reasons (difference between 1ms and 100ms and 100mb vs 2GB memory usage) i need to add a hyper (not tower) layer that comes before everything for my proxy implementation. I had to rewrite my code based on your serve function and inject the proxy as the lowest service.
I was wondering if you would consider adding a mechanic like Acceptor
but used to modify the final tower service before it is served ?
Why do you have to obtain a handle and spawn a task with axum-server instead of having the same API as hyper (Server::with_graceful_shutdown
)?
Greetings!
I want to implement version with native-tls
to this crate, but I don't sure which way is right.
I can just copy-paste tls_rustls
folder and rewrite it to native-tls
, but you use tokio-rustls
which have tokio-native-tls
with same API, and it will be a lot of same code.
Or, I can change current implementation through cfg flag.
axum-server isn't an ideal name because this project is not strictly related to Axum, and that can be confusing. This issue is for discussion regarding options for different names.
Some initial ideas:
When the SockAddr
bind
port is specified as 0, the kernel picks a random port to listen on. How does one gain access to this bound listener port? axum
allows access to https://docs.rs/hyper/0.14.18/hyper/server/struct.Server.html#method.local_addr, how can one obtain the equivalent information using axum-server
?
Not really much to add to here.
If the server is dropped before binding to a socket, Handle::listening()
can get stuck. This could e.g. happen in the following scenario:
use axum::Router;
use axum_server::Handle;
struct Server {
addr: SocketAddr,
task_handle: tokio::task::JoinHandle<()>,
server_handle: Handle,
}
impl Server {
async fn start(router: Router, addr: SocketAddr) -> Self {
let handle = Handle::new();
let server = axum_server::Server::bind(addr).handle(handle).serve(router.into_make_service());
let task_handle = tokio::spawn(server);
let addr = handle.listening().await; // this will hang forever if the `serve()` future fails binding
Server {
task_handle,
addr,
}
}
}
#[tokio::main]
async fn main() {
let srv1 = Server::start(Router::new(), ([127.0.0.1], 0).into()).await;
let addr1 = srv1.addr;
let srv2 = Server::start(Router::new(), addr).await; // This call never returns
}
There are some other similar scenarios, but they're all in equal in the listening()
future getting stuck indefinitely if the Server
never sets the address.
There's a few possible workarounds:
listening()
future in a timeoutlistening()
return Option<SocketAddr>
and let the caller handle the waiting (should be fairly straight forward, address should be available after starting to await the serve()
future)SocketAddr
and enable access through a getterI ran into this issue while implementing something similar to the example above but paired with a shutdown listener that immediately evaluated. This introduced a race condition between dropping the server future because of the shutdown listener and the server binding to the socket. If the server managed to bind to the socket, the listening()
future finished, if the shutdown listener evaluated first, the listening()
future got stuck.
In my case it was solvable by spawning the server task through tokio::spawn()
and putting the Handle::listening
call in a select!{}
together with the server's JoinHandle
, thus if the server task exitted (e.g. because it can't bind to the socket), the listening
call would stop and I could return the error from start()
. Actual code here
Hi,
When using Axum with WebSockets, I noticed having an open WebSocket doesn't stop the server from shutting down gracefully. Here's a demo repository.
My expectation was that the demo would remain running for 30 seconds, printing "beep" every second until the graceful shutdown timeout is reached.
I'm not terribly familiar with Hyper so I don't immediately see a fix, so I figured I'd start with an issue. I'm happy to work on a fix, however.
Hi, I was reading through the code and I noticed this list of connections
axum-server/src/server/http_server.rs
Line 116 in 8574eaa
it seems to grow every time the server accepts a connection and is never cleared out, so it might be an issue for a long-running server
I tried making lots of requests to the hello_world example server and after ~500,000 requests memory usage was about 1 GB
Hello, thanks for your nice library!
Currently, app.into_make_service_with_connect_info::<SocketAddr>()
is not support.
use axum::{
routing::get,
Router,
};
let app = Router::new().route("/", get(|| async { "Hi!" }));
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.expect("server failed");
https://docs.rs/axum/latest/src/axum/routing/mod.rs.html#384-388
axum-server/src/tls_rustls/mod.rs
Line 278 in 86bc6e7
This is wrong as the axum server doesn't necessarily support http2.
Can you please add support for axum 0.6?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.