Merge pull request #238 from junkurihara/develop

0.9.7
This commit is contained in:
Jun Kurihara 2025-02-01 05:30:51 +09:00 committed by GitHub
commit cb2099fe77
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 120 additions and 43 deletions

View file

@ -19,6 +19,16 @@ updates:
schedule: schedule:
interval: "daily" interval: "daily"
- package-ecosystem: "cargo"
directory: "/rpxy-certs"
schedule:
interval: "daily"
- package-ecosystem: "cargo"
directory: "/rpxy-acme"
schedule:
interval: "daily"
# Enable version updates for Docker # Enable version updates for Docker
- package-ecosystem: "docker" - package-ecosystem: "docker"
directory: "/docker" directory: "/docker"

View file

@ -16,7 +16,7 @@ env:
jobs: jobs:
build_and_push: build_and_push:
runs-on: ubuntu-latest runs-on: ubuntu-22.04
if: ${{ github.event_name == 'push' }} || ${{ github.event_name == 'pull_request' && github.event.pull_request.merged == true }} if: ${{ github.event_name == 'push' }} || ${{ github.event_name == 'pull_request' && github.event.pull_request.merged == true }}
strategy: strategy:
fail-fast: false fail-fast: false
@ -170,6 +170,14 @@ jobs:
platforms: ${{ matrix.platforms }} platforms: ${{ matrix.platforms }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
- name: check pull_request title
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref == 'develop' && github.event.pull_request.base.ref == 'main' && github.event.pull_request.merged == true }}
uses: kaisugi/action-regex-match@v1.0.1
id: regex-match
with:
text: ${{ github.event.pull_request.title }}
regex: "^(\\d+\\.\\d+\\.\\d+)$"
- name: Release build and push from main branch - name: Release build and push from main branch
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref == 'develop' && github.event.pull_request.base.ref == 'main' && github.event.pull_request.merged == true }} if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref == 'develop' && github.event.pull_request.base.ref == 'main' && github.event.pull_request.merged == true }}
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
@ -181,6 +189,8 @@ jobs:
${{ env.GHCR }}/${{ env.GHCR_IMAGE_NAME }}:latest${{ matrix.tags-suffix }} ${{ env.GHCR }}/${{ env.GHCR_IMAGE_NAME }}:latest${{ matrix.tags-suffix }}
${{ env.DH_REGISTRY_NAME }}:latest${{ matrix.tags-suffix }} ${{ env.DH_REGISTRY_NAME }}:latest${{ matrix.tags-suffix }}
${{ matrix.aliases }} ${{ matrix.aliases }}
${{ env.GHCR }}/${{ env.GHCR_IMAGE_NAME }}:${{ github.event.pull_request.title }}${{ matrix.tags-suffix }}
${{ env.DH_REGISTRY_NAME }}:${{ github.event.pull_request.title }}${{ matrix.tags-suffix }}
build-contexts: ${{ matrix.build-contexts }} build-contexts: ${{ matrix.build-contexts }}
file: ${{ matrix.dockerfile }} file: ${{ matrix.dockerfile }}
cache-from: type=gha,scope=rpxy-latest-${{ matrix.target }} cache-from: type=gha,scope=rpxy-latest-${{ matrix.target }}

View file

@ -1,6 +1,22 @@
# CHANGELOG # CHANGELOG
## 0.9.7 or 0.10.0 (Unreleased) ## 0.9.8 or 0.10.0 (Unreleased)
## 0.9.7
### Improvement
- Feat: add version tag for docker images via github actions
- Feat: support gRPC: This makes rpxy to serve gRPC requests on the same port as HTTP and HTTPS, i.e., listen_port and listen_port_tls. This means that by using the different subdomain for HTTP(S) and gRPC, we can multiplex them on same ports without opening another port dedicated to gRPC. To this end, this update made the forwarder to force HTTP/2 for gRPC requests towards backend (gRPC) app.
- Deps and refactor
### Bugfix
- Fixed bug for the upstream option "force_http2_upstream"
### Other
- Tentative downgrade of github actions `runs-on` from ubuntu-latest to ubuntu-22.04.
## 0.9.6 ## 0.9.6

View file

@ -1,5 +1,5 @@
[workspace.package] [workspace.package]
version = "0.9.6" version = "0.9.7"
authors = ["Jun Kurihara"] authors = ["Jun Kurihara"]
homepage = "https://github.com/junkurihara/rust-rpxy" homepage = "https://github.com/junkurihara/rust-rpxy"
repository = "https://github.com/junkurihara/rust-rpxy" repository = "https://github.com/junkurihara/rust-rpxy"

View file

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2024 Jun Kurihara Copyright (c) 2025 Jun Kurihara
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -14,15 +14,28 @@
[^pure_rust]: Doubtfully can be claimed to be written in pure Rust since current `rpxy` is based on `aws-lc-rs` for cryptographic operations. [^pure_rust]: Doubtfully can be claimed to be written in pure Rust since current `rpxy` is based on `aws-lc-rs` for cryptographic operations.
By 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] Furthermore, `rpxy` supports the automatic issuance and renewal of certificates via [TLS-ALPN-01 (RFC8737)](https://www.rfc-editor.org/rfc/rfc8737) of [ACME protocol (RFC8555)](https://www.rfc-editor.org/rfc/rfc8555) thanks to [`rustls-acme`](https://github.com/FlorianUekermann/rustls-acme), and the hybridized post-quantum key exchange [`X25519MLKEM768`](https://www.ietf.org/archive/id/draft-kwiatkowski-tls-ecdhe-mlkem-02.html)[^kyber] for TLS/QUIC incoming and outgoing initiation thanks to [`rustls-post-quantum`](https://docs.rs/rustls-post-quantum/latest/rustls_post_quantum/). Supported features are summarized as follows:
[^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. - Supported HTTP(S) protocols: HTTP/1.1, HTTP/2 and brand-new HTTP/3 [^h3lib]
- gRPC is also supported
- Serving multiple domain names with TLS termination
- Mutual TLS authentication with client certificates
- Automated certificate issuance and renewal via TLS-ALPN-01 ACME protocol [^acme]
- Post-quantum key exchange for TLS/QUIC [^kyber]
- TLS connection sanitization to avoid the domain fronting [^sanitization]
- Load balancing with round-robin, random, and sticky session
- and more...
[^kyber]: This is already a default feature. Also note that `X25519MLKEM768` is still a draft version yet this is widely used on the Internet. [^h3lib]: HTTP/3 is enabled 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). 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.
[^acme]: `rpxy` supports the automatic issuance and renewal of certificates via [TLS-ALPN-01 (RFC8737)](https://www.rfc-editor.org/rfc/rfc8737) of [ACME protocol (RFC8555)](https://www.rfc-editor.org/rfc/rfc8555) thanks to [`rustls-acme`](https://github.com/FlorianUekermann/rustls-acme).
[^kyber]: `rpxy` supports the hybridized post-quantum key exchange [`X25519MLKEM768`](https://www.ietf.org/archive/id/draft-kwiatkowski-tls-ecdhe-mlkem-02.html)[^kyber] for TLS/QUIC incoming and outgoing initiation thanks to [`rustls-post-quantum`](https://docs.rs/rustls-post-quantum/latest/rustls_post_quantum/). This is already a default feature. Also note that `X25519MLKEM768` is still a draft version yet this is widely used on the Internet.
[^sanitization]: By 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). We should note that NGINX doesn't guarantee such a consistency by default. To this end, you have to add `if` statement in the configuration file in NGINX.
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). 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).
[^1]: We should note that NGINX doesn't guarantee such a consistency by default. To this end, you have to add `if` statement in the configuration file in NGINX.
## Installing/Building an Executable Binary of `rpxy` ## Installing/Building an Executable Binary of `rpxy`
@ -422,6 +435,18 @@ Check a third party project [`Gamerboy59/rpxy-webui`](https://github.com/Gamerbo
todo! todo!
## Credits
`rpxy` cannot be built without the following projects and inspirations:
- [`hyper`](https://github.com/hyperium/hyper) and [`hyperium/h3`](https://github.com/hyperium/h3)
- [`rustls`](https://github.com/rustls/rustls)
- [`tokio`](https://github.com/tokio-rs/tokio)
- [`quinn`](https://github.com/quinn-rs/quinn)
- [`s2n-quic`](https://github.com/aws/s2n-quic)
- [`rustls-acme`](https://github.com/FlorianUekermann/rustls-acme)
## License ## License
`rpxy` is free, open-source software licensed under MIT License. `rpxy` is free, open-source software licensed under MIT License.

View file

@ -40,6 +40,7 @@ RUN apt-get update && apt-get install -qy --no-install-recommends $BUILD_DEPS ${
echo "Install toolchain" && \ echo "Install toolchain" && \
rustup target add $(cat /arch)-${TARGET_SUFFIX} && \ rustup target add $(cat /arch)-${TARGET_SUFFIX} && \
echo "Building rpxy from source" && \ echo "Building rpxy from source" && \
cargo update &&\
cargo build --release --target=$(cat /arch)-${TARGET_SUFFIX} ${CARGO_FEATURES} && \ cargo build --release --target=$(cat /arch)-${TARGET_SUFFIX} ${CARGO_FEATURES} && \
strip --strip-all /tmp/target/$(cat /arch)-${TARGET_SUFFIX}/release/rpxy &&\ strip --strip-all /tmp/target/$(cat /arch)-${TARGET_SUFFIX}/release/rpxy &&\
cp /tmp/target/$(cat /arch)-${TARGET_SUFFIX}/release/rpxy /tmp/target/release/rpxy cp /tmp/target/$(cat /arch)-${TARGET_SUFFIX}/release/rpxy /tmp/target/release/rpxy

View file

@ -25,6 +25,7 @@ COPY . /tmp/
ENV RUSTFLAGS "-C link-arg=-s" ENV RUSTFLAGS "-C link-arg=-s"
RUN echo "Building rpxy from source" && \ RUN echo "Building rpxy from source" && \
cargo update && \
cargo build --release --target $(cat /arch)-unknown-linux-musl ${CARGO_FEATURES} && \ cargo build --release --target $(cat /arch)-unknown-linux-musl ${CARGO_FEATURES} && \
musl-strip --strip-all /tmp/target/$(cat /arch)-unknown-linux-musl/release/rpxy && \ musl-strip --strip-all /tmp/target/$(cat /arch)-unknown-linux-musl/release/rpxy && \
cp /tmp/target/$(cat /arch)-unknown-linux-musl/release/rpxy /tmp/target/release/rpxy cp /tmp/target/$(cat /arch)-unknown-linux-musl/release/rpxy /tmp/target/release/rpxy

View file

@ -16,15 +16,15 @@ post-quantum = ["rustls-post-quantum"]
[dependencies] [dependencies]
url = { version = "2.5.4" } url = { version = "2.5.4" }
ahash = "0.8.11" ahash = "0.8.11"
thiserror = "2.0.9" thiserror = "2.0.11"
tracing = "0.1.41" tracing = "0.1.41"
async-trait = "0.1.84" async-trait = "0.1.85"
base64 = "0.22.1" base64 = "0.22.1"
aws-lc-rs = { version = "1.12.0", default-features = false, features = [ aws-lc-rs = { version = "1.12.2", default-features = false, features = [
"aws-lc-sys", "aws-lc-sys",
] } ] }
blocking = "1.6.1" blocking = "1.6.1"
rustls = { version = "0.23.20", default-features = false, features = [ rustls = { version = "0.23.22", default-features = false, features = [
"std", "std",
"aws_lc_rs", "aws_lc_rs",
] } ] }
@ -32,7 +32,7 @@ rustls-platform-verifier = { version = "0.5.0" }
rustls-acme = { path = "../submodules/rustls-acme/", default-features = false, features = [ rustls-acme = { path = "../submodules/rustls-acme/", default-features = false, features = [
"aws-lc-rs", "aws-lc-rs",
] } ] }
rustls-post-quantum = { version = "0.2.1", optional = true } rustls-post-quantum = { version = "0.2.2", optional = true }
tokio = { version = "1.42.0", default-features = false } tokio = { version = "1.43.0", default-features = false }
tokio-util = { version = "0.7.13", default-features = false } tokio-util = { version = "0.7.13", default-features = false }
tokio-stream = { version = "0.1.17", default-features = false } tokio-stream = { version = "0.1.17", default-features = false }

View file

@ -33,7 +33,7 @@ mimalloc = { version = "*", default-features = false }
anyhow = "1.0.95" anyhow = "1.0.95"
ahash = "0.8.11" ahash = "0.8.11"
serde = { version = "1.0.217", default-features = false, features = ["derive"] } serde = { version = "1.0.217", default-features = false, features = ["derive"] }
tokio = { version = "1.42.0", default-features = false, features = [ tokio = { version = "1.43.0", default-features = false, features = [
"net", "net",
"rt-multi-thread", "rt-multi-thread",
"time", "time",
@ -41,11 +41,11 @@ tokio = { version = "1.42.0", default-features = false, features = [
"macros", "macros",
] } ] }
tokio-util = { version = "0.7.13", default-features = false } tokio-util = { version = "0.7.13", default-features = false }
async-trait = "0.1.84" async-trait = "0.1.85"
futures-util = { version = "0.3.31", default-features = false } futures-util = { version = "0.3.31", default-features = false }
# config # config
clap = { version = "4.5.23", features = ["std", "cargo", "wrap_help"] } clap = { version = "4.5.27", features = ["std", "cargo", "wrap_help"] }
toml = { version = "0.8.19", default-features = false, features = ["parse"] } toml = { version = "0.8.19", default-features = false, features = ["parse"] }
hot_reload = "0.1.8" hot_reload = "0.1.8"
serde_ignored = "0.1.10" serde_ignored = "0.1.10"

View file

@ -19,10 +19,10 @@ http3 = []
ahash = { version = "0.8.11" } ahash = { version = "0.8.11" }
tracing = { version = "0.1.41" } tracing = { version = "0.1.41" }
derive_builder = { version = "0.20.2" } derive_builder = { version = "0.20.2" }
thiserror = { version = "2.0.9" } thiserror = { version = "2.0.11" }
hot_reload = { version = "0.1.8" } hot_reload = { version = "0.1.8" }
async-trait = { version = "0.1.84" } async-trait = { version = "0.1.85" }
rustls = { version = "0.23.20", default-features = false, features = [ rustls = { version = "0.23.22", default-features = false, features = [
"std", "std",
"aws_lc_rs", "aws_lc_rs",
] } ] }
@ -31,11 +31,11 @@ rustls-webpki = { version = "0.102.8", default-features = false, features = [
"std", "std",
"aws_lc_rs", "aws_lc_rs",
] } ] }
rustls-post-quantum = { version = "0.2.1", optional = true } rustls-post-quantum = { version = "0.2.2", optional = true }
x509-parser = { version = "0.16.0" } x509-parser = { version = "0.17.0" }
[dev-dependencies] [dev-dependencies]
tokio = { version = "1.42.0", default-features = false, features = [ tokio = { version = "1.43.0", default-features = false, features = [
"rt-multi-thread", "rt-multi-thread",
"macros", "macros",
] } ] }

View file

@ -36,12 +36,12 @@ post-quantum = [
] ]
[dependencies] [dependencies]
rand = "0.8.5" rand = "0.9.0"
ahash = "0.8.11" ahash = "0.8.11"
bytes = "1.9.0" bytes = "1.9.0"
derive_builder = "0.20.2" derive_builder = "0.20.2"
futures = { version = "0.3.31", features = ["alloc", "async-await"] } futures = { version = "0.3.31", features = ["alloc", "async-await"] }
tokio = { version = "1.42.0", default-features = false, features = [ tokio = { version = "1.43.0", default-features = false, features = [
"net", "net",
"rt-multi-thread", "rt-multi-thread",
"time", "time",
@ -50,17 +50,17 @@ tokio = { version = "1.42.0", default-features = false, features = [
"fs", "fs",
] } ] }
tokio-util = { version = "0.7.13", default-features = false } tokio-util = { version = "0.7.13", default-features = false }
pin-project-lite = "0.2.15" pin-project-lite = "0.2.16"
async-trait = "0.1.84" async-trait = "0.1.85"
# Error handling # Error handling
anyhow = "1.0.95" anyhow = "1.0.95"
thiserror = "2.0.9" thiserror = "2.0.11"
# http for both server and client # http for both server and client
http = "1.2.0" http = "1.2.0"
http-body-util = "0.1.2" http-body-util = "0.1.2"
hyper = { version = "1.5.2", default-features = false } hyper = { version = "1.6.0", default-features = false }
hyper-util = { version = "0.1.10", features = ["full"] } hyper-util = { version = "0.1.10", features = ["full"] }
futures-util = { version = "0.3.31", default-features = false } futures-util = { version = "0.3.31", default-features = false }
futures-channel = { version = "0.3.31", default-features = false } futures-channel = { version = "0.3.31", default-features = false }
@ -80,8 +80,8 @@ hyper-rustls = { version = "0.27.5", default-features = false, features = [
# tls and cert management for server # tls and cert management for server
rpxy-certs = { path = "../rpxy-certs/", default-features = false } rpxy-certs = { path = "../rpxy-certs/", default-features = false }
hot_reload = "0.1.8" hot_reload = "0.1.8"
rustls = { version = "0.23.20", default-features = false } rustls = { version = "0.23.22", default-features = false }
rustls-post-quantum = { version = "0.2.1", optional = true } rustls-post-quantum = { version = "0.2.2", optional = true }
tokio-rustls = { version = "0.26.1", features = ["early-data"] } tokio-rustls = { version = "0.26.1", features = ["early-data"] }
# acme # acme
@ -94,11 +94,11 @@ tracing = { version = "0.1.41" }
quinn = { version = "0.11.6", optional = true } quinn = { version = "0.11.6", optional = true }
h3 = { version = "0.0.6", features = ["tracing"], optional = true } h3 = { version = "0.0.6", features = ["tracing"], optional = true }
h3-quinn = { version = "0.0.7", optional = true } h3-quinn = { version = "0.0.7", optional = true }
s2n-quic = { version = "1.51.0", path = "../submodules/s2n-quic/quic/s2n-quic/", default-features = false, features = [ s2n-quic = { version = "1.52.1", path = "../submodules/s2n-quic/quic/s2n-quic/", default-features = false, features = [
"provider-tls-rustls", "provider-tls-rustls",
], optional = true } ], optional = true }
s2n-quic-core = { version = "0.51.0", path = "../submodules/s2n-quic/quic/s2n-quic-core", default-features = false, optional = true } s2n-quic-core = { version = "0.52.1", path = "../submodules/s2n-quic/quic/s2n-quic-core", default-features = false, optional = true }
s2n-quic-rustls = { version = "0.51.0", path = "../submodules/s2n-quic/quic/s2n-quic-rustls", optional = true } s2n-quic-rustls = { version = "0.52.1", path = "../submodules/s2n-quic/quic/s2n-quic-rustls", optional = true }
s2n-quic-h3 = { path = "../submodules/s2n-quic/quic/s2n-quic-h3/", features = [ s2n-quic-h3 = { path = "../submodules/s2n-quic/quic/s2n-quic-h3/", features = [
"tracing", "tracing",
], optional = true } ], optional = true }
@ -108,7 +108,7 @@ socket2 = { version = "0.5.8", features = ["all"], optional = true }
# cache # cache
http-cache-semantics = { path = "../submodules/rusty-http-cache-semantics", default-features = false, optional = true } http-cache-semantics = { path = "../submodules/rusty-http-cache-semantics", default-features = false, optional = true }
lru = { version = "0.12.5", optional = true } lru = { version = "0.13.0", optional = true }
sha2 = { version = "0.10.8", default-features = false, optional = true } sha2 = { version = "0.10.8", default-features = false, optional = true }
# cookie handling for sticky cookie # cookie handling for sticky cookie

View file

@ -80,8 +80,8 @@ impl LoadBalanceRandomBuilder {
impl LoadBalanceWithPointer for LoadBalanceRandom { impl LoadBalanceWithPointer for LoadBalanceRandom {
/// Returns the random index within the range /// Returns the random index within the range
fn get_ptr(&self, _info: Option<&LoadBalanceContext>) -> PointerToUpstream { fn get_ptr(&self, _info: Option<&LoadBalanceContext>) -> PointerToUpstream {
let mut rng = rand::thread_rng(); let mut rng = rand::rng();
let ptr = rng.gen_range(0..self.num_upstreams); let ptr = rng.random_range(0..self.num_upstreams);
PointerToUpstream { ptr, context: None } PointerToUpstream { ptr, context: None }
} }
} }

View file

@ -226,7 +226,9 @@ where
let connector = builder.https_or_http().enable_all_versions().wrap_connector(http.clone()); let connector = builder.https_or_http().enable_all_versions().wrap_connector(http.clone());
let connector_h2 = builder_h2.https_or_http().enable_http2().wrap_connector(http); let connector_h2 = builder_h2.https_or_http().enable_http2().wrap_connector(http);
let inner = Client::builder(LocalExecutor::new(_globals.runtime_handle.clone())).build::<_, B1>(connector); let inner = Client::builder(LocalExecutor::new(_globals.runtime_handle.clone())).build::<_, B1>(connector);
let inner_h2 = Client::builder(LocalExecutor::new(_globals.runtime_handle.clone())).build::<_, B1>(connector_h2); let inner_h2 = Client::builder(LocalExecutor::new(_globals.runtime_handle.clone()))
.http2_only(true)
.build::<_, B1>(connector_h2);
Ok(Self { Ok(Self {
inner, inner,

View file

@ -200,7 +200,7 @@ where
// Handle StatusCode::SWITCHING_PROTOCOLS in response // Handle StatusCode::SWITCHING_PROTOCOLS in response
let upgrade_in_response = extract_upgrade(res_backend.headers()); let upgrade_in_response = extract_upgrade(res_backend.headers());
let should_upgrade = match (upgrade_in_request.as_ref(), upgrade_in_response.as_ref()) { let should_upgrade = match (upgrade_in_request.as_ref(), upgrade_in_response.as_ref()) {
(Some(u_req), Some(u_res)) => u_req.to_ascii_lowercase() == u_res.to_ascii_lowercase(), (Some(u_req), Some(u_res)) => u_req.eq_ignore_ascii_case(u_res),
_ => false, _ => false,
}; };

View file

@ -272,7 +272,7 @@ pub(super) fn extract_upgrade(headers: &HeaderMap) -> Option<String> {
.to_str() .to_str()
.unwrap_or("") .unwrap_or("")
.split(',') .split(',')
.any(|w| w.trim().to_ascii_lowercase() == header::UPGRADE.as_str().to_ascii_lowercase()) .any(|w| w.trim().eq_ignore_ascii_case(header::UPGRADE.as_str()))
{ {
if let Some(u) = headers.get(header::UPGRADE) { if let Some(u) = headers.get(header::UPGRADE) {
if let Ok(m) = u.to_str() { if let Ok(m) = u.to_str() {

View file

@ -59,6 +59,18 @@ pub(super) fn update_request_line<B>(
upstream_chosen: &Upstream, upstream_chosen: &Upstream,
upstream_candidates: &UpstreamCandidates, upstream_candidates: &UpstreamCandidates,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
// If request is grpc, HTTP/2 is required
if req
.headers()
.get(header::CONTENT_TYPE)
.map(|v| v.as_bytes().starts_with(b"application/grpc"))
== Some(true)
{
debug!("Must be http/2 for gRPC request.");
*req.version_mut() = Version::HTTP_2;
return Ok(());
}
// If not specified (force_httpXX_upstream) and https, version is preserved except for http/3 // If not specified (force_httpXX_upstream) and https, version is preserved except for http/3
if upstream_chosen.uri.scheme() == Some(&Scheme::HTTP) { if upstream_chosen.uri.scheme() == Some(&Scheme::HTTP) {
// Change version to http/1.1 when destination scheme is http // Change version to http/1.1 when destination scheme is http

@ -1 +1 @@
Subproject commit af2d016b6aa4e09586253a0459efc4af6635c79b Subproject commit a65d7e7000b49e6e1e14daf32baee094f4d8dacd

@ -1 +1 @@
Subproject commit d5b5efd9de4dab3c958c50be5380652d801cc65f Subproject commit 2500716b70bd6e548cdf690188ded7afe6726330

@ -1 +1 @@
Subproject commit b49cb517d0256864a9382f04fedd0e9f71531d85 Subproject commit 78524172f54af5e3d5a0404b230d265c82eaf446