mod config;
mod r#protocol;
pub(crate) use config::*;
pub(crate) use r#protocol::*;
use std::{env::var, io::Error, net::SocketAddr, process::exit, sync::Arc};
use bytes::{Buf, Bytes};
use h2::{RecvStream, SendStream, server::handshake};
use h3::{
error::StreamError,
server::{RequestResolver, RequestStream, builder},
};
use h3_quinn::BidiStream;
use http::{HeaderValue, Response, header::CONTENT_LENGTH};
use quinn::{Connection, Endpoint, crypto::rustls::QuicServerConfig};
use rustls::{
ServerConfig, SupportedProtocolVersion,
crypto::ring::default_provider,
pki_types::{CertificateDer, PrivateKeyDer, pem::PemObject},
version::{TLS12, TLS13},
};
use rustls_pki_types::pem;
use tokio::{
io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt},
net::{TcpListener, TcpStream},
spawn,
};
use tokio_rustls::{TlsAcceptor, server::TlsStream};
pub(crate) type HttpError = http::Error;
pub(crate) type Http2Error = h2::Error;
pub(crate) type Http2Connection = h2::server::Connection<Box<dyn Transport>, Bytes>;
pub(crate) type Http3Transport = h3_quinn::Connection;
pub(crate) type Http3Connection = h3::server::Connection<Http3Transport, Bytes>;
pub(crate) type QuicEndpointConfig = quinn::ServerConfig;
pub async fn serve() -> Result<(), String> {
let address: String = match var(ENV_ADDRESS) {
Ok(address) => address,
Err(_) => DEFAULT_ADDRESS.to_owned(),
};
let protocol: Protocol = match var(ENV_PROTOCOL) {
Ok(name) => match Protocol::from_name(&name) {
Some(protocol) => protocol,
None => return Err(format!("unknown protocol {name}")),
},
Err(_) => Protocol::Http1,
};
let certificate: Option<String> = var(ENV_TLS_CERTIFICATE).ok();
let key: Option<String> = var(ENV_TLS_KEY).ok();
match protocol {
Protocol::Http3 => serve_http3(&address, certificate, key).await,
_ => serve_stream(&address, protocol, certificate, key).await,
}
}
async fn serve_stream(
address: &str,
protocol: Protocol,
certificate: Option<String>,
key: Option<String>,
) -> Result<(), String> {
let listener: TcpListener = match TcpListener::bind(address).await {
Ok(listener) => listener,
Err(error) => return Err(error.to_string()),
};
let acceptor: Option<TlsAcceptor> = match (protocol.uses_tls(), certificate, key) {
(true, Some(certificate), Some(key)) => {
let config: ServerConfig = build_server_config(&certificate, &key, protocol.alpn())?;
Some(TlsAcceptor::from(Arc::new(config)))
}
(true, _, _) => return Err("the TLS protocol needs a certificate and a key".to_owned()),
_ => None,
};
println!("hyperlane-performance-control ready on {address} speaking {protocol:?}");
loop {
let (stream, _): (TcpStream, _) = match listener.accept().await {
Ok(accepted) => accepted,
Err(error) => return Err(error.to_string()),
};
let _: Result<(), Error> = stream.set_nodelay(true);
let acceptor: Option<TlsAcceptor> = acceptor.clone();
spawn(async move {
match acceptor {
Some(acceptor) => {
let session: TlsStream<TcpStream> = match acceptor.accept(stream).await {
Ok(session) => session,
Err(_) => return,
};
let transport: Box<dyn Transport> = Box::new(session);
let _: Result<(), String> = match protocol {
Protocol::H2c | Protocol::H2Tls => serve_http2(transport).await,
_ => serve_http1(transport).await,
};
}
None => {
let transport: Box<dyn Transport> = Box::new(stream);
let _: Result<(), String> = match protocol {
Protocol::H2c | Protocol::H2Tls => serve_http2(transport).await,
_ => serve_http1(transport).await,
};
}
}
});
}
}
async fn serve_http3(
address: &str,
certificate: Option<String>,
key: Option<String>,
) -> Result<(), String> {
let (Some(certificate), Some(key)) = (certificate, key) else {
return Err(ERROR_HTTP3_NEEDS_TLS.to_owned());
};
let config: ServerConfig = build_server_config(&certificate, &key, ALPN_HTTP3)?;
let quic: QuicServerConfig = match QuicServerConfig::try_from(config) {
Ok(quic) => quic,
Err(error) => return Err(error.to_string()),
};
let bind: SocketAddr = match address.parse() {
Ok(bind) => bind,
Err(error) => return Err(error.to_string()),
};
let endpoint: Endpoint =
match Endpoint::server(QuicEndpointConfig::with_crypto(Arc::new(quic)), bind) {
Ok(endpoint) => endpoint,
Err(error) => return Err(error.to_string()),
};
println!("hyperlane-performance-control ready on {address} speaking Http3");
while let Some(incoming) = endpoint.accept().await {
let connection: Connection = match incoming.await {
Ok(connection) => connection,
Err(_) => continue,
};
spawn(async move {
let _: Result<(), String> = serve_http3_connection(connection).await;
});
}
Ok(())
}
#[tokio::main]
async fn main() {
if let Err(error) = serve().await {
eprintln!("{error}");
exit(1);
}
}