From bc919b94784af802e1e9820532bc53f170e42c21 Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Sun, 30 Jul 2023 10:13:29 +0900 Subject: [PATCH 01/14] docs: changelog --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a11094..16c6339 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # CHANGELOG -## 0.4.0 (unreleased) +## 0.5.0 (unreleased) + +### Improvement + +## 0.4.0 ### Improvement From fb389a6aab21683957402b20c223ea1398f763a4 Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Sun, 30 Jul 2023 11:17:21 +0900 Subject: [PATCH 02/14] refactor: split quic layer and h3 layer --- .gitmodules | 3 + Cargo.toml | 2 +- rpxy-bin/Cargo.toml | 2 +- rpxy-lib/src/lib.rs | 1 + rpxy-lib/src/proxy/mod.rs | 2 + rpxy-lib/src/proxy/proxy_h3.rs | 113 ++++++++++++++-------------- rpxy-lib/src/proxy/proxy_quic.rs | 124 +++++++++++++++++++++++++++++++ rpxy-lib/src/proxy/proxy_tls.rs | 99 ------------------------ s2n-quic | 1 + 9 files changed, 188 insertions(+), 159 deletions(-) create mode 100644 rpxy-lib/src/proxy/proxy_quic.rs create mode 160000 s2n-quic diff --git a/.gitmodules b/.gitmodules index b9069a0..59b7ea8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "quinn"] path = quinn url = git@github.com:junkurihara/quinn.git +[submodule "s2n-quic"] + path = s2n-quic + url = git@github.com:junkurihara/s2n-quic.git diff --git a/Cargo.toml b/Cargo.toml index 64d1414..aa65657 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] members = ["rpxy-bin", "rpxy-lib"] -exclude = ["quinn", "h3-quinn", "h3"] +exclude = ["quinn", "h3-quinn", "h3", "s2n-quic"] [profile.release] codegen-units = 1 diff --git a/rpxy-bin/Cargo.toml b/rpxy-bin/Cargo.toml index 0fc2ae4..3d1f90c 100644 --- a/rpxy-bin/Cargo.toml +++ b/rpxy-bin/Cargo.toml @@ -13,7 +13,7 @@ publish = false [features] default = ["http3"] -http3 = [] +http3 = ["rpxy-lib/http3"] [dependencies] rpxy-lib = { path = "../rpxy-lib/", features = ["http3", "sticky-cookie"] } diff --git a/rpxy-lib/src/lib.rs b/rpxy-lib/src/lib.rs index 72f8a8a..1915b68 100644 --- a/rpxy-lib/src/lib.rs +++ b/rpxy-lib/src/lib.rs @@ -44,6 +44,7 @@ where if proxy_config.https_port.is_some() { info!("Listen port: {} (for TLS)", proxy_config.https_port.unwrap()); } + #[cfg(feature = "http3")] if proxy_config.http3 { info!("Experimental HTTP/3.0 is enabled. Note it is still very unstable."); } diff --git a/rpxy-lib/src/proxy/mod.rs b/rpxy-lib/src/proxy/mod.rs index 749239c..05d63b0 100644 --- a/rpxy-lib/src/proxy/mod.rs +++ b/rpxy-lib/src/proxy/mod.rs @@ -3,6 +3,8 @@ mod proxy_client_cert; #[cfg(feature = "http3")] mod proxy_h3; mod proxy_main; +#[cfg(feature = "http3")] +mod proxy_quic; mod proxy_tls; mod socket; diff --git a/rpxy-lib/src/proxy/proxy_h3.rs b/rpxy-lib/src/proxy/proxy_h3.rs index 324060f..eac6dbf 100644 --- a/rpxy-lib/src/proxy/proxy_h3.rs +++ b/rpxy-lib/src/proxy/proxy_h3.rs @@ -1,7 +1,7 @@ use super::Proxy; use crate::{certs::CryptoSource, error::*, log::*, utils::ServerNameBytesExp}; use bytes::{Buf, Bytes}; -use h3::{quic::BidiStream, server::RequestStream}; +use h3::{quic::BidiStream, quic::Connection as ConnectionQuic, server::RequestStream}; use hyper::{client::connect::Connect, Body, Request, Response}; use std::net::SocketAddr; use tokio::time::{timeout, Duration}; @@ -11,67 +11,64 @@ where T: Connect + Clone + Sync + Send + 'static, U: CryptoSource + Clone + Sync + Send + 'static, { - pub(super) async fn connection_serve_h3( + pub(super) async fn connection_serve_h3( self, - conn: quinn::Connecting, + quic_connection: C, tls_server_name: ServerNameBytesExp, - ) -> Result<()> { - let client_addr = conn.remote_address(); - - match conn.await { - Ok(new_conn) => { - let mut h3_conn = h3::server::Connection::<_, bytes::Bytes>::new(h3_quinn::Connection::new(new_conn)).await?; - info!( - "QUIC/HTTP3 connection established from {:?} {:?}", - client_addr, tls_server_name - ); - // TODO: Is here enough to fetch server_name from NewConnection? - // to avoid deep nested call from listener_service_h3 - loop { - // this routine follows hyperium/h3 examples https://github.com/hyperium/h3/blob/master/examples/server.rs - match h3_conn.accept().await { - Ok(None) => { - break; - } - Err(e) => { - warn!("HTTP/3 error on accept incoming connection: {}", e); - match e.get_error_level() { - h3::error::ErrorLevel::ConnectionError => break, - h3::error::ErrorLevel::StreamError => continue, - } - } - Ok(Some((req, stream))) => { - // We consider the connection count separately from the stream count. - // Max clients for h1/h2 = max 'stream' for h3. - let request_count = self.globals.request_count.clone(); - if request_count.increment() > self.globals.proxy_config.max_clients { - request_count.decrement(); - h3_conn.shutdown(0).await?; - break; - } - debug!("Request incoming: current # {}", request_count.current()); - - let self_inner = self.clone(); - let tls_server_name_inner = tls_server_name.clone(); - self.globals.runtime_handle.spawn(async move { - if let Err(e) = timeout( - self_inner.globals.proxy_config.proxy_timeout + Duration::from_secs(1), // timeout per stream are considered as same as one in http2 - self_inner.stream_serve_h3(req, stream, client_addr, tls_server_name_inner), - ) - .await - { - error!("HTTP/3 failed to process stream: {}", e); - } - request_count.decrement(); - debug!("Request processed: current # {}", request_count.current()); - }); - } + client_addr: SocketAddr, + ) -> Result<()> + where + C: ConnectionQuic, + >::BidiStream: BidiStream + Send + 'static, + <>::BidiStream as BidiStream>::RecvStream: Send, + <>::BidiStream as BidiStream>::SendStream: Send, + { + let mut h3_conn = h3::server::Connection::<_, Bytes>::new(quic_connection).await?; + info!( + "QUIC/HTTP3 connection established from {:?} {:?}", + client_addr, tls_server_name + ); + // TODO: Is here enough to fetch server_name from NewConnection? + // to avoid deep nested call from listener_service_h3 + loop { + // this routine follows hyperium/h3 examples https://github.com/hyperium/h3/blob/master/examples/server.rs + match h3_conn.accept().await { + Ok(None) => { + break; + } + Err(e) => { + warn!("HTTP/3 error on accept incoming connection: {}", e); + match e.get_error_level() { + h3::error::ErrorLevel::ConnectionError => break, + h3::error::ErrorLevel::StreamError => continue, } } - } - Err(err) => { - warn!("QUIC accepting connection failed: {:?}", err); - return Err(RpxyError::QuicConn(err)); + Ok(Some((req, stream))) => { + // We consider the connection count separately from the stream count. + // Max clients for h1/h2 = max 'stream' for h3. + let request_count = self.globals.request_count.clone(); + if request_count.increment() > self.globals.proxy_config.max_clients { + request_count.decrement(); + h3_conn.shutdown(0).await?; + break; + } + debug!("Request incoming: current # {}", request_count.current()); + + let self_inner = self.clone(); + let tls_server_name_inner = tls_server_name.clone(); + self.globals.runtime_handle.spawn(async move { + if let Err(e) = timeout( + self_inner.globals.proxy_config.proxy_timeout + Duration::from_secs(1), // timeout per stream are considered as same as one in http2 + self_inner.stream_serve_h3(req, stream, client_addr, tls_server_name_inner), + ) + .await + { + error!("HTTP/3 failed to process stream: {}", e); + } + request_count.decrement(); + debug!("Request processed: current # {}", request_count.current()); + }); + } } } diff --git a/rpxy-lib/src/proxy/proxy_quic.rs b/rpxy-lib/src/proxy/proxy_quic.rs new file mode 100644 index 0000000..0e660c1 --- /dev/null +++ b/rpxy-lib/src/proxy/proxy_quic.rs @@ -0,0 +1,124 @@ +use super::socket::bind_udp_socket; +use super::{ + crypto_service::{ServerCrypto, ServerCryptoBase}, + proxy_main::Proxy, +}; +use crate::{certs::CryptoSource, error::*, log::*, utils::BytesName}; +use hot_reload::ReloaderReceiver; +use hyper::client::connect::Connect; +use quinn::{crypto::rustls::HandshakeData, Endpoint, ServerConfig as QuicServerConfig, TransportConfig}; +use rustls::ServerConfig; +use std::sync::Arc; + +impl Proxy +where + T: Connect + Clone + Sync + Send + 'static, + U: CryptoSource + Clone + Sync + Send + 'static, +{ + pub(super) async fn listener_service_h3( + &self, + mut server_crypto_rx: ReloaderReceiver, + ) -> Result<()> { + info!("Start UDP proxy serving with HTTP/3 request for configured host names"); + // first set as null config server + let rustls_server_config = ServerConfig::builder() + .with_safe_default_cipher_suites() + .with_safe_default_kx_groups() + .with_protocol_versions(&[&rustls::version::TLS13])? + .with_no_client_auth() + .with_cert_resolver(Arc::new(rustls::server::ResolvesServerCertUsingSni::new())); + + let mut transport_config_quic = TransportConfig::default(); + transport_config_quic + .max_concurrent_bidi_streams(self.globals.proxy_config.h3_max_concurrent_bidistream) + .max_concurrent_uni_streams(self.globals.proxy_config.h3_max_concurrent_unistream) + .max_idle_timeout( + self + .globals + .proxy_config + .h3_max_idle_timeout + .map(|v| quinn::IdleTimeout::try_from(v).unwrap()), + ); + + let mut server_config_h3 = QuicServerConfig::with_crypto(Arc::new(rustls_server_config)); + server_config_h3.transport = Arc::new(transport_config_quic); + server_config_h3.concurrent_connections(self.globals.proxy_config.h3_max_concurrent_connections); + + // To reuse address + let udp_socket = bind_udp_socket(&self.listening_on)?; + let runtime = quinn::default_runtime() + .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "No async runtime found"))?; + let endpoint = Endpoint::new( + quinn::EndpointConfig::default(), + Some(server_config_h3), + udp_socket, + runtime, + )?; + + let mut server_crypto: Option> = None; + loop { + tokio::select! { + new_conn = endpoint.accept() => { + if server_crypto.is_none() || new_conn.is_none() { + continue; + } + let mut conn: quinn::Connecting = new_conn.unwrap(); + let Ok(hsd) = conn.handshake_data().await else { + continue + }; + + let Ok(hsd_downcast) = hsd.downcast::() else { + continue + }; + let Some(new_server_name) = hsd_downcast.server_name else { + warn!("HTTP/3 no SNI is given"); + continue; + }; + debug!( + "HTTP/3 connection incoming (SNI {:?})", + new_server_name + ); + // TODO: server_nameをここで出してどんどん深く投げていくのは効率が悪い。connecting -> connectionsの後でいいのでは? + // TODO: 通常のTLSと同じenumか何かにまとめたい + let self_clone = self.clone(); + self.globals.runtime_handle.spawn(async move { + let client_addr = conn.remote_address(); + let quic_connection = match conn.await { + Ok(new_conn) => { + info!("New connection established"); + h3_quinn::Connection::new(new_conn) + }, + Err(e) => { + warn!("QUIC accepting connection failed: {:?}", e); + return Err(RpxyError::QuicConn(e)); + } + }; + // Timeout is based on underlying quic + if let Err(e) = self_clone.connection_serve_h3(quic_connection, new_server_name.to_server_name_vec(), client_addr).await { + warn!("QUIC or HTTP/3 connection failed: {}", e); + }; + Ok(()) + }); + } + _ = server_crypto_rx.changed() => { + if server_crypto_rx.borrow().is_none() { + error!("Reloader is broken"); + break; + } + let cert_keys_map = server_crypto_rx.borrow().clone().unwrap(); + + server_crypto = (&cert_keys_map).try_into().ok(); + let Some(inner) = server_crypto.clone() else { + error!("Failed to update server crypto for h3"); + break; + }; + endpoint.set_server_config(Some(QuicServerConfig::with_crypto(inner.clone().inner_global_no_client_auth.clone()))); + + } + else => break + } + } + endpoint.wait_idle().await; + Ok(()) as Result<()> + } +} diff --git a/rpxy-lib/src/proxy/proxy_tls.rs b/rpxy-lib/src/proxy/proxy_tls.rs index 5512eff..b937b02 100644 --- a/rpxy-lib/src/proxy/proxy_tls.rs +++ b/rpxy-lib/src/proxy/proxy_tls.rs @@ -1,5 +1,3 @@ -#[cfg(feature = "http3")] -use super::socket::bind_udp_socket; use super::{ crypto_service::{CryptoReloader, ServerCrypto, ServerCryptoBase, SniServerCryptoMap}, proxy_main::{LocalExecutor, Proxy}, @@ -8,10 +6,6 @@ use super::{ use crate::{certs::CryptoSource, constants::*, error::*, log::*, utils::BytesName}; use hot_reload::{ReloaderReceiver, ReloaderService}; use hyper::{client::connect::Connect, server::conn::Http}; -#[cfg(feature = "http3")] -use quinn::{crypto::rustls::HandshakeData, Endpoint, ServerConfig as QuicServerConfig, TransportConfig}; -#[cfg(feature = "http3")] -use rustls::ServerConfig; use std::sync::Arc; use tokio::time::{timeout, Duration}; @@ -105,99 +99,6 @@ where Ok(()) as Result<()> } - #[cfg(feature = "http3")] - async fn listener_service_h3(&self, mut server_crypto_rx: ReloaderReceiver) -> Result<()> { - info!("Start UDP proxy serving with HTTP/3 request for configured host names"); - // first set as null config server - let rustls_server_config = ServerConfig::builder() - .with_safe_default_cipher_suites() - .with_safe_default_kx_groups() - .with_protocol_versions(&[&rustls::version::TLS13])? - .with_no_client_auth() - .with_cert_resolver(Arc::new(rustls::server::ResolvesServerCertUsingSni::new())); - - let mut transport_config_quic = TransportConfig::default(); - transport_config_quic - .max_concurrent_bidi_streams(self.globals.proxy_config.h3_max_concurrent_bidistream) - .max_concurrent_uni_streams(self.globals.proxy_config.h3_max_concurrent_unistream) - .max_idle_timeout( - self - .globals - .proxy_config - .h3_max_idle_timeout - .map(|v| quinn::IdleTimeout::try_from(v).unwrap()), - ); - - let mut server_config_h3 = QuicServerConfig::with_crypto(Arc::new(rustls_server_config)); - server_config_h3.transport = Arc::new(transport_config_quic); - server_config_h3.concurrent_connections(self.globals.proxy_config.h3_max_concurrent_connections); - - // To reuse address - let udp_socket = bind_udp_socket(&self.listening_on)?; - let runtime = quinn::default_runtime() - .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "No async runtime found"))?; - let endpoint = Endpoint::new( - quinn::EndpointConfig::default(), - Some(server_config_h3), - udp_socket, - runtime, - )?; - - let mut server_crypto: Option> = None; - loop { - tokio::select! { - new_conn = endpoint.accept() => { - if server_crypto.is_none() || new_conn.is_none() { - continue; - } - let mut conn: quinn::Connecting = new_conn.unwrap(); - let Ok(hsd) = conn.handshake_data().await else { - continue - }; - - let Ok(hsd_downcast) = hsd.downcast::() else { - continue - }; - let Some(new_server_name) = hsd_downcast.server_name else { - warn!("HTTP/3 no SNI is given"); - continue; - }; - debug!( - "HTTP/3 connection incoming (SNI {:?})", - new_server_name - ); - // TODO: server_nameをここで出してどんどん深く投げていくのは効率が悪い。connecting -> connectionsの後でいいのでは? - // TODO: 通常のTLSと同じenumか何かにまとめたい - let fut = self.clone().connection_serve_h3(conn, new_server_name.to_server_name_vec()); - self.globals.runtime_handle.spawn(async move { - // Timeout is based on underlying quic - if let Err(e) = fut.await { - warn!("QUIC or HTTP/3 connection failed: {}", e) - } - }); - } - _ = server_crypto_rx.changed() => { - if server_crypto_rx.borrow().is_none() { - error!("Reloader is broken"); - break; - } - let cert_keys_map = server_crypto_rx.borrow().clone().unwrap(); - - server_crypto = (&cert_keys_map).try_into().ok(); - let Some(inner) = server_crypto.clone() else { - error!("Failed to update server crypto for h3"); - break; - }; - endpoint.set_server_config(Some(QuicServerConfig::with_crypto(inner.clone().inner_global_no_client_auth.clone()))); - - } - else => break - } - } - endpoint.wait_idle().await; - Ok(()) as Result<()> - } - pub async fn start_with_tls(self, server: Http) -> Result<()> { let (cert_reloader_service, cert_reloader_rx) = ReloaderService::, ServerCryptoBase>::new( &self.globals.clone(), diff --git a/s2n-quic b/s2n-quic new file mode 160000 index 0000000..179acb8 --- /dev/null +++ b/s2n-quic @@ -0,0 +1 @@ +Subproject commit 179acb8a873eafbfc7b68de4018cd251caddfa44 From 0b1eb89ed10a5f101005ec5fe8fb543a4b7e7aea Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Sun, 30 Jul 2023 16:10:19 +0900 Subject: [PATCH 03/14] feat: initial support of s2n-quic --- CHANGELOG.md | 2 + README.md | 11 +- rpxy-bin/Cargo.toml | 8 +- rpxy-bin/src/config/toml.rs | 4 +- rpxy-bin/src/main.rs | 3 + rpxy-lib/Cargo.toml | 15 +- rpxy-lib/src/constants.rs | 2 +- rpxy-lib/src/error.rs | 12 +- rpxy-lib/src/globals.rs | 36 ++-- rpxy-lib/src/handler/handler_main.rs | 4 +- rpxy-lib/src/lib.rs | 5 +- rpxy-lib/src/proxy/crypto_service.rs | 155 ++++++++++++++---- rpxy-lib/src/proxy/mod.rs | 8 +- rpxy-lib/src/proxy/proxy_h3.rs | 3 + .../{proxy_quic.rs => proxy_quic_quinn.rs} | 6 +- rpxy-lib/src/proxy/proxy_quic_s2n.rs | 135 +++++++++++++++ rpxy-lib/src/proxy/proxy_tls.rs | 4 +- rpxy-lib/src/proxy/socket.rs | 6 +- 18 files changed, 343 insertions(+), 76 deletions(-) rename rpxy-lib/src/proxy/{proxy_quic.rs => proxy_quic_quinn.rs} (98%) create mode 100644 rpxy-lib/src/proxy/proxy_quic_s2n.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 16c6339..000a48c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Improvement +- Feat: `s2n-quic` with `s2n-quic-h3` is supported as QUIC and HTTP/3 library in addition to `quinn` with `h3-quinn`, related to #57. + ## 0.4.0 ### Improvement diff --git a/README.md b/README.md index c733a7e..eadc42c 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,9 @@ `rpxy` [ahr-pik-see] is an implementation of simple and lightweight reverse-proxy with some additional features. The implementation is based on [`hyper`](https://github.com/hyperium/hyper), [`rustls`](https://github.com/rustls/rustls) and [`tokio`](https://github.com/tokio-rs/tokio), i.e., written in pure Rust. Our `rpxy` routes multiple host names to appropriate backend application servers while serving TLS connections. - As default, `rpxy` provides the *TLS connection sanitization* by correctly binding a certificate used to establish a secure channel with the backend application. Specifically, it always keeps the consistency between the given SNI (server name indication) in `ClientHello` of the underlying TLS and the domain name given by the overlaid HTTP HOST header (or URL in Request line) [^1]. Additionally, as a somewhat unstable feature, our `rpxy` can handle the brand-new HTTP/3 connection thanks to [`quinn`](https://github.com/quinn-rs/quinn) and [`hyperium/h3`](https://github.com/hyperium/h3). + As default, `rpxy` provides the *TLS connection sanitization* by correctly binding a certificate used to establish a secure channel with the backend application. Specifically, it always keeps the consistency between the given SNI (server name indication) in `ClientHello` of the underlying TLS and the domain name given by the overlaid HTTP HOST header (or URL in Request line) [^1]. Additionally, as a somewhat unstable feature, our `rpxy` can handle the brand-new HTTP/3 connection thanks to [`quinn`](https://github.com/quinn-rs/quinn), [`s2n-quic`](https://github.com/aws/s2n-quic) and [`hyperium/h3`](https://github.com/hyperium/h3).[^h3lib] + + [^h3lib]: HTTP/3 libraries are mutually exclusive. You need to explicitly specify `s2n-quic` with `--no-default-features` flag. This project is still *work-in-progress*. But it is already working in some production environments and serves a number of domain names. Furthermore it *significantly outperforms* NGINX and Caddy, e.g., *1.5x faster than NGINX*, in the setting of a very simple HTTP reverse-proxy scenario (See [`bench`](./bench/) directory). @@ -27,11 +29,14 @@ You can build an executable binary yourself by checking out this Git repository. % git clone https://github.com/junkurihara/rust-rpxy % cd rust-rpxy -# Update submodule hyperium/h3 +# Update submodules % git submodule update --init -# Build +# Build (default: QUIC and HTTP/3 is enabled using `quinn`) % cargo build --release + +# If you want to use `s2n-quic`, build as follows. +% cargo build --no-default-features --features http3-s2n --release ``` Then you have an executive binary `rust-rpxy/target/release/rpxy`. diff --git a/rpxy-bin/Cargo.toml b/rpxy-bin/Cargo.toml index 3d1f90c..7e20882 100644 --- a/rpxy-bin/Cargo.toml +++ b/rpxy-bin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rpxy" -version = "0.4.0" +version = "0.5.0" authors = ["Jun Kurihara"] homepage = "https://github.com/junkurihara/rust-rpxy" repository = "https://github.com/junkurihara/rust-rpxy" @@ -14,9 +14,13 @@ publish = false [features] default = ["http3"] http3 = ["rpxy-lib/http3"] +http3-quinn = ["rpxy-lib/http3-quinn"] +http3-s2n = ["rpxy-lib/http3-s2n"] [dependencies] -rpxy-lib = { path = "../rpxy-lib/", features = ["http3", "sticky-cookie"] } +rpxy-lib = { path = "../rpxy-lib/", default-features = false, features = [ + "sticky-cookie", +] } anyhow = "1.0.72" rustc-hash = "1.1.0" diff --git a/rpxy-bin/src/config/toml.rs b/rpxy-bin/src/config/toml.rs index 84260c0..35c5d7a 100644 --- a/rpxy-bin/src/config/toml.rs +++ b/rpxy-bin/src/config/toml.rs @@ -142,10 +142,10 @@ impl TryInto for &ConfigToml { proxy_config.h3_max_concurrent_connections = x; } if let Some(x) = h3option.max_concurrent_bidistream { - proxy_config.h3_max_concurrent_bidistream = x.into(); + proxy_config.h3_max_concurrent_bidistream = x; } if let Some(x) = h3option.max_concurrent_unistream { - proxy_config.h3_max_concurrent_unistream = x.into(); + proxy_config.h3_max_concurrent_unistream = x; } if let Some(x) = h3option.max_idle_timeout { if x == 0u64 { diff --git a/rpxy-bin/src/main.rs b/rpxy-bin/src/main.rs index 8fe00dc..861c3d5 100644 --- a/rpxy-bin/src/main.rs +++ b/rpxy-bin/src/main.rs @@ -19,6 +19,9 @@ use crate::{ use hot_reload::{ReloaderReceiver, ReloaderService}; use rpxy_lib::entrypoint; +#[cfg(all(feature = "http3-quinn", feature = "http3-s2n"))] +compile_error!("feature \"http3-quinn\" and feature \"http3-s2n\" cannot be enabled at the same time"); + fn main() { init_logger(); diff --git a/rpxy-lib/Cargo.toml b/rpxy-lib/Cargo.toml index 3f405e8..063ca13 100644 --- a/rpxy-lib/Cargo.toml +++ b/rpxy-lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rpxy-lib" -version = "0.4.0" +version = "0.5.0" authors = ["Jun Kurihara"] homepage = "https://github.com/junkurihara/rust-rpxy" repository = "https://github.com/junkurihara/rust-rpxy" @@ -13,7 +13,9 @@ publish = false [features] default = ["http3", "sticky-cookie"] -http3 = ["quinn", "h3", "h3-quinn"] +http3 = ["http3-s2n"] +http3-quinn = ["quinn", "h3", "h3-quinn", "socket2"] +http3-s2n = ["h3", "s2n-quic", "s2n-quic-rustls", "s2n-quic-h3"] sticky-cookie = ["base64", "sha2", "chrono"] [dependencies] @@ -63,8 +65,13 @@ quinn = { path = "../quinn/quinn", optional = true } # Tentative to support rust h3 = { path = "../h3/h3/", optional = true } # h3-quinn = { path = "./h3/h3-quinn/", optional = true } h3-quinn = { path = "../h3-quinn/", optional = true } # Tentative to support rustls-0.21 -# for UDP socket wit SO_REUSEADDR -socket2 = { version = "0.5.3", features = ["all"] } +# for UDP socket wit SO_REUSEADDR when h3 with quinn +socket2 = { version = "0.5.3", features = ["all"], optional = true } +s2n-quic = { path = "../s2n-quic/quic/s2n-quic/", features = [ + "provider-tls-rustls", +], optional = true } +s2n-quic-h3 = { path = "../s2n-quic/quic/s2n-quic-h3/", optional = true } +s2n-quic-rustls = { path = "../s2n-quic/quic/s2n-quic-rustls/", optional = true } # cookie handling for sticky cookie chrono = { version = "0.4.26", default-features = false, features = [ diff --git a/rpxy-lib/src/constants.rs b/rpxy-lib/src/constants.rs index 9d7fb5e..39a93e7 100644 --- a/rpxy-lib/src/constants.rs +++ b/rpxy-lib/src/constants.rs @@ -17,7 +17,7 @@ pub const LOAD_CERTS_ONLY_WHEN_UPDATED: bool = true; // pub const H3_REQUEST_BUF_SIZE: usize = 65_536; // 64KB // handled by quinn #[allow(non_snake_case)] -#[cfg(feature = "http3")] +#[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] pub mod H3 { pub const ALT_SVC_MAX_AGE: u32 = 3600; pub const REQUEST_MAX_BODY_SIZE: usize = 268_435_456; // 256MB diff --git a/rpxy-lib/src/error.rs b/rpxy-lib/src/error.rs index 3407e8a..dd88a9a 100644 --- a/rpxy-lib/src/error.rs +++ b/rpxy-lib/src/error.rs @@ -37,14 +37,22 @@ pub enum RpxyError { // #[error("Toml Deserialization Error")] // TomlDe(#[from] toml::de::Error), - #[cfg(feature = "http3")] + #[cfg(feature = "http3-quinn")] #[error("Quic Connection Error")] QuicConn(#[from] quinn::ConnectionError), - #[cfg(feature = "http3")] + #[cfg(feature = "http3-s2n")] + #[error("Quic Connection Error [s2n-quic]")] + QUicConn(#[from] s2n_quic::connection::Error), + + #[cfg(feature = "http3-quinn")] #[error("H3 Error")] H3(#[from] h3::Error), + #[cfg(feature = "http3-s2n")] + #[error("H3 Error [s2n-quic]")] + H3(#[from] s2n_quic_h3::h3::Error), + #[error("rustls Connection Error")] Rustls(#[from] rustls::Error), diff --git a/rpxy-lib/src/globals.rs b/rpxy-lib/src/globals.rs index 44808dd..6186d84 100644 --- a/rpxy-lib/src/globals.rs +++ b/rpxy-lib/src/globals.rs @@ -53,19 +53,19 @@ pub struct ProxyConfig { // experimentals pub sni_consistency: bool, // Handler // All need to make packet acceptor - #[cfg(feature = "http3")] + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] pub http3: bool, - #[cfg(feature = "http3")] + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] pub h3_alt_svc_max_age: u32, - #[cfg(feature = "http3")] + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] pub h3_request_max_body_size: usize, - #[cfg(feature = "http3")] - pub h3_max_concurrent_bidistream: quinn::VarInt, - #[cfg(feature = "http3")] - pub h3_max_concurrent_unistream: quinn::VarInt, - #[cfg(feature = "http3")] + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] + pub h3_max_concurrent_bidistream: u32, + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] + pub h3_max_concurrent_unistream: u32, + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] pub h3_max_concurrent_connections: u32, - #[cfg(feature = "http3")] + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] pub h3_max_idle_timeout: Option, } @@ -87,19 +87,19 @@ impl Default for ProxyConfig { sni_consistency: true, - #[cfg(feature = "http3")] + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] http3: false, - #[cfg(feature = "http3")] + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] h3_alt_svc_max_age: H3::ALT_SVC_MAX_AGE, - #[cfg(feature = "http3")] + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] h3_request_max_body_size: H3::REQUEST_MAX_BODY_SIZE, - #[cfg(feature = "http3")] + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] h3_max_concurrent_connections: H3::MAX_CONCURRENT_CONNECTIONS, - #[cfg(feature = "http3")] - h3_max_concurrent_bidistream: H3::MAX_CONCURRENT_BIDISTREAM.into(), - #[cfg(feature = "http3")] - h3_max_concurrent_unistream: H3::MAX_CONCURRENT_UNISTREAM.into(), - #[cfg(feature = "http3")] + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] + h3_max_concurrent_bidistream: H3::MAX_CONCURRENT_BIDISTREAM, + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] + h3_max_concurrent_unistream: H3::MAX_CONCURRENT_UNISTREAM, + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] h3_max_idle_timeout: Some(Duration::from_secs(H3::MAX_IDLE_TIMEOUT)), } } diff --git a/rpxy-lib/src/handler/handler_main.rs b/rpxy-lib/src/handler/handler_main.rs index f6c4dc7..0b554ae 100644 --- a/rpxy-lib/src/handler/handler_main.rs +++ b/rpxy-lib/src/handler/handler_main.rs @@ -210,7 +210,7 @@ where remove_hop_header(headers); add_header_entry_overwrite_if_exist(headers, "server", env!("CARGO_PKG_NAME"))?; - #[cfg(feature = "http3")] + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] { // Manipulate ALT_SVC allowing h3 in response message only when mutual TLS is not enabled // TODO: This is a workaround for avoiding a client authentication in HTTP/3 @@ -235,7 +235,7 @@ where headers.remove(header::ALT_SVC.as_str()); } } - #[cfg(not(feature = "http3"))] + #[cfg(not(any(feature = "http3-quinn", feature = "http3-s2n")))] { if let Some(port) = self.globals.proxy_config.https_port { headers.remove(header::ALT_SVC.as_str()); diff --git a/rpxy-lib/src/lib.rs b/rpxy-lib/src/lib.rs index 1915b68..c472b05 100644 --- a/rpxy-lib/src/lib.rs +++ b/rpxy-lib/src/lib.rs @@ -23,6 +23,9 @@ pub mod reexports { pub use rustls::{Certificate, PrivateKey}; } +#[cfg(all(feature = "http3-quinn", feature = "http3-s2n"))] +compile_error!("feature \"http3-quinn\" and feature \"http3-s2n\" cannot be enabled at the same time"); + /// Entrypoint that creates and spawns tasks of reverse proxy services pub async fn entrypoint( proxy_config: &ProxyConfig, @@ -44,7 +47,7 @@ where if proxy_config.https_port.is_some() { info!("Listen port: {} (for TLS)", proxy_config.https_port.unwrap()); } - #[cfg(feature = "http3")] + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] if proxy_config.http3 { info!("Experimental HTTP/3.0 is enabled. Note it is still very unstable."); } diff --git a/rpxy-lib/src/proxy/crypto_service.rs b/rpxy-lib/src/proxy/crypto_service.rs index 8675a1d..d6191e6 100644 --- a/rpxy-lib/src/proxy/crypto_service.rs +++ b/rpxy-lib/src/proxy/crypto_service.rs @@ -22,7 +22,10 @@ where pub type SniServerCryptoMap = HashMap>; pub struct ServerCrypto { // For Quic/HTTP3, only servers with no client authentication + #[cfg(feature = "http3-quinn")] pub inner_global_no_client_auth: Arc, + #[cfg(feature = "http3-s2n")] + pub inner_global_no_client_auth: s2n_quic_rustls::Server, // For TLS over TCP/HTTP2 and 1.1, map of SNI to server_crypto for all given servers pub inner_local_map: Arc, } @@ -68,7 +71,22 @@ impl TryInto> for &ServerCryptoBase { type Error = anyhow::Error; fn try_into(self) -> Result, Self::Error> { - let mut resolver_global = ResolvesServerCertUsingSni::new(); + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] + let server_crypto_global = self.build_server_crypto_global()?; + let server_crypto_local_map: SniServerCryptoMap = self.build_server_crypto_local_map()?; + + Ok(Arc::new(ServerCrypto { + #[cfg(feature = "http3-quinn")] + inner_global_no_client_auth: Arc::new(server_crypto_global), + #[cfg(feature = "http3-s2n")] + inner_global_no_client_auth: server_crypto_global, + inner_local_map: Arc::new(server_crypto_local_map), + })) + } +} + +impl ServerCryptoBase { + fn build_server_crypto_local_map(&self) -> Result> { let mut server_crypto_local_map: SniServerCryptoMap = HashMap::default(); for (server_name_bytes_exp, certs_and_keys) in self.inner.iter() { @@ -93,16 +111,7 @@ impl TryInto> for &ServerCryptoBase { } // add client certificate if specified - if certs_and_keys.client_ca_certs.is_none() { - // aggregated server config for no client auth server for http3 - if let Err(e) = resolver_global.add(server_name.as_str(), certified_key) { - error!( - "{}: Failed to read some certificates and keys {}", - server_name.as_str(), - e - ) - } - } else { + if certs_and_keys.client_ca_certs.is_some() { // add client certificate if specified match certs_and_keys.parse_client_ca_certs() { Ok((owned_trust_anchors, _subject_key_ids)) => { @@ -120,14 +129,14 @@ impl TryInto> for &ServerCryptoBase { let mut server_config_local = if client_ca_roots_local.is_empty() { // with no client auth, enable http1.1 -- 3 - #[cfg(not(feature = "http3"))] + #[cfg(not(any(feature = "http3-quinn", feature = "http3-s2n")))] { ServerConfig::builder() .with_safe_defaults() .with_no_client_auth() .with_cert_resolver(Arc::new(resolver_local)) } - #[cfg(feature = "http3")] + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] { let mut sc = ServerConfig::builder() .with_safe_defaults() @@ -150,6 +159,33 @@ impl TryInto> for &ServerCryptoBase { server_crypto_local_map.insert(server_name_bytes_exp.to_owned(), Arc::new(server_config_local)); } + Ok(server_crypto_local_map) + } + + #[cfg(feature = "http3-quinn")] + fn build_server_crypto_global(&self) -> Result> { + let mut resolver_global = ResolvesServerCertUsingSni::new(); + + for (server_name_bytes_exp, certs_and_keys) in self.inner.iter() { + let server_name: String = server_name_bytes_exp.try_into()?; + + // Parse server certificates and private keys + let Ok(certified_key): Result = certs_and_keys.parse_server_certs_and_keys() else { + warn!("Failed to add certificate for {}", server_name); + continue; + }; + + if certs_and_keys.client_ca_certs.is_none() { + // aggregated server config for no client auth server for http3 + if let Err(e) = resolver_global.add(server_name.as_str(), certified_key) { + error!( + "{}: Failed to read some certificates and keys {}", + server_name.as_str(), + e + ) + } + } + } ////////////// let mut server_crypto_global = ServerConfig::builder() @@ -159,23 +195,82 @@ impl TryInto> for &ServerCryptoBase { ////////////////////////////// - #[cfg(feature = "http3")] - { - server_crypto_global.alpn_protocols = vec![ - b"h3".to_vec(), - b"hq-29".to_vec(), // TODO: remove later? - b"h2".to_vec(), - b"http/1.1".to_vec(), - ]; - } - #[cfg(not(feature = "http3"))] - { - server_crypto_global.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; - } + server_crypto_global.alpn_protocols = vec![ + b"h3".to_vec(), + b"hq-29".to_vec(), // TODO: remove later? + b"h2".to_vec(), + b"http/1.1".to_vec(), + ]; + Ok(server_crypto_global) + } - Ok(Arc::new(ServerCrypto { - inner_global_no_client_auth: Arc::new(server_crypto_global), - inner_local_map: Arc::new(server_crypto_local_map), - })) + #[cfg(feature = "http3-s2n")] + fn build_server_crypto_global(&self) -> Result> { + let mut resolver_global = s2n_quic_rustls::rustls::server::ResolvesServerCertUsingSni::new(); + + for (server_name_bytes_exp, certs_and_keys) in self.inner.iter() { + let server_name: String = server_name_bytes_exp.try_into()?; + + // Parse server certificates and private keys + let Ok(certified_key) = parse_server_certs_and_keys_s2n(certs_and_keys) else { + warn!("Failed to add certificate for {}", server_name); + continue; + }; + + if certs_and_keys.client_ca_certs.is_none() { + // aggregated server config for no client auth server for http3 + if let Err(e) = resolver_global.add(server_name.as_str(), certified_key) { + error!( + "{}: Failed to read some certificates and keys {}", + server_name.as_str(), + e + ) + } + } + } + let alpn = vec![ + b"h3".to_vec(), + b"hq-29".to_vec(), // TODO: remove later? + b"h2".to_vec(), + b"http/1.1".to_vec(), + ]; + let server_crypto_global = s2n_quic::provider::tls::rustls::Server::builder() + .with_cert_resolver(Arc::new(resolver_global)) + .map_err(|e| anyhow::anyhow!(e))? + .with_application_protocols(alpn.iter()) + .map_err(|e| anyhow::anyhow!(e))? + .build() + .map_err(|e| anyhow::anyhow!(e))?; + Ok(server_crypto_global) } } + +#[cfg(feature = "http3-s2n")] +/// This is workaround for the version difference between rustls and s2n-quic-rustls +fn parse_server_certs_and_keys_s2n( + certs_and_keys: &CertsAndKeys, +) -> Result { + let signing_key = certs_and_keys + .cert_keys + .iter() + .find_map(|k| { + let s2n_private_key = s2n_quic_rustls::PrivateKey(k.0.clone()); + if let Ok(sk) = s2n_quic_rustls::rustls::sign::any_supported_type(&s2n_private_key) { + Some(sk) + } else { + None + } + }) + .ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Unable to find a valid certificate and key", + ) + })?; + let certs: Vec<_> = certs_and_keys + .certs + .iter() + .map(|c| s2n_quic_rustls::rustls::Certificate(c.0.clone())) + .collect(); + Ok(s2n_quic_rustls::rustls::sign::CertifiedKey::new(certs, signing_key)) +} diff --git a/rpxy-lib/src/proxy/mod.rs b/rpxy-lib/src/proxy/mod.rs index 05d63b0..0551b62 100644 --- a/rpxy-lib/src/proxy/mod.rs +++ b/rpxy-lib/src/proxy/mod.rs @@ -1,10 +1,12 @@ mod crypto_service; mod proxy_client_cert; -#[cfg(feature = "http3")] +#[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] mod proxy_h3; mod proxy_main; -#[cfg(feature = "http3")] -mod proxy_quic; +#[cfg(feature = "http3-quinn")] +mod proxy_quic_quinn; +#[cfg(feature = "http3-s2n")] +mod proxy_quic_s2n; mod proxy_tls; mod socket; diff --git a/rpxy-lib/src/proxy/proxy_h3.rs b/rpxy-lib/src/proxy/proxy_h3.rs index eac6dbf..7773ad9 100644 --- a/rpxy-lib/src/proxy/proxy_h3.rs +++ b/rpxy-lib/src/proxy/proxy_h3.rs @@ -1,8 +1,11 @@ use super::Proxy; use crate::{certs::CryptoSource, error::*, log::*, utils::ServerNameBytesExp}; use bytes::{Buf, Bytes}; +#[cfg(feature = "http3-quinn")] use h3::{quic::BidiStream, quic::Connection as ConnectionQuic, server::RequestStream}; use hyper::{client::connect::Connect, Body, Request, Response}; +#[cfg(feature = "http3-s2n")] +use s2n_quic_h3::h3::{self, quic::BidiStream, quic::Connection as ConnectionQuic, server::RequestStream}; use std::net::SocketAddr; use tokio::time::{timeout, Duration}; diff --git a/rpxy-lib/src/proxy/proxy_quic.rs b/rpxy-lib/src/proxy/proxy_quic_quinn.rs similarity index 98% rename from rpxy-lib/src/proxy/proxy_quic.rs rename to rpxy-lib/src/proxy/proxy_quic_quinn.rs index 0e660c1..fb08420 100644 --- a/rpxy-lib/src/proxy/proxy_quic.rs +++ b/rpxy-lib/src/proxy/proxy_quic_quinn.rs @@ -19,7 +19,7 @@ where &self, mut server_crypto_rx: ReloaderReceiver, ) -> Result<()> { - info!("Start UDP proxy serving with HTTP/3 request for configured host names"); + info!("Start UDP proxy serving with HTTP/3 request for configured host names [quinn]"); // first set as null config server let rustls_server_config = ServerConfig::builder() .with_safe_default_cipher_suites() @@ -30,8 +30,8 @@ where let mut transport_config_quic = TransportConfig::default(); transport_config_quic - .max_concurrent_bidi_streams(self.globals.proxy_config.h3_max_concurrent_bidistream) - .max_concurrent_uni_streams(self.globals.proxy_config.h3_max_concurrent_unistream) + .max_concurrent_bidi_streams(self.globals.proxy_config.h3_max_concurrent_bidistream.into()) + .max_concurrent_uni_streams(self.globals.proxy_config.h3_max_concurrent_unistream.into()) .max_idle_timeout( self .globals diff --git a/rpxy-lib/src/proxy/proxy_quic_s2n.rs b/rpxy-lib/src/proxy/proxy_quic_s2n.rs new file mode 100644 index 0000000..e0c41a5 --- /dev/null +++ b/rpxy-lib/src/proxy/proxy_quic_s2n.rs @@ -0,0 +1,135 @@ +use super::{ + crypto_service::{ServerCrypto, ServerCryptoBase}, + proxy_main::Proxy, +}; +use crate::{certs::CryptoSource, error::*, log::*, utils::BytesName}; +use hot_reload::ReloaderReceiver; +use hyper::client::connect::Connect; +use s2n_quic::provider; +use std::sync::Arc; + +impl Proxy +where + T: Connect + Clone + Sync + Send + 'static, + U: CryptoSource + Clone + Sync + Send + 'static, +{ + pub(super) async fn listener_service_h3( + &self, + mut server_crypto_rx: ReloaderReceiver, + ) -> Result<()> { + info!("Start UDP proxy serving with HTTP/3 request for configured host names [s2n-quic]"); + + // initially wait for receipt + let mut server_crypto: Option> = { + let _ = server_crypto_rx.changed().await; + let sc = self.receive_server_crypto(server_crypto_rx.clone())?; + Some(sc) + }; + + // event loop + loop { + tokio::select! { + v = self.serve_connection(&server_crypto) => { + if let Err(e) = v { + error!("Quic connection event loop illegally shutdown [s2n-quic] {e}"); + break; + } + } + _ = server_crypto_rx.changed() => { + server_crypto = match self.receive_server_crypto(server_crypto_rx.clone()) { + Ok(sc) => Some(sc), + Err(e) => { + error!("{e}"); + break; + } + }; + } + else => break + } + } + + Ok(()) + } + + fn receive_server_crypto(&self, server_crypto_rx: ReloaderReceiver) -> Result> { + let cert_keys_map = server_crypto_rx.borrow().clone().ok_or_else(|| { + error!("Reloader is broken"); + RpxyError::Other(anyhow!("Reloader is broken")) + })?; + + let server_crypto: Option> = (&cert_keys_map).try_into().ok(); + server_crypto.ok_or_else(|| { + error!("Failed to update server crypto for h3 [s2n-quic]"); + RpxyError::Other(anyhow!("Failed to update server crypto for h3 [s2n-quic]")) + }) + } + + async fn serve_connection(&self, server_crypto: &Option>) -> Result<()> { + // setup UDP socket + let io = provider::io::tokio::Builder::default() + .with_receive_address(self.listening_on)? + .with_reuse_port()? + .build()?; + + // setup limits + let mut limits = provider::limits::Limits::default() + .with_max_open_local_bidirectional_streams(self.globals.proxy_config.h3_max_concurrent_bidistream as u64) + .map_err(|e| anyhow!(e))? + .with_max_open_remote_bidirectional_streams(self.globals.proxy_config.h3_max_concurrent_bidistream as u64) + .map_err(|e| anyhow!(e))? + .with_max_open_local_unidirectional_streams(self.globals.proxy_config.h3_max_concurrent_unistream as u64) + .map_err(|e| anyhow!(e))? + .with_max_open_remote_unidirectional_streams(self.globals.proxy_config.h3_max_concurrent_unistream as u64) + .map_err(|e| anyhow!(e))? + .with_max_active_connection_ids(self.globals.proxy_config.h3_max_concurrent_connections as u64) + .map_err(|e| anyhow!(e))?; + limits = if let Some(v) = self.globals.proxy_config.h3_max_idle_timeout { + limits.with_max_idle_timeout(v).map_err(|e| anyhow!(e))? + } else { + limits + }; + + // setup tls + let Some(server_crypto) = server_crypto else { + warn!("No server crypto is given [s2n-quic]"); + return Err(RpxyError::Other(anyhow!("No server crypto is given [s2n-quic]"))); + }; + let tls = server_crypto.inner_global_no_client_auth.clone(); + + let mut server = s2n_quic::Server::builder() + .with_tls(tls) + .map_err(|e| anyhow::anyhow!(e))? + .with_io(io) + .map_err(|e| anyhow!(e))? + .with_limits(limits) + .map_err(|e| anyhow!(e))? + .start() + .map_err(|e| anyhow!(e))?; + + // quic event loop. this immediately cancels when crypto is updated by tokio::select! + while let Some(new_conn) = server.accept().await { + debug!("New QUIC connection established"); + let Ok(Some(new_server_name)) = new_conn.server_name() else { + warn!("HTTP/3 no SNI is given"); + continue; + }; + debug!("HTTP/3 connection incoming (SNI {:?})", new_server_name); + let self_clone = self.clone(); + + self.globals.runtime_handle.spawn(async move { + let client_addr = new_conn.remote_addr()?; + let quic_connection = s2n_quic_h3::Connection::new(new_conn); + // Timeout is based on underlying quic + if let Err(e) = self_clone + .connection_serve_h3(quic_connection, new_server_name.to_server_name_vec(), client_addr) + .await + { + warn!("QUIC or HTTP/3 connection failed: {}", e); + }; + Ok(()) as Result<()> + }); + } + + Ok(()) + } +} diff --git a/rpxy-lib/src/proxy/proxy_tls.rs b/rpxy-lib/src/proxy/proxy_tls.rs index b937b02..da4205e 100644 --- a/rpxy-lib/src/proxy/proxy_tls.rs +++ b/rpxy-lib/src/proxy/proxy_tls.rs @@ -108,7 +108,7 @@ where .await .map_err(|e| anyhow::anyhow!(e))?; - #[cfg(not(feature = "http3"))] + #[cfg(not(any(feature = "http3-quinn", feature = "http3-s2n")))] { tokio::select! { _= cert_reloader_service.start() => { @@ -124,7 +124,7 @@ where }; Ok(()) } - #[cfg(feature = "http3")] + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] { if self.globals.proxy_config.http3 { tokio::select! { diff --git a/rpxy-lib/src/proxy/socket.rs b/rpxy-lib/src/proxy/socket.rs index 2151710..a8b9f01 100644 --- a/rpxy-lib/src/proxy/socket.rs +++ b/rpxy-lib/src/proxy/socket.rs @@ -1,8 +1,8 @@ use crate::{error::*, log::*}; -#[cfg(feature = "http3")] +#[cfg(feature = "http3-quinn")] use socket2::{Domain, Protocol, Socket, Type}; use std::net::SocketAddr; -#[cfg(feature = "http3")] +#[cfg(feature = "http3-quinn")] use std::net::UdpSocket; use tokio::net::TcpSocket; @@ -23,7 +23,7 @@ pub(super) fn bind_tcp_socket(listening_on: &SocketAddr) -> Result { Ok(tcp_socket) } -#[cfg(feature = "http3")] +#[cfg(feature = "http3-quinn")] /// Bind UDP socket to the given `SocketAddr`, and returns the UDP socket with `SO_REUSEADDR` and `SO_REUSEPORT` options. /// This option is required to re-bind the socket address when the proxy instance is reconstructed. pub(super) fn bind_udp_socket(listening_on: &SocketAddr) -> Result { From e751bd34ac47b9bb1808bed5813b190bff2d9212 Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Sun, 30 Jul 2023 17:53:10 +0900 Subject: [PATCH 04/14] fix: bugfix when switching features from http3-quinn to http3-s2n --- docker/Dockerfile | 8 ++++++-- docker/docker-compose.yml | 7 +++++-- rpxy-bin/Cargo.toml | 3 +-- rpxy-bin/src/config/toml.rs | 6 +++--- rpxy-lib/Cargo.toml | 5 ++--- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 456df2b..82f6605 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -15,6 +15,10 @@ WORKDIR /tmp COPY . /tmp/ ARG TARGETARCH +ARG CARGO_FEATURES +ENV CARGO_FEATURES ${CARGO_FEATURES} +ARG ADDITIONAL_DEPS +ENV ADDITIONAL_DEPS ${ADDITIONAL_DEPS} RUN if [ $TARGETARCH = "amd64" ]; then \ echo "x86_64" > /arch; \ @@ -29,13 +33,13 @@ ENV RUSTFLAGS "-C link-arg=-s" RUN update-ca-certificates 2> /dev/null || true -RUN apt-get update && apt-get install -qy --no-install-recommends $BUILD_DEPS && \ +RUN apt-get update && apt-get install -qy --no-install-recommends $BUILD_DEPS ${ADDITIONAL_DEPS} && \ curl -sSf https://sh.rustup.rs | bash -s -- -y --default-toolchain stable && \ export PATH="$HOME/.cargo/bin:$PATH" && \ echo "Install toolchain" && \ rustup target add $(cat /arch)-unknown-linux-gnu &&\ echo "Building rpxy from source" && \ - cargo build --release --target=$(cat /arch)-unknown-linux-gnu && \ + cargo build --release --target=$(cat /arch)-unknown-linux-gnu ${CARGO_FEATURES} && \ strip --strip-all /tmp/target/$(cat /arch)-unknown-linux-gnu/release/rpxy &&\ cp /tmp/target/$(cat /arch)-unknown-linux-gnu/release/rpxy /tmp/target/release/rpxy diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 886a471..5bd0c40 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -10,10 +10,13 @@ services: - 127.0.0.1:8443:8443 build: context: ../ + args: + - "CARGO_FEATURES=--no-default-features --features http3-s2n" + - "ADDITIONAL_DEPS=pkg-config libssl-dev cmake libclang1 gcc g++" dockerfile: ./docker/Dockerfile platforms: # Choose your platforms - - "linux/amd64" - # - "linux/arm64" + # - "linux/amd64" + - "linux/arm64" environment: - LOG_LEVEL=debug - LOG_TO_FILE=true diff --git a/rpxy-bin/Cargo.toml b/rpxy-bin/Cargo.toml index 7e20882..b8d1420 100644 --- a/rpxy-bin/Cargo.toml +++ b/rpxy-bin/Cargo.toml @@ -12,8 +12,7 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["http3"] -http3 = ["rpxy-lib/http3"] +default = ["http3-quinn"] http3-quinn = ["rpxy-lib/http3-quinn"] http3-s2n = ["rpxy-lib/http3-s2n"] diff --git a/rpxy-bin/src/config/toml.rs b/rpxy-bin/src/config/toml.rs index 35c5d7a..5f6ab4a 100644 --- a/rpxy-bin/src/config/toml.rs +++ b/rpxy-bin/src/config/toml.rs @@ -21,7 +21,7 @@ pub struct ConfigToml { pub experimental: Option, } -#[cfg(feature = "http3")] +#[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] #[derive(Deserialize, Debug, Default, PartialEq, Eq, Clone)] pub struct Http3Option { pub alt_svc_max_age: Option, @@ -34,7 +34,7 @@ pub struct Http3Option { #[derive(Deserialize, Debug, Default, PartialEq, Eq, Clone)] pub struct Experimental { - #[cfg(feature = "http3")] + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] pub h3: Option, pub ignore_sni_consistency: Option, } @@ -128,7 +128,7 @@ impl TryInto for &ConfigToml { // experimental if let Some(exp) = &self.experimental { - #[cfg(feature = "http3")] + #[cfg(any(feature = "http3-quinn", feature = "http3-s2n"))] { if let Some(h3option) = &exp.h3 { proxy_config.http3 = true; diff --git a/rpxy-lib/Cargo.toml b/rpxy-lib/Cargo.toml index 063ca13..e1327f7 100644 --- a/rpxy-lib/Cargo.toml +++ b/rpxy-lib/Cargo.toml @@ -12,8 +12,7 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["http3", "sticky-cookie"] -http3 = ["http3-s2n"] +default = ["http3-quinn", "sticky-cookie"] http3-quinn = ["quinn", "h3", "h3-quinn", "socket2"] http3-s2n = ["h3", "s2n-quic", "s2n-quic-rustls", "s2n-quic-h3"] sticky-cookie = ["base64", "sha2", "chrono"] @@ -67,7 +66,7 @@ h3 = { path = "../h3/h3/", optional = true } h3-quinn = { path = "../h3-quinn/", optional = true } # Tentative to support rustls-0.21 # for UDP socket wit SO_REUSEADDR when h3 with quinn socket2 = { version = "0.5.3", features = ["all"], optional = true } -s2n-quic = { path = "../s2n-quic/quic/s2n-quic/", features = [ +s2n-quic = { path = "../s2n-quic/quic/s2n-quic/", default-features = false, features = [ "provider-tls-rustls", ], optional = true } s2n-quic-h3 = { path = "../s2n-quic/quic/s2n-quic-h3/", optional = true } From dab0036910bd826edba45eb9db8818803baaa31a Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Sun, 30 Jul 2023 17:57:34 +0900 Subject: [PATCH 05/14] chore: github actions test for s2n build fix github actions fix github actions --- .github/workflows/docker_build_push.yml | 17 +++++++++++++++++ docker/docker-compose.yml | 6 +++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker_build_push.yml b/.github/workflows/docker_build_push.yml index b3e5e88..6170337 100644 --- a/.github/workflows/docker_build_push.yml +++ b/.github/workflows/docker_build_push.yml @@ -5,6 +5,7 @@ on: branches: - main - develop + - "feat/s2n-quic" env: REGISTRY_IMAGE: jqtype/rpxy @@ -85,6 +86,22 @@ jobs: platforms: linux/amd64,linux/arm64 labels: ${{ steps.meta.outputs.labels }} + - name: Nightly build and push s2n-quic + uses: docker/build-push-action@v4 + with: + context: . + build-args: | + "CARGO_FEATURES=--no-default-features --features http3-s2n" + "ADDITIONAL_DEPS=pkg-config libssl-dev cmake libclang1 gcc g++" + push: true + tags: | + ${{ env.REGISTRY_IMAGE }}:nightly-s2n + file: ./docker/Dockerfile + cache-from: type=gha + cache-to: type=gha,mode=max + platforms: linux/amd64 + labels: ${{ steps.meta.outputs.labels }} + - name: Nightly build and push slim if: ${{ env.BRANCH == 'develop' }} uses: docker/build-push-action@v4 diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 5bd0c40..83ae91c 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -10,9 +10,9 @@ services: - 127.0.0.1:8443:8443 build: context: ../ - args: - - "CARGO_FEATURES=--no-default-features --features http3-s2n" - - "ADDITIONAL_DEPS=pkg-config libssl-dev cmake libclang1 gcc g++" + # args: # Specify when build quic-s2n version + # - "CARGO_FEATURES=--no-default-features --features http3-s2n" + # - "ADDITIONAL_DEPS=pkg-config libssl-dev cmake libclang1 gcc g++" dockerfile: ./docker/Dockerfile platforms: # Choose your platforms # - "linux/amd64" From 6a1bb18c2fc91f4c29b4b2650e1e4c663dd508c6 Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Mon, 31 Jul 2023 14:56:39 +0900 Subject: [PATCH 06/14] chore: update todo, deps chore: todo --- .github/workflows/docker_build_push.yml | 1 + TODO.md | 18 ++++++++++-------- rpxy-bin/Cargo.toml | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/docker_build_push.yml b/.github/workflows/docker_build_push.yml index 6170337..af7ab41 100644 --- a/.github/workflows/docker_build_push.yml +++ b/.github/workflows/docker_build_push.yml @@ -86,6 +86,7 @@ jobs: platforms: linux/amd64,linux/arm64 labels: ${{ steps.meta.outputs.labels }} + # 今Pushし放題なので注意 - name: Nightly build and push s2n-quic uses: docker/build-push-action@v4 with: diff --git a/TODO.md b/TODO.md index c552359..f3d29a5 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,7 @@ # TODO List +- [Try in v0.5.0] **Use `gchr.io`** +- [Try in v0.5.0] **Cache option for the response with `Cache-Control: public` header directive ([#55](https://github.com/junkurihara/rust-rpxy/issues/55))** - Improvement of path matcher - More flexible option for rewriting path - Refactoring @@ -10,14 +12,6 @@ - upstream/upstream group: information on targeted destinations for each set of (a domain + a path) - load-balance: load balancing mod for a domain + path - - Done in v0.4.0: - ~~Split `rpxy` source codes into `rpxy-lib` and `rpxy-bin` to make the core part (reverse proxy) isolated from the misc part like toml file loader. This is in order to make the configuration-related part more flexible (related to [#33](https://github.com/junkurihara/rust-rpxy/issues/33))~~ - -- Cache option for the response with `Cache-Control: public` header directive ([#55](https://github.com/junkurihara/rust-rpxy/issues/55)) -- Consideration on migrating from `quinn` and `h3-quinn` to other QUIC implementations ([#57](https://github.com/junkurihara/rust-rpxy/issues/57)) -- Done in v0.4.0: - ~~Benchmark with other reverse proxy implementations like Sozu ([#58](https://github.com/junkurihara/rust-rpxy/issues/58)) Currently, Sozu can work only on `amd64` format due to its HTTP message parser limitation... Since the main developer have only `arm64` (Apple M1) laptops, so we should do that on VPS?~~ - - Unit tests - Options to serve custom http_error page. - Prometheus metrics @@ -30,4 +24,12 @@ - Make the session-persistance option for load-balancing sophisticated. (mostly done in v0.3.0) - add option for sticky cookie name - add option for sticky cookie duration + +- Done in v0.5.0: + ~~Consideration on migrating from `quinn` and `h3-quinn` to other QUIC implementations ([#57](https://github.com/junkurihara/rust-rpxy/issues/57))~~ +- Done in v0.4.0: + ~~Benchmark with other reverse proxy implementations like Sozu ([#58](https://github.com/junkurihara/rust-rpxy/issues/58)) Currently, Sozu can work only on `amd64` format due to its HTTP message parser limitation... Since the main developer have only `arm64` (Apple M1) laptops, so we should do that on VPS?~~ +- Done in v0.4.0: + ~~Split `rpxy` source codes into `rpxy-lib` and `rpxy-bin` to make the core part (reverse proxy) isolated from the misc part like toml file loader. This is in order to make the configuration-related part more flexible (related to [#33](https://github.com/junkurihara/rust-rpxy/issues/33))~~ + - etc. diff --git a/rpxy-bin/Cargo.toml b/rpxy-bin/Cargo.toml index b8d1420..6b0b9f4 100644 --- a/rpxy-bin/Cargo.toml +++ b/rpxy-bin/Cargo.toml @@ -23,7 +23,7 @@ rpxy-lib = { path = "../rpxy-lib/", default-features = false, features = [ anyhow = "1.0.72" rustc-hash = "1.1.0" -serde = { version = "1.0.178", default-features = false, features = ["derive"] } +serde = { version = "1.0.179", default-features = false, features = ["derive"] } derive_builder = "0.12.0" tokio = { version = "1.29.1", default-features = false, features = [ "net", From 53f058f96dab59cd456b7d2814d6e1cd3d813498 Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Mon, 31 Jul 2023 23:03:11 +0900 Subject: [PATCH 07/14] feat: update dockerfiles --- README.md | 4 ++-- docker/Dockerfile | 9 +++++---- docker/docker-compose.slim.yml | 34 ++++++++++++++++++++++++++++++++++ docker/docker-compose.yml | 17 +++++++++-------- quinn | 2 +- 5 files changed, 51 insertions(+), 15 deletions(-) create mode 100644 docker/docker-compose.slim.yml diff --git a/README.md b/README.md index eadc42c..074f91e 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ As default, `rpxy` provides the *TLS connection sanitization* by correctly binding a certificate used to establish a secure channel with the backend application. Specifically, it always keeps the consistency between the given SNI (server name indication) in `ClientHello` of the underlying TLS and the domain name given by the overlaid HTTP HOST header (or URL in Request line) [^1]. Additionally, as a somewhat unstable feature, our `rpxy` can handle the brand-new HTTP/3 connection thanks to [`quinn`](https://github.com/quinn-rs/quinn), [`s2n-quic`](https://github.com/aws/s2n-quic) and [`hyperium/h3`](https://github.com/hyperium/h3).[^h3lib] - [^h3lib]: HTTP/3 libraries are mutually exclusive. You need to explicitly specify `s2n-quic` with `--no-default-features` flag. + [^h3lib]: HTTP/3 libraries are mutually exclusive. You need to explicitly specify `s2n-quic` with `--no-default-features` flag. Also note that if you build `rpxy` with `s2n-quic`, then it requires `openssl` just for building the package. This project is still *work-in-progress*. But it is already working in some production environments and serves a number of domain names. Furthermore it *significantly outperforms* NGINX and Caddy, e.g., *1.5x faster than NGINX*, in the setting of a very simple HTTP reverse-proxy scenario (See [`bench`](./bench/) directory). @@ -35,7 +35,7 @@ You can build an executable binary yourself by checking out this Git repository. # Build (default: QUIC and HTTP/3 is enabled using `quinn`) % cargo build --release -# If you want to use `s2n-quic`, build as follows. +# If you want to use `s2n-quic`, build as follows. You may need several additional dependencies. % cargo build --no-default-features --features http3-s2n --release ``` diff --git a/docker/Dockerfile b/docker/Dockerfile index 82f6605..8888814 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -9,6 +9,7 @@ FROM --platform=$BUILDPLATFORM base AS builder ENV CFLAGS=-Ofast ENV BUILD_DEPS curl make ca-certificates build-essential +ENV TARGET_SUFFIX=unknown-linux-gnu WORKDIR /tmp @@ -37,11 +38,11 @@ RUN apt-get update && apt-get install -qy --no-install-recommends $BUILD_DEPS ${ curl -sSf https://sh.rustup.rs | bash -s -- -y --default-toolchain stable && \ export PATH="$HOME/.cargo/bin:$PATH" && \ echo "Install toolchain" && \ - rustup target add $(cat /arch)-unknown-linux-gnu &&\ + rustup target add $(cat /arch)-${TARGET_SUFFIX} && \ echo "Building rpxy from source" && \ - cargo build --release --target=$(cat /arch)-unknown-linux-gnu ${CARGO_FEATURES} && \ - strip --strip-all /tmp/target/$(cat /arch)-unknown-linux-gnu/release/rpxy &&\ - cp /tmp/target/$(cat /arch)-unknown-linux-gnu/release/rpxy /tmp/target/release/rpxy + cargo build --release --target=$(cat /arch)-${TARGET_SUFFIX} ${CARGO_FEATURES} && \ + strip --strip-all /tmp/target/$(cat /arch)-${TARGET_SUFFIX}/release/rpxy &&\ + cp /tmp/target/$(cat /arch)-${TARGET_SUFFIX}/release/rpxy /tmp/target/release/rpxy ######################################## FROM --platform=$TARGETPLATFORM base AS runner diff --git a/docker/docker-compose.slim.yml b/docker/docker-compose.slim.yml new file mode 100644 index 0000000..b147562 --- /dev/null +++ b/docker/docker-compose.slim.yml @@ -0,0 +1,34 @@ +version: "3" +services: + rpxy-rp: + image: jqtype/rpxy + container_name: rpxy + init: true + restart: unless-stopped + ports: + - 127.0.0.1:8080:8080/tcp + - 127.0.0.1:8443:8443/udp + - 127.0.0.1:8443:8443/tcp + build: + context: ../ + additional_contexts: # Uncomment when you build with musl + - messense/rust-musl-cross:amd64-musl=docker-image://messense/rust-musl-cross:x86_64-musl + - messense/rust-musl-cross:arm64-musl=docker-image://messense/rust-musl-cross:aarch64-musl + dockerfile: ./docker/Dockerfile.slim # based on alpine and build x86_64-unknown-linux-musl + platforms: # Choose your platforms + - "linux/amd64" + # - "linux/arm64" + environment: + - LOG_LEVEL=debug + - LOG_TO_FILE=true + - HOST_USER=jun + - HOST_UID=501 + - HOST_GID=501 + # - WATCH=true + tty: false + privileged: true + volumes: + - ./log:/rpxy/log + - ../example-certs/server.crt:/certs/server.crt:ro + - ../example-certs/server.key:/certs/server.key:ro + - ../config-example.toml:/etc/rpxy.toml:ro diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 83ae91c..6d74959 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -6,17 +6,18 @@ services: init: true restart: unless-stopped ports: - - 127.0.0.1:8080:8080 - - 127.0.0.1:8443:8443 + - 127.0.0.1:8080:8080/tcp + - 127.0.0.1:8443:8443/udp + - 127.0.0.1:8443:8443/tcp build: context: ../ - # args: # Specify when build quic-s2n version - # - "CARGO_FEATURES=--no-default-features --features http3-s2n" - # - "ADDITIONAL_DEPS=pkg-config libssl-dev cmake libclang1 gcc g++" - dockerfile: ./docker/Dockerfile + args: # Uncomment when build quic-s2n version + - "CARGO_FEATURES=--no-default-features --features http3-s2n" + - "ADDITIONAL_DEPS=pkg-config libssl-dev cmake libclang1 gcc g++" + dockerfile: ./docker/Dockerfile # based on ubuntu 22.04 and build x86_64-unknown-linux-gnu platforms: # Choose your platforms - # - "linux/amd64" - - "linux/arm64" + - "linux/amd64" + # - "linux/arm64" environment: - LOG_LEVEL=debug - LOG_TO_FILE=true diff --git a/quinn b/quinn index 532ba7d..70e14b5 160000 --- a/quinn +++ b/quinn @@ -1 +1 @@ -Subproject commit 532ba7d80405ad083fd05546fa71becbe5eff1a4 +Subproject commit 70e14b5c26b45ee1e3d5dd64b2a184e2d6376880 From ff14fd467815991776fd1199b88ff4fa133459c3 Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Mon, 31 Jul 2023 23:23:34 +0900 Subject: [PATCH 08/14] feat: add ghcr.io push --- .github/workflows/ghcr_build_push.yml | 69 +++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 .github/workflows/ghcr_build_push.yml diff --git a/.github/workflows/ghcr_build_push.yml b/.github/workflows/ghcr_build_push.yml new file mode 100644 index 0000000..84317fb --- /dev/null +++ b/.github/workflows/ghcr_build_push.yml @@ -0,0 +1,69 @@ +name: Build and Publish Docker Images to ghcr.io + +on: + push: + branches: + - "feat/s2n-quic" + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build_and_push: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - target: "default" + dockerfile: ./docker/Dockerfile + platforms: linux/amd64 + + - target: "s2n" + dockerfile: ./docker/Dockerfile + build-args: | + "CARGO_FEATURES=--no-default-features --features http3-s2n" + "ADDITIONAL_DEPS=pkg-config libssl-dev cmake libclang1 gcc g++" + platforms: linux/amd64 + tags-suffix: "-s2n" + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # 今Pushし放題なので注意 + - name: Nightly build and push + uses: docker/build-push-action@v4 + with: + context: . + build-args: ${{ matrix.build-args }} + push: true + tags: | + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:nightly${{ matrix.tags-suffix }} + file: ${{ matrix.dockerfile }} + cache-from: type=gha,scope=rpxy-${{ matrix.target }} + cache-to: type=gha,mode=max,scope=rpxy-${{ matrix.target }} + platforms: ${{ matrix.platforms }} + labels: ${{ steps.meta.outputs.labels }} From 65e868893a6ec47b8b44d3c7705efa138bd00d0e Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Tue, 1 Aug 2023 01:14:41 +0900 Subject: [PATCH 09/14] feat: update github actions --- .github/workflows/docker_build_push.yml | 137 +++++++++++------------- .github/workflows/ghcr_build_push.yml | 69 ------------ 2 files changed, 62 insertions(+), 144 deletions(-) delete mode 100644 .github/workflows/ghcr_build_push.yml diff --git a/.github/workflows/docker_build_push.yml b/.github/workflows/docker_build_push.yml index af7ab41..deb4854 100644 --- a/.github/workflows/docker_build_push.yml +++ b/.github/workflows/docker_build_push.yml @@ -1,18 +1,46 @@ name: Build and Publish Docker - on: push: branches: - - main - - develop - - "feat/s2n-quic" + - "develop" + - "main" + pull_request: + types: [synchronize, opened] env: - REGISTRY_IMAGE: jqtype/rpxy + GHCR: ghcr.io + GHCR_IMAGE_NAME: ${{ github.repository }} + DH_REGISTRY_NAME: jqtype/rpxy jobs: build_and_push: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - target: "default" + dockerfile: ./docker/Dockerfile + platforms: linux/amd64,linux/arm64 + + - target: "default-slim" + dockerfile: ./docker/Dockerfile.slim + build-contexts: | + messense/rust-musl-cross:amd64-musl=docker-image://messense/rust-musl-cross:x86_64-musl + messense/rust-musl-cross:arm64-musl=docker-image://messense/rust-musl-cross:aarch64-musl + platforms: linux/amd64,linux/arm64 + tags-suffix: "-slim" + # Aliases must be used only for release builds + aliases: | + "slim" + + - target: "s2n" + dockerfile: ./docker/Dockerfile + build-args: | + "CARGO_FEATURES=--no-default-features --features http3-s2n" + "ADDITIONAL_DEPS=pkg-config libssl-dev cmake libclang1 gcc g++" + platforms: linux/amd64,linux/arm64 + tags-suffix: "-s2n" steps: - name: Checkout @@ -20,14 +48,11 @@ jobs: with: submodules: recursive - - name: GitHub Environment - run: echo "BRANCH=${GITHUB_REF##*/}" >> $GITHUB_ENV - - name: Docker meta id: meta uses: docker/metadata-action@v4 with: - images: ${{ env.REGISTRY_IMAGE }} + images: ${{ env.GHCR }}/${{ env.GHCR_IMAGE_NAME }} - name: Set up QEMU uses: docker/setup-qemu-action@v2 @@ -35,87 +60,49 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ${{ env.GHCR }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Release build and push - if: ${{ env.BRANCH == 'main' }} + - name: Nightly build and push uses: docker/build-push-action@v4 with: context: . - push: true + build-args: ${{ matrix.build-args }} + push: ${{ github.event_name != 'pull_request' }} tags: | - ${{ env.REGISTRY_IMAGE }}:latest - file: ./docker/Dockerfile - cache-from: type=gha - cache-to: type=gha,mode=max - platforms: linux/amd64,linux/arm64 + ${{ env.GHCR }}/${{ env.GHCR_IMAGE_NAME }}:nightly${{ matrix.tags-suffix }} + ${{ env.DH_REGISTRY_NAME }}:nightly${{ matrix.tags-suffix }} + build-contexts: ${{ matrix.build-contexts }} + file: ${{ matrix.dockerfile }} + cache-from: type=gha,scope=rpxy-nightly-${{ matrix.target }} + cache-to: type=gha,mode=max,scope=rpxy-nightly-${{ matrix.target }} + platforms: ${{ matrix.platforms }} labels: ${{ steps.meta.outputs.labels }} - name: Release build and push slim - if: ${{ env.BRANCH == 'main' }} + if: ${{ github.ref_name == 'main' }} uses: docker/build-push-action@v4 with: context: . - push: true + build-args: ${{ matrix.build-args }} + push: ${{ github.event_name != 'pull_request' }} tags: | - ${{ env.REGISTRY_IMAGE }}:slim, ${{ env.REGISTRY_IMAGE }}:latest-slim - build-contexts: | - messense/rust-musl-cross:amd64-musl=docker-image://messense/rust-musl-cross:x86_64-musl - messense/rust-musl-cross:arm64-musl=docker-image://messense/rust-musl-cross:aarch64-musl - file: ./docker/Dockerfile.slim - cache-from: type=gha - cache-to: type=gha,mode=max - platforms: linux/amd64,linux/arm64 - labels: ${{ steps.meta.outputs.labels }} - - - name: Nightly build and push - if: ${{ env.BRANCH == 'develop' }} - uses: docker/build-push-action@v4 - with: - context: . - push: true - tags: | - ${{ env.REGISTRY_IMAGE }}:nightly - file: ./docker/Dockerfile - cache-from: type=gha - cache-to: type=gha,mode=max - platforms: linux/amd64,linux/arm64 - labels: ${{ steps.meta.outputs.labels }} - - # 今Pushし放題なので注意 - - name: Nightly build and push s2n-quic - uses: docker/build-push-action@v4 - with: - context: . - build-args: | - "CARGO_FEATURES=--no-default-features --features http3-s2n" - "ADDITIONAL_DEPS=pkg-config libssl-dev cmake libclang1 gcc g++" - push: true - tags: | - ${{ env.REGISTRY_IMAGE }}:nightly-s2n - file: ./docker/Dockerfile - cache-from: type=gha - cache-to: type=gha,mode=max - platforms: linux/amd64 - labels: ${{ steps.meta.outputs.labels }} - - - name: Nightly build and push slim - if: ${{ env.BRANCH == 'develop' }} - uses: docker/build-push-action@v4 - with: - context: . - push: true - tags: | - ${{ env.REGISTRY_IMAGE }}:nightly-slim - build-contexts: | - messense/rust-musl-cross:amd64-musl=docker-image://messense/rust-musl-cross:x86_64-musl - messense/rust-musl-cross:arm64-musl=docker-image://messense/rust-musl-cross:aarch64-musl - file: ./docker/Dockerfile.slim - cache-from: type=gha - cache-to: type=gha,mode=max - platforms: linux/amd64,linux/arm64 + ${{ env.GHCR }}/${{ env.GHCR_IMAGE_NAME }}:latest${{ matrix.tags-suffix }} + ${{ env.DH_REGISTRY_NAME }}:latest${{ matrix.tags-suffix }} + ${{ matrix.aliases }} + build-contexts: ${{ matrix.build-contexts }} + file: ${{ matrix.dockerfile }} + cache-from: type=gha,scope=rpxy-latest-${{ matrix.target }} + cache-to: type=gha,mode=max,scope=rpxy-latest-${{ matrix.target }} + platforms: ${{ matrix.platforms }} labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/ghcr_build_push.yml b/.github/workflows/ghcr_build_push.yml deleted file mode 100644 index 84317fb..0000000 --- a/.github/workflows/ghcr_build_push.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: Build and Publish Docker Images to ghcr.io - -on: - push: - branches: - - "feat/s2n-quic" - -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - -jobs: - build_and_push: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - target: "default" - dockerfile: ./docker/Dockerfile - platforms: linux/amd64 - - - target: "s2n" - dockerfile: ./docker/Dockerfile - build-args: | - "CARGO_FEATURES=--no-default-features --features http3-s2n" - "ADDITIONAL_DEPS=pkg-config libssl-dev cmake libclang1 gcc g++" - platforms: linux/amd64 - tags-suffix: "-s2n" - - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Docker meta - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - # 今Pushし放題なので注意 - - name: Nightly build and push - uses: docker/build-push-action@v4 - with: - context: . - build-args: ${{ matrix.build-args }} - push: true - tags: | - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:nightly${{ matrix.tags-suffix }} - file: ${{ matrix.dockerfile }} - cache-from: type=gha,scope=rpxy-${{ matrix.target }} - cache-to: type=gha,mode=max,scope=rpxy-${{ matrix.target }} - platforms: ${{ matrix.platforms }} - labels: ${{ steps.meta.outputs.labels }} From 2893d94ce7e623d5718c5ae6457829eba22cbf14 Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Wed, 2 Aug 2023 14:58:50 +0900 Subject: [PATCH 10/14] feat: update github actions --- .github/workflows/docker_build_push.yml | 28 +++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docker_build_push.yml b/.github/workflows/docker_build_push.yml index deb4854..a3930c8 100644 --- a/.github/workflows/docker_build_push.yml +++ b/.github/workflows/docker_build_push.yml @@ -73,12 +73,27 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Nightly build and push + - name: Nightly build test on amd64 for pull requests + if: ${{ github.event_name == 'pull_request' }} uses: docker/build-push-action@v4 with: context: . build-args: ${{ matrix.build-args }} - push: ${{ github.event_name != 'pull_request' }} + push: false + build-contexts: ${{ matrix.build-contexts }} + file: ${{ matrix.dockerfile }} + cache-from: type=gha,scope=rpxy-nightly-${{ matrix.target }} + cache-to: type=gha,mode=max,scope=rpxy-nightly-${{ matrix.target }} + platforms: linux/amd64 + labels: ${{ steps.meta.outputs.labels }} + + - name: Nightly build and push from develop branch + if: ${{ (github.ref_name == 'develop') && (github.event_name == 'push') }} + uses: docker/build-push-action@v4 + with: + context: . + build-args: ${{ matrix.build-args }} + push: true tags: | ${{ env.GHCR }}/${{ env.GHCR_IMAGE_NAME }}:nightly${{ matrix.tags-suffix }} ${{ env.DH_REGISTRY_NAME }}:nightly${{ matrix.tags-suffix }} @@ -89,17 +104,18 @@ jobs: platforms: ${{ matrix.platforms }} labels: ${{ steps.meta.outputs.labels }} - - name: Release build and push slim - if: ${{ github.ref_name == 'main' }} + - name: Release build and push from main branch + if: ${{ (github.ref_name == 'main') && (github.event_name == 'push') }} uses: docker/build-push-action@v4 with: context: . build-args: ${{ matrix.build-args }} - push: ${{ github.event_name != 'pull_request' }} + push: true tags: | ${{ env.GHCR }}/${{ env.GHCR_IMAGE_NAME }}:latest${{ matrix.tags-suffix }} ${{ env.DH_REGISTRY_NAME }}:latest${{ matrix.tags-suffix }} - ${{ matrix.aliases }} + ${{ env.GHCR }}/${{ env.GHCR_IMAGE_NAME }}:${{ matrix.aliases }} + ${{ env.DH_REGISTRY_NAME }}:${{ matrix.aliases }} build-contexts: ${{ matrix.build-contexts }} file: ${{ matrix.dockerfile }} cache-from: type=gha,scope=rpxy-latest-${{ matrix.target }} From 9645b2c3216432f39f79328088e562abf8339d51 Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Wed, 2 Aug 2023 17:31:31 +0900 Subject: [PATCH 11/14] docs: update docker docs --- .github/workflows/docker_build_push.yml | 3 +++ README.md | 4 +++- TODO.md | 2 +- docker/README.md | 19 +++++++++++++++++++ docker/docker-compose.slim.yml | 20 ++++++++++---------- docker/docker-compose.yml | 20 ++++++++++---------- 6 files changed, 46 insertions(+), 22 deletions(-) create mode 100644 docker/README.md diff --git a/.github/workflows/docker_build_push.yml b/.github/workflows/docker_build_push.yml index a3930c8..71d67ad 100644 --- a/.github/workflows/docker_build_push.yml +++ b/.github/workflows/docker_build_push.yml @@ -41,6 +41,9 @@ jobs: "ADDITIONAL_DEPS=pkg-config libssl-dev cmake libclang1 gcc g++" platforms: linux/amd64,linux/arm64 tags-suffix: "-s2n" + # Aliases must be used only for release builds + aliases: | + "s2n" steps: - name: Checkout diff --git a/README.md b/README.md index 074f91e..f14668d 100644 --- a/README.md +++ b/README.md @@ -236,7 +236,9 @@ Since it is currently a work-in-progress project, we are frequently adding new o ## Using Docker Image -You can also use [docker image](https://hub.docker.com/r/jqtype/rpxy) instead of directly executing the binary. There are only several docker-specific environment variables. +You can also use `docker` image hosted on [Docker Hub](https://hub.docker.com/r/jqtype/rpxy) and [GitHub Container Registry](https://github.com/junkurihara/rust-rpxy/pkgs/container/rust-rpxy) instead of directly executing the binary. See [`./docker/README.md`](./docker/README.md) for the differences on image tags. + +There are only several docker-specific environment variables. - `HOST_USER` (default: `user`): User name executing `rpxy` inside the container. - `HOST_UID` (default: `900`): `UID` of `HOST_USER`. diff --git a/TODO.md b/TODO.md index f3d29a5..e93bfcb 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,5 @@ # TODO List -- [Try in v0.5.0] **Use `gchr.io`** - [Try in v0.5.0] **Cache option for the response with `Cache-Control: public` header directive ([#55](https://github.com/junkurihara/rust-rpxy/issues/55))** - Improvement of path matcher - More flexible option for rewriting path @@ -25,6 +24,7 @@ - add option for sticky cookie name - add option for sticky cookie duration +- Done in v0.5.0 ~~**Use `gchr.io`**~~ - Done in v0.5.0: ~~Consideration on migrating from `quinn` and `h3-quinn` to other QUIC implementations ([#57](https://github.com/junkurihara/rust-rpxy/issues/57))~~ - Done in v0.4.0: diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000..74344a7 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,19 @@ +# Docker Images of `rpxy` + +The `rpxy` docker images are hosted both on [Docker Hub](https://hub.docker.com/r/jqtype/rpxy) and [GitHub Container Registry](https://github.com/junkurihara/rust-rpxy/pkgs/container/rust-rpxy). Differences among tags are summarized as follows. + +## Latest Builds + +- `latest`: Built from the `main` branch with default features, running on Ubuntu. +- `latest-slim`, `slim`: Built by `musl` from the `main` branch with default features, running on Alpine. +- `latest-s2n`, `s2n`: Built from the `main` branch with the `http3-s2n` feature, running on Ubuntu. + +## Nightly Builds + +- `nightly`: Built from the `develop` branch with default features, running on Ubuntu. +- `nightly-slim`: Built by `musl` from the `develop` branch with default features, running on Alpine. +- `nightly-s2n`: Built from the `develop` branch with the `http3-s2n` feature, running on Ubuntu. + +## Caveats + +Due to some compile errors of `s2n-quic` subpackages with `musl`, `nightly-s2n-slim` or `latest-s2n-slim` are not yet provided. diff --git a/docker/docker-compose.slim.yml b/docker/docker-compose.slim.yml index b147562..9e9f958 100644 --- a/docker/docker-compose.slim.yml +++ b/docker/docker-compose.slim.yml @@ -1,7 +1,7 @@ version: "3" services: rpxy-rp: - image: jqtype/rpxy + image: jqtype/rpxy:slim # ghcr.io/junkurihara/rust-rpxy:slim also works container_name: rpxy init: true restart: unless-stopped @@ -9,15 +9,15 @@ services: - 127.0.0.1:8080:8080/tcp - 127.0.0.1:8443:8443/udp - 127.0.0.1:8443:8443/tcp - build: - context: ../ - additional_contexts: # Uncomment when you build with musl - - messense/rust-musl-cross:amd64-musl=docker-image://messense/rust-musl-cross:x86_64-musl - - messense/rust-musl-cross:arm64-musl=docker-image://messense/rust-musl-cross:aarch64-musl - dockerfile: ./docker/Dockerfile.slim # based on alpine and build x86_64-unknown-linux-musl - platforms: # Choose your platforms - - "linux/amd64" - # - "linux/arm64" + # build: # Uncomment if you build yourself + # context: ../ + # additional_contexts: + # - messense/rust-musl-cross:amd64-musl=docker-image://messense/rust-musl-cross:x86_64-musl + # - messense/rust-musl-cross:arm64-musl=docker-image://messense/rust-musl-cross:aarch64-musl + # dockerfile: ./docker/Dockerfile.slim # based on alpine and build x86_64-unknown-linux-musl + # platforms: # Choose your platforms + # - "linux/amd64" + # # - "linux/arm64" environment: - LOG_LEVEL=debug - LOG_TO_FILE=true diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 6d74959..bf56ace 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,7 +1,7 @@ version: "3" services: rpxy-rp: - image: jqtype/rpxy + image: jqtype/rpxy:latest # ghcr.io/junkurihara/rust-rpxy:latest also works container_name: rpxy init: true restart: unless-stopped @@ -9,15 +9,15 @@ services: - 127.0.0.1:8080:8080/tcp - 127.0.0.1:8443:8443/udp - 127.0.0.1:8443:8443/tcp - build: - context: ../ - args: # Uncomment when build quic-s2n version - - "CARGO_FEATURES=--no-default-features --features http3-s2n" - - "ADDITIONAL_DEPS=pkg-config libssl-dev cmake libclang1 gcc g++" - dockerfile: ./docker/Dockerfile # based on ubuntu 22.04 and build x86_64-unknown-linux-gnu - platforms: # Choose your platforms - - "linux/amd64" - # - "linux/arm64" + # build: # Uncomment if you build yourself + # context: ../ + # args: # Uncomment when build quic-s2n version + # - "CARGO_FEATURES=--no-default-features --features http3-s2n" + # - "ADDITIONAL_DEPS=pkg-config libssl-dev cmake libclang1 gcc g++" + # dockerfile: ./docker/Dockerfile # based on ubuntu 22.04 and build x86_64-unknown-linux-gnu + # platforms: # Choose your platforms + # - "linux/amd64" + # # - "linux/arm64" environment: - LOG_LEVEL=debug - LOG_TO_FILE=true From 240d8c444f173a327eb9b5176d60fb38cd7d1cd0 Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Wed, 2 Aug 2023 17:38:34 +0900 Subject: [PATCH 12/14] deps --- rpxy-bin/Cargo.toml | 2 +- s2n-quic | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rpxy-bin/Cargo.toml b/rpxy-bin/Cargo.toml index 6b0b9f4..d7f5808 100644 --- a/rpxy-bin/Cargo.toml +++ b/rpxy-bin/Cargo.toml @@ -23,7 +23,7 @@ rpxy-lib = { path = "../rpxy-lib/", default-features = false, features = [ anyhow = "1.0.72" rustc-hash = "1.1.0" -serde = { version = "1.0.179", default-features = false, features = ["derive"] } +serde = { version = "1.0.180", default-features = false, features = ["derive"] } derive_builder = "0.12.0" tokio = { version = "1.29.1", default-features = false, features = [ "net", diff --git a/s2n-quic b/s2n-quic index 179acb8..8ef0a6b 160000 --- a/s2n-quic +++ b/s2n-quic @@ -1 +1 @@ -Subproject commit 179acb8a873eafbfc7b68de4018cd251caddfa44 +Subproject commit 8ef0a6b66a856dc9f34ce18159c617ac29154cc7 From 816bfbb909677f50d51529374d45f15969d1e9e4 Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Wed, 2 Aug 2023 17:42:08 +0900 Subject: [PATCH 13/14] chore: fix dockerfile slim name --- .github/workflows/docker_build_push.yml | 2 +- docker/{Dockerfile.slim => Dockerfile-slim} | 0 docker/{docker-compose.slim.yml => docker-compose-slim.yml} | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename docker/{Dockerfile.slim => Dockerfile-slim} (100%) rename docker/{docker-compose.slim.yml => docker-compose-slim.yml} (94%) diff --git a/.github/workflows/docker_build_push.yml b/.github/workflows/docker_build_push.yml index 71d67ad..018c890 100644 --- a/.github/workflows/docker_build_push.yml +++ b/.github/workflows/docker_build_push.yml @@ -24,7 +24,7 @@ jobs: platforms: linux/amd64,linux/arm64 - target: "default-slim" - dockerfile: ./docker/Dockerfile.slim + dockerfile: ./docker/Dockerfile-slim build-contexts: | messense/rust-musl-cross:amd64-musl=docker-image://messense/rust-musl-cross:x86_64-musl messense/rust-musl-cross:arm64-musl=docker-image://messense/rust-musl-cross:aarch64-musl diff --git a/docker/Dockerfile.slim b/docker/Dockerfile-slim similarity index 100% rename from docker/Dockerfile.slim rename to docker/Dockerfile-slim diff --git a/docker/docker-compose.slim.yml b/docker/docker-compose-slim.yml similarity index 94% rename from docker/docker-compose.slim.yml rename to docker/docker-compose-slim.yml index 9e9f958..8dcba55 100644 --- a/docker/docker-compose.slim.yml +++ b/docker/docker-compose-slim.yml @@ -14,7 +14,7 @@ services: # additional_contexts: # - messense/rust-musl-cross:amd64-musl=docker-image://messense/rust-musl-cross:x86_64-musl # - messense/rust-musl-cross:arm64-musl=docker-image://messense/rust-musl-cross:aarch64-musl - # dockerfile: ./docker/Dockerfile.slim # based on alpine and build x86_64-unknown-linux-musl + # dockerfile: ./docker/Dockerfile-slim # based on alpine and build x86_64-unknown-linux-musl # platforms: # Choose your platforms # - "linux/amd64" # # - "linux/arm64" From 738b4009a5cdf9ce3de3184dfd4e11c1fd3afbd2 Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Wed, 2 Aug 2023 18:00:09 +0900 Subject: [PATCH 14/14] docs --- CHANGELOG.md | 7 ++++++- TODO.md | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 000a48c..2df06ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,15 @@ # CHANGELOG -## 0.5.0 (unreleased) +## 0.6.0 (unreleased) + +## 0.5.0 ### Improvement - Feat: `s2n-quic` with `s2n-quic-h3` is supported as QUIC and HTTP/3 library in addition to `quinn` with `h3-quinn`, related to #57. +- Feat: Publish dockerfile for `rpxy` with `s2n-quic` on both `amd64` and `arm64`. +- Feat: Start to publish docker images on `ghcr.io` +- Refactor: logs of minor improvements ## 0.4.0 diff --git a/TODO.md b/TODO.md index e93bfcb..eb95265 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,6 @@ # TODO List -- [Try in v0.5.0] **Cache option for the response with `Cache-Control: public` header directive ([#55](https://github.com/junkurihara/rust-rpxy/issues/55))** +- [Try in v0.6.0] **Cache option for the response with `Cache-Control: public` header directive ([#55](https://github.com/junkurihara/rust-rpxy/issues/55))** - Improvement of path matcher - More flexible option for rewriting path - Refactoring