Skip to content

Add end-to-end HTTP/3 support and prepare v0.1.3-rc.10#40

Merged
vansour merged 5 commits intomainfrom
feat/http3-phase8-rc10
Apr 12, 2026
Merged

Add end-to-end HTTP/3 support and prepare v0.1.3-rc.10#40
vansour merged 5 commits intomainfrom
feat/http3-phase8-rc10

Conversation

@vansour
Copy link
Copy Markdown
Owner

@vansour vansour commented Apr 12, 2026

Summary

  • complete the staged HTTP/3 delivery plan through Phase 8 across config, runtime, downstream, upstream, gRPC, health checks, reload/restart, and release tooling
  • add dedicated downstream/upstream/gRPC over HTTP/3 integration coverage plus HTTP/3 drain and restart regression tests
  • wire HTTP/3 into the default validation gates, clippy gate, soak scripts, benchmark matrix, and bump the workspace version to 0.1.3-rc.10

Validation

  • ./scripts/test-fast.sh
  • ./scripts/run-clippy-gate.sh
  • ./scripts/test-slow.sh
  • ./scripts/run-tls-gate.sh
  • ./scripts/run-soak.sh --iterations 1
  • ./scripts/prepare-release.sh --tag v0.1.3-rc.10 --allow-dirty --skip-fetch

Copilot AI review requested due to automatic review settings April 12, 2026 10:26
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 12, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Summary by CodeRabbit

  • 新功能

    • 添加对 HTTP/3 的端到端支持:下游、上游与 gRPC over HTTP/3,含监听器配置、Alt‑Svc 宣告与发布/重载语义。
    • 增强监听器绑定与传输层可观测性(绑定清单、HTTP/3 状态、TLS/ALPN 信息)。
  • 测试

    • 新增大量 HTTP/3 集成/回归测试并扩展基准/浸泡矩阵。
  • 文档

    • 发布 HTTP/3 架构规划与 Phase‑0 冻结文档,更新 README 的 HTTP/3 章节。
  • 其他

    • CI/workflow 与本地脚本:引入 clippy gate 脚本并调整测试流水线。
    • 版本更新至 v0.1.3-rc.10。

Walkthrough

该 PR 为 rginx 引入 HTTP/3(QUIC)端到端支持:增加配置模型、编译/验证、上游/下游 HTTP/3 客户端与服务器、TLS/QUIC 集成、监听器绑定与继承、Admin 快照/状态扩展,以及大量 E2E/单元测试与 CI/脚本调整(含 Clippy gate 脚本)。

Changes

Cohort / File(s) Summary
架构与文档
ARCHITECTURE_HTTP3_PLAN.md, ARCHITECTURE_HTTP3_PHASE0_FREEZE.md, README.md
新增 HTTP/3 架构与发布计划及 Phase‑0 冻结文档,README 增补能力说明、基准/测试矩阵与发布门控条目。
CI 与脚本(Clippy gate / 测试分层)
.github/workflows/ci.yml, .github/workflows/release.yml, scripts/run-clippy-gate.sh, scripts/prepare-release.sh, scripts/run-tls-gate.sh, scripts/test-fast.sh, scripts/test-slow.sh, scripts/run-soak.sh, scripts/run-benchmark-matrix.py, scripts/run-tls-gate.sh
统一使用 ./scripts/run-clippy-gate.sh 作为 clippy 门控,调整工作流依赖,向测试/soak/基准脚本添加 HTTP/3 目标与参数。
工作区版本与依赖
Cargo.toml, crates/*/Cargo.toml, clippy.toml
升级 workspace 版本到 0.1.3-rc.10;新增 h3/h3-quinn/quinn 依赖(quinn 带特性);移除 rustls-pemfile;在 clippy.toml 指定 MSRV。
配置模型 / 编译 / 验证
crates/rginx-config/src/model.rs, crates/rginx-config/src/compile/*.rs, crates/rginx-config/src/validate/*.rs, configs/*.ron
新增 Http3Config(server/listener 级);编译/验证加入 HTTP/3 分支(要求同监听 TLS、TLS1.3、禁止 listener client_auth、拒绝 cleartext 上游),并调整示例配置示意为 Return。
核心类型与快照
crates/rginx-core/src/config.rs, crates/rginx-core/src/config/upstream.rs, crates/rginx-core/src/lib.rs, crates/rginx-http/src/state/snapshots.rs
新增 ListenerHttp3 / ListenerTransportBinding / ListenerApplicationProtocol / RuntimeListenerBindingSnapshot 等类型;UpstreamProtocol 增加 Http3;ConfigSnapshot 增加绑定计数与 HTTP/3 探测方法。
HTTP/3 服务器与网络层
crates/rginx-http/src/server/http3.rs, crates/rginx-http/src/server/mod.rs, crates/rginx-runtime/src/bootstrap/listeners.rs
新增完整 QUIC/H3 accept/connection/request 处理模块(bind/serve),集成 accept worker 与优雅排水;bootstrap 支持 UDP 绑定/继承并纳入启动/排水计数。
HTTP/3 上游客户端与代理
crates/rginx-http/src/proxy/clients/http3.rs, crates/rginx-http/src/proxy/clients/mod.rs, crates/rginx-http/src/proxy/clients/tls.rs
新增基于 quinn+h3 的 Http3ClientBufferedResponseBody;将 Proxy 客户端改为枚举以支持 Http / Http3;新增构建 HTTP/3 TLS 客户端配置。
请求处理与 Alt‑Svc 注入
crates/rginx-http/src/handler/dispatch.rs, crates/rginx-http/src/handler/mod.rs, crates/rginx-http/src/proxy/forward/*.rs, crates/rginx-http/src/proxy/forward/response.rs
统一下游请求体类型为 HttpBody,新增 alt_svc_header_value 计算并在下游响应可选注入 Alt‑Svc;转发到 HTTP/3 upstream 时拒绝 Upgrade;调整响应构建签名以携带 Alt‑Svc。
TLS / 证书解析与 PEM 处理
crates/rginx-http/src/tls/acceptor.rs, crates/rginx-http/src/tls/certificates.rs, crates/rginx-http/src/pki/certificate.rs, 多个测试模块
新增 HTTP/3 专用 TLS 配置构建(ALPN=h3、强制 TLS1.3 校验);将 PEM 解析从 rustls_pemfile 大规模迁移到 rustls::pki_types pem helpers,并统一 PEM 错误映射。
Admin / 运行时快照 / CLI 输出
crates/rginx-app/src/admin_cli/status.rs, crates/rginx-app/src/main.rs, crates/rginx-runtime/src/admin.rs, crates/rginx-http/src/state/*
Admin 快照模式版本提升(11→12);CLI/status 输出新增 listener binding 列表、bind_addrs、每-binding 记录及 TLS/http3 字段。
运行时重启 / 继承 / ListenerHandle
crates/rginx-runtime/src/restart.rs, crates/rginx-runtime/src/bootstrap/*, crates/rginx-runtime/src/bootstrap/reload.rs
ListenerHandle 支持可选 UDP socket;InheritedListeners 扩展为 tcp/udp 集合;重启/重载/初始组构建改为基于完整 ConfigSnapshot,支持 UDP 继承与地址冲突验证。
测试套件(大量 E2E / 单测)
crates/rginx-app/tests/*.rs, crates/*/tests/*, 新增测试文件(http3.rs, upstream_http3.rs, grpc_http3.rs, reload/* 等)
新增并扩展大量集成/单元测试覆盖:下游 HTTP/3、上游 HTTP/3(含 mTLS)、gRPC over HTTP/3、活跃健康检查、reload/restart 排水与重启验证、OCSP 场景、snapshot/status 断言等;并调整测试以使用新 PEM API 与新增字段。
基准与对比工具
scripts/nginx_compare/*, scripts/run-benchmark-matrix.py
为基准/比较 harness 与矩阵加入 rginx 的 HTTP/3 场景(curl --http3-only、grpc h3),并在渲染文档中标注 nginx 对比不支持 QUIC/HTTP/3。
杂项 / 忽略 / deny 配置
.gitignore, deny.toml
更新 .gitignore(perf、flamegraph、pycache 等);新增 deny.toml 限定构建目标与许可白名单。

Sequence Diagram

sequenceDiagram
    autonumber
    participant Client as Client (HTTP/3)
    participant QUIC as QUIC Endpoint / Listener
    participant Handler as rginx Handler
    participant Proxy as ProxyClient (HTTP / HTTP3)
    participant Upstream as Upstream Server

    Client->>QUIC: QUIC + TLS1.3 握手,H3 请求
    QUIC->>Handler: 转换为 Request<HttpBody>
    Handler->>Handler: 计算 alt_svc_header_value(listener, version)
    alt route is Return
        Handler->>Client: Response + Alt‑Svc header(若启用)
    else route is Proxy
        Handler->>Proxy: request(upstream, peer, Request<HttpBody>)
        alt upstream protocol == http3
            Proxy->>Proxy: build_http3_client_config()
            Proxy->>Upstream: QUIC connect (SNI/mTLS) + H3 request
            Upstream->>Proxy: H3 response headers/body/trailers
            Proxy->>Handler: Response<HttpBody>(BufferedResponseBody)
        else upstream protocol == http1/http2
            Proxy->>Upstream: HTTP/1.x 或 HTTP/2 请求
            Upstream->>Proxy: response
            Proxy->>Handler: Response<HttpBody>
        end
        Handler->>Handler: build_downstream_response(Response<HttpBody>, alt_svc)
        Handler->>Client: 下游响应(含可选 Alt‑Svc 注入)
    end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

🐰 春风把 UDP 吹醒,QUIC 在草丛间跳舞,

小兔数着绑定,Alt‑Svc 在信笺上诉说,
测试跑过隧道,脚本把关门槛不放松,
快照记录了每个监听与小小的端口梦,
我来撒胡萝卜庆祝:HTTP/3,出发去奔跑!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/http3-phase8-rc10

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request completes end-to-end HTTP/3 enablement across rginx (config/compile/validate, runtime bootstrap & restart inheritance, downstream accept loop, upstream proxy client, admin/check/status snapshots), wires HTTP/3 into release/CI gates, and prepares the v0.1.3-rc.10 release.

Changes:

  • Add downstream HTTP/3 server support (QUIC endpoint binding + accept/serve loop) and HTTP/3-aware listener bindings/snapshots.
  • Add upstream HTTP/3 proxy support (new UpstreamProtocol::Http3 + QUIC client plumbing) and expand reload/restart + drain test coverage.
  • Update release/CI tooling (clippy gate script, soak/slow/tls gates, benchmark matrix) and bump workspace/versioned docs to 0.1.3-rc.10.

Reviewed changes

Copilot reviewed 88 out of 89 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
scripts/test-slow.sh Run integration tests sequentially and enforce presence of required HTTP/3 gate targets.
scripts/test-fast.sh Convert fast tests into a labeled matrix loop + keep rginx bin unit tests.
scripts/run-tls-gate.sh Add HTTP/3 downstream/upstream/gRPC tests and normalize ROOT_DIR handling.
scripts/run-soak.sh Add HTTP/3 scenarios to the soak matrix.
scripts/run-clippy-gate.sh New clippy gate script used by CI/release workflows.
scripts/run-benchmark-matrix.py Add HTTP/3 and gRPC-over-HTTP/3 benchmark scenario flags.
scripts/prepare-release.sh Use the new clippy gate script in the release pipeline.
scripts/nginx_compare/scenarios.py Add rginx-only HTTP/3 return scenario and mark nginx as unsupported for HTTP/3.
scripts/nginx_compare/render.py Update report text to include HTTP/3 benchmarking context.
scripts/nginx_compare/configs.py Add an HTTP/3-enabled rginx return config generator.
README.md Document HTTP/3 support surface, gates, and update version to v0.1.3-rc.10.
crates/rginx-runtime/src/restart.rs Extend restart socket inheritance to include UDP sockets for HTTP/3 listeners.
crates/rginx-runtime/src/ocsp.rs Add/expand OCSP unit tests (HTTP responder, cache behaviors).
crates/rginx-runtime/src/health.rs Update test fixtures for new listener HTTP/3 field.
crates/rginx-runtime/src/bootstrap/shutdown.rs Add shutdown unit tests around draining/timeout behavior.
crates/rginx-runtime/src/bootstrap/reload.rs Pass full next config into listener-binding preparation.
crates/rginx-runtime/src/bootstrap/mod.rs Update initial listener group bootstrap to take full config and inherited sockets.
crates/rginx-runtime/src/bootstrap/listeners.rs Add UDP+HTTP/3 endpoint handling in listener worker groups (bind/activate/reload checks).
crates/rginx-runtime/src/admin.rs Bump admin snapshot schema version to 12.
crates/rginx-runtime/Cargo.toml Add quinn dependency for HTTP/3 runtime integration.
crates/rginx-http/src/transition.rs Extend reload/restart boundary reporting for HTTP/3 listener fields.
crates/rginx-http/src/tls/mod.rs Re-export HTTP/3 server TLS config builder.
crates/rginx-http/src/tls/certificates.rs Switch PEM parsing to rustls pki-types iterators and improve parse error mapping.
crates/rginx-http/src/tls/acceptor.rs Add HTTP/3-only server TLS config builder (TLS1.3 + ALPN h3 constraints).
crates/rginx-http/src/state/tls_runtime/listeners.rs Include HTTP/3 TLS/listener metadata in TLS status snapshots.
crates/rginx-http/src/state/tls_runtime/bindings.rs Update test fixtures for new listener HTTP/3 field.
crates/rginx-http/src/state/tests.rs Add snapshot tests for HTTP/3 bindings and snapshot-delta module filtering.
crates/rginx-http/src/state/snapshots.rs Add binding-level snapshot types and HTTP/3 fields to runtime/TLS listener snapshots.
crates/rginx-http/src/state/lifecycle.rs Populate new binding snapshot fields from listener transport bindings.
crates/rginx-http/src/server/tests.rs Update test PEM parsing to rustls pki-types iterators.
crates/rginx-http/src/server/mod.rs Introduce HTTP/3 server module and exports.
crates/rginx-http/src/server/http3.rs New downstream HTTP/3 server implementation (endpoint binding, accept loop, request/response bridging).
crates/rginx-http/src/server/graceful.rs Normalize request body type to HttpBody by boxing incoming bodies for handler.
crates/rginx-http/src/proxy/tests/mod.rs Update listener fixtures for new HTTP/3 field.
crates/rginx-http/src/proxy/tests/client_profiles.rs Extend upstream protocol tests to include HTTP/3 and client cache partitioning.
crates/rginx-http/src/proxy/request_body.rs Switch downstream request body handling to unified HttpBody.
crates/rginx-http/src/proxy/mod.rs Adjust proxy exports (keep ProxyClients public).
crates/rginx-http/src/proxy/health/registry.rs Update listener fixtures for new HTTP/3 field.
crates/rginx-http/src/proxy/health.rs Route upstream requests through protocol-aware ProxyClient::request.
crates/rginx-http/src/proxy/forward/response.rs Switch upstream response body type to unified HttpBody.
crates/rginx-http/src/proxy/forward/mod.rs Block upgrade requests to HTTP/3 upstreams; route requests through protocol-aware client.
crates/rginx-http/src/proxy/common.rs Map UpstreamProtocol::Http3 to HTTP/3 request version.
crates/rginx-http/src/proxy/clients/tls.rs Add QUIC/TLS client config builder for upstream HTTP/3 (TLS1.3 + ALPN h3).
crates/rginx-http/src/proxy/clients/tests.rs Update listener fixtures for new HTTP/3 field.
crates/rginx-http/src/proxy/clients/mod.rs Introduce protocol-aware ProxyClient enum (HTTP vs HTTP/3) and selection/build logic.
crates/rginx-http/src/proxy/clients/http3.rs New upstream HTTP/3 client using Quinn + h3.
crates/rginx-http/src/pki/certificate.rs Switch certificate chain PEM parsing to rustls pki-types iterators.
crates/rginx-http/src/lib.rs Re-export new runtime listener binding snapshot type.
crates/rginx-http/src/handler/tests.rs Add Alt-Svc injection test + update calls for new finalize signature.
crates/rginx-http/src/handler/mod.rs Introduce boxed_body helper and migrate handler to unified HttpBody.
crates/rginx-http/src/handler/dispatch.rs Inject Alt-Svc headers for eligible HTTPS requests; migrate to unified HttpBody.
crates/rginx-http/Cargo.toml Add HTTP/3 deps (h3, h3-quinn, quinn) and remove unused deps.
crates/rginx-core/src/lib.rs Export new listener binding-related types.
crates/rginx-core/src/config/upstream.rs Add UpstreamProtocol::Http3.
crates/rginx-core/src/config/tests.rs Add tests for listener binding inventory and HTTP/3 binding details.
crates/rginx-core/src/config.rs Add listener HTTP/3 config + transport binding inventory helpers/types.
crates/rginx-config/src/validate/upstream.rs Enforce HTTPS-only peers for HTTP/3 upstream protocol.
crates/rginx-config/src/validate/tests.rs Add validation tests for HTTP/3 listener TLS requirement + HTTP/3 upstream HTTPS requirement.
crates/rginx-config/src/validate/server.rs Add HTTP/3 listener validation and TCP/UDP bind de-duplication.
crates/rginx-config/src/model.rs Add HTTP/3 config schema and UpstreamProtocolConfig::Http3.
crates/rginx-config/src/compile/upstream.rs Compile HTTP/3 upstream protocol with HTTPS-only guard.
crates/rginx-config/src/compile/tests.rs Add compile coverage for HTTP/3 upstreams and HTTP/3 listener defaults.
crates/rginx-config/src/compile/server.rs Compile listener HTTP/3 metadata into core snapshot (default listen + Alt-Svc defaults).
crates/rginx-app/tests/upstream_server_name.rs Update test PEM parsing via rustls pki-types.
crates/rginx-app/tests/upstream_mtls.rs Update test PEM parsing via rustls pki-types.
crates/rginx-app/tests/upstream_http3.rs New integration tests for proxying to HTTP/3 upstreams (incl. mTLS + SNI override).
crates/rginx-app/tests/upstream_http2.rs Update test PEM parsing via rustls pki-types.
crates/rginx-app/tests/reload/restart_flow.rs Add restart regression coverage for HTTP/3 listener availability.
crates/rginx-app/tests/reload/reload_flow.rs Add drain regression coverage for removing an HTTP/3 listener on reload.
crates/rginx-app/tests/ocsp.rs Add integration tests for OCSP cache retention and reload recovery paths.
crates/rginx-app/tests/grpc_proxy.rs Update test PEM parsing via rustls pki-types.
crates/rginx-app/tests/downstream_mtls.rs Update test PEM parsing via rustls pki-types.
crates/rginx-app/tests/check.rs Update check output assertions for new reload boundary fields + default config changes; add HTTP/3 listener binding coverage.
crates/rginx-app/tests/admin/snapshot.rs Update schema version assertions and add HTTP/3 binding snapshot coverage.
crates/rginx-app/tests/admin/commands.rs Add status command coverage for HTTP/3 listener bindings.
crates/rginx-app/src/main.rs Extend check output to include listener binding inventory and HTTP/3/TLS listener details.
crates/rginx-app/src/admin_cli/status.rs Extend status CLI output to include listener binding details and HTTP/3 state.
crates/rginx-app/Cargo.toml Add HTTP/3 deps for integration tests; remove unused http dep.
configs/rginx.ron Make default install config self-contained (no upstream), return a welcome page.
configs/conf.d/default.ron Make default vhost config self-contained (Return handler) and clarify proxy example location.
clippy.toml Add explicit MSRV for clippy baseline.
Cargo.toml Bump workspace version to 0.1.3-rc.10 and add workspace deps for HTTP/3 (h3/h3-quinn/quinn).
Cargo.lock Lock new HTTP/3 and Quinn dependency graph + bump crate versions.
ARCHITECTURE_HTTP3_PHASE0_FREEZE.md Add Phase 0 HTTP/3 semantics freeze document (dated 2026-04-12).
.github/workflows/release.yml Switch clippy step to run the clippy gate script.
.github/workflows/ci.yml Add clippy as an explicit dependency and use the clippy gate script.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/rginx-http/src/proxy/clients/mod.rs
Comment thread crates/rginx-http/src/proxy/clients/http3.rs
Comment thread crates/rginx-http/src/proxy/clients/http3.rs
Comment thread crates/rginx-runtime/src/bootstrap/listeners.rs
Comment thread crates/rginx-runtime/src/restart.rs
Comment thread crates/rginx-http/src/server/http3.rs
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (8)
.github/workflows/release.yml (1)

123-124: 建议将 Clippy Gate 前置到慢测之前以更快失败。

当前命令本身没问题;但可把该步骤提前到 fast tests 后,减少无效慢测开销并与 prepare-release.sh 顺序保持一致。

♻️ 建议调整步骤顺序(示意)
       - name: Run Fast Test Layer
         run: ./scripts/test-fast.sh

+      - name: Run Clippy
+        run: ./scripts/run-clippy-gate.sh
+
       - name: Run Slow Test Layer
         run: ./scripts/test-slow.sh

       - name: Run TLS Release Gate
         run: ./scripts/run-tls-gate.sh
-
-      - name: Run Clippy
-        run: ./scripts/run-clippy-gate.sh
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 123 - 124, 将名为 "Run Clippy"
的步骤(当前调用 ./scripts/run-clippy-gate.sh)从其当前位置提前:把它放在 fast tests 之后、slow tests
之前,以便更快失败并减少无谓的慢测开销,同时使步骤顺序与 prepare-release.sh 保持一致;修改 release.yml 中该 job/step
的顺序即可,确保依赖关系(如果有)仍然满足。
scripts/nginx_compare/configs.py (1)

132-176: 建议抽取公共返回配置模板,减少重复维护

rginx_http3_return_configrginx_tls_return_config 的主体几乎一致,后续改路由或 runtime 参数时容易出现不一致。建议把公共骨架抽到一个内部 helper,仅注入 server 扩展段。

可选重构示例
+def _rginx_return_config_with_server_extra(port: int, server_extra: str) -> str:
+    return textwrap.dedent(
+        f"""\
+        Config(
+            runtime: RuntimeConfig(
+                shutdown_timeout_secs: 5,
+                worker_threads: Some({BENCHMARK_WORKERS}),
+                accept_workers: Some({BENCHMARK_WORKERS}),
+            ),
+            server: ServerConfig(
+                listen: "127.0.0.1:{port}",
+                keep_alive: Some(true),
+{server_extra}
+            ),
+            upstreams: [],
+            locations: [
+                LocationConfig(
+                    matcher: Exact("/-/ready"),
+                    handler: Return(status: 200, location: "", body: Some("ready\\n")),
+                ),
+                LocationConfig(
+                    matcher: Exact("/"),
+                    handler: Return(status: 200, location: "", body: Some("ok\\n")),
+                ),
+            ],
+            servers: [],
+        )
+        """
+    )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/nginx_compare/configs.py` around lines 132 - 176, Both
rginx_http3_return_config and rginx_tls_return_config share nearly identical
config bodies; extract the common template into a helper (e.g.,
_rginx_base_return_config) that builds the shared runtime, upstreams, locations,
and servers, and accepts a parameter for the server-specific block (the
TLS/http3 server stanza). Update rginx_http3_return_config and
rginx_tls_return_config to call this helper, passing their respective server
extension (tls/http3) strings (or a small builder function) so future changes to
runtime/routes are maintained in one place.
crates/rginx-runtime/src/ocsp.rs (1)

429-445: 测试 responder 线程建议显式回收,降低偶发卡住风险

当前 spawn_http_responder 启动后不返回 JoinHandle;若请求在连接前失败,线程可能在 accept() 持续阻塞。建议返回句柄并在测试末尾 join(或引入超时退出)以提升测试稳定性。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/rginx-runtime/src/ocsp.rs` around lines 429 - 445,
spawn_http_responder currently spawns a thread with thread::spawn and never
returns its JoinHandle, leaving the responder possibly blocked in accept() and
causing flaky tests; modify spawn_http_responder to return the
thread::JoinHandle (or a wrapper that can be joined) and propagate that handle
to the caller (tests) so they can join() the thread (or use a timeout/wakeup) at
test teardown, and update callers to join the handle to ensure the responder
thread is explicitly joined/cleaned up; key symbols: spawn_http_responder,
thread::spawn, JoinHandle.
crates/rginx-app/tests/admin/snapshot.rs (1)

126-130: 建议避免按下标断言 binding 顺序。

当前断言依赖 bindings 固定顺序,后续只要内部排序策略调整,测试就会出现非功能性失败。建议改为按 transport 查找目标 binding 再断言字段。

可参考的改法
-    assert_eq!(listener.bindings[0].transport, "tcp");
-    assert_eq!(listener.bindings[1].transport, "udp");
-    assert_eq!(listener.bindings[1].protocols, vec!["http3".to_string()]);
-    assert_eq!(listener.bindings[1].advertise_alt_svc, Some(true));
-    assert_eq!(listener.bindings[1].alt_svc_max_age_secs, Some(7200));
+    let udp_binding = listener
+        .bindings
+        .iter()
+        .find(|binding| binding.transport == "udp")
+        .expect("snapshot should include udp binding");
+    assert_eq!(udp_binding.protocols, vec!["http3".to_string()]);
+    assert_eq!(udp_binding.advertise_alt_svc, Some(true));
+    assert_eq!(udp_binding.alt_svc_max_age_secs, Some(7200));
-    assert_eq!(
-        snapshot["status"]["listeners"][0]["bindings"][1]["transport"],
-        serde_json::Value::from("udp")
-    );
-    assert_eq!(
-        snapshot["status"]["listeners"][0]["bindings"][1]["protocols"][0],
-        serde_json::Value::from("http3")
-    );
+    let udp_json = snapshot["status"]["listeners"][0]["bindings"]
+        .as_array()
+        .and_then(|bindings| {
+            bindings.iter().find(|binding| binding["transport"] == serde_json::Value::from("udp"))
+        })
+        .expect("snapshot JSON should include udp binding");
+    assert_eq!(udp_json["protocols"][0], serde_json::Value::from("http3"));

Also applies to: 146-155

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/rginx-app/tests/admin/snapshot.rs` around lines 126 - 130, Current
assertions rely on fixed ordering in listener.bindings (using bindings[0],
bindings[1]) which is brittle; update tests to locate the target binding by its
transport field instead of by index. For example, search listener.bindings for
the binding where binding.transport == "tcp" or "udp" (using iterator/find or
equivalent) and then assert on that binding's protocols, advertise_alt_svc, and
alt_svc_max_age_secs; apply the same change for the other assertions around the
listener.bindings usage (including the block referenced at lines ~146-155).
crates/rginx-app/tests/grpc_http3.rs (1)

105-109: 临时目录清理在测试失败时可能泄漏。

当前的清理逻辑在测试结束时通过 fs::remove_dir_all 执行。如果测试中的 assert! 失败,清理代码不会执行,导致临时目录残留。

考虑使用 Drop 守卫或 tempfile crate 来确保清理总是发生。不过这是测试代码,影响有限,可以作为后续优化。

🔧 可选:使用 tempfile crate 自动清理
// 使用 tempfile::TempDir 代替手动创建的目录
let shared_dir = tempfile::tempdir().expect("temp dir should be created");
// TempDir 会在 Drop 时自动清理
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/rginx-app/tests/grpc_http3.rs` around lines 105 - 109, The test
currently calls fs::remove_dir_all on upstream_temp_dir and shared_dir after
server.shutdown_and_wait and upstream_task.await, which will not run if the test
panics and thus leaks temp directories; replace the manual dirs with
tempfile::TempDir (or wrap the paths in a custom Drop guard) so cleanup happens
on Drop even if assertions fail, and update usages that reference
upstream_temp_dir and shared_dir to use TempDir::path() (or your guard's path
accessor); keep server.shutdown_and_wait and upstream_task.await where
appropriate but remove the final fs::remove_dir_all calls since the TempDir/Drop
guard will guarantee removal.
crates/rginx-app/tests/http3.rs (1)

249-340: 建议:HTTP/3 客户端清理可优化

http3_request 函数在 Line 336 中止 driver task 后立即关闭 endpoint,但未等待 driver task 完成。在测试场景下这是可接受的,但如果要提升健壮性,可以考虑短暂等待。

另外,当 body 参数为 None 时,Line 300-305 的逻辑仍然正确,因为直接跳过并调用 finish()

♻️ 可选的清理优化
     driver_task.abort();
+    let _ = driver_task.await;
     endpoint.close(quinn::VarInt::from_u32(0), b"done");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/rginx-app/tests/http3.rs` around lines 249 - 340, The driver task
launched in http3_request is aborted and the endpoint is immediately closed,
which can leave background work in an inconsistent state; update http3_request
to attempt a short, bounded wait for driver_task to finish before calling
endpoint.close (e.g., await driver_task with a timeout or poll for completion
and then abort if still running), referencing the driver_task variable and the
endpoint.close call so the cleanup sequence is: request_stream[*] complete, try
to await driver_task with a small timeout, if it hasn’t finished then abort it,
then call endpoint.close to ensure orderly shutdown.
crates/rginx-http/src/proxy/clients/http3.rs (2)

130-144: 注意:完整响应体缓冲可能导致内存压力

当前实现将整个上游响应体缓冲到内存中(Line 130-138)。对于大响应,这可能导致内存压力。

对于初始 HTTP/3 支持这是可接受的,但建议在后续迭代中考虑流式传递响应体,以支持大文件代理场景。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/rginx-http/src/proxy/clients/http3.rs` around lines 130 - 144, The
code currently fully buffers the upstream HTTP/3 response body into
buffered_body by looping on request_stream.recv_data() and then calling
request_stream.recv_trailers(), which can cause OOM for large responses; change
the logic in the request handling for request_stream to stream chunks directly
instead of accumulating them: read each chunk from request_stream.recv_data()
and forward/write it to the downstream responder/response body sink as you
receive it (flushing as needed), only buffering small temporary slices if
required, and then handle trailers from request_stream.recv_trailers() after the
stream ends; use the existing request_stream, recv_data, recv_trailers, and
buffered_trailers symbols to locate and replace the buffering logic.

27-53: 考虑连接复用以提升性能

当前实现为每个请求创建新的 quinn::Endpoint(Line 32)。这在初始实现中是可接受的,但对于高吞吐量场景,复用 endpoint 和连接可以显著减少握手开销。

考虑在后续迭代中引入连接池机制,类似于 Hyper 客户端的 pool_idle_timeoutpool_max_idle_per_host 配置。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/rginx-http/src/proxy/clients/http3.rs` around lines 27 - 53, The code
currently creates a new quinn::Endpoint for every request (see
resolve_peer_socket_addr -> endpoint variable and endpoint.connect usage), which
causes repeated handshakes; refactor to reuse a shared Endpoint and/or maintain
a connection pool keyed by upstream/peer (or SocketAddr/hostname) on the client
struct instead of instantiating quinn::Endpoint per call, initialize that shared
Endpoint once (applying client_config clone) and update connect logic to borrow
from the pool or reuse existing quinn::Connection before falling back to
endpoint.connect with respect to the existing connect_timeout handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/rginx-app/src/admin_cli/status.rs`:
- Around line 21-34: The bind_addrs logic currently only checks
status.listeners.is_empty() so if listeners exist but all listener.bindings are
empty it produces an empty string; update the computation around bind_addrs in
admin_cli::status.rs to collect the formatted bindings from status.listeners
(the same iterator using listener.bindings and format!("{}://{}",
binding.transport, binding.listen_addr)), then if the collected Vec is empty
return "-".to_string() instead of joining to an empty string—ensure you
reference the bind_addrs variable, status.listeners, listener.bindings, and the
formatting step when implementing the empty-check and fallback.

In `@crates/rginx-app/tests/upstream_http3.rs`:
- Around line 323-330: The verify_client_cert implementation currently only
flips client_cert_seen and always returns Ok, which permits any presented cert
and yields false positives; modify verify_client_cert to actually validate the
end-entity and intermediates against the expected trust anchors and identity
instead of unconditional success: perform proper chain/CA verification (using
the same RootCertStore/verification routine used elsewhere in the codebase or
rustls/webpki helper calls), check the end_entity certificate subject/SAN
matches the expected identity if applicable, set client_cert_seen on success,
and return Err(rustls::Error::General(...)) on verification failure so the test
fails for wrong CA/identity. Ensure you update verify_client_cert and related
test scaffolding to use the real verifier rather than only toggling
client_cert_seen.
- Around line 289-295: 测试中的 resolve 方法将 observed_sni 总是回填为 server_name 或
default_server_name,掩盖了客户端未发送 SNI 的情况;修改 resolve(位于 impl 的 resolve(&self,
client_hello: ClientHello<'_>))使得只在 client_hello.server_name() 为 Some 时更新
self.observed_sni,保持 None 时不写入默认值(即不要用 self.default_server_name 填充
observed_sni),同时仍返回 Some(self.certified_key.clone()),以便测试能区分实际发送的 SNI 与默认值。

In `@crates/rginx-http/src/proxy/tests/client_profiles.rs`:
- Around line 182-203: The test is inflating cached client count by changing the
peer authority for the http3 Upstream instead of only changing the protocol;
modify the http3 Upstream (the variable named http3) so its UpstreamPeer uses
the same url/authority/host as the other samples (keep the same
host/port/authority values used by http1/http2 peers) and only set
UpstreamProtocol::Http3 for upstream_settings; then rebuild the snapshot via
snapshot_with_upstreams and run ProxyClients::from_config so the assertion on
clients.cached_client_count() reflects protocol-only differences.

In `@crates/rginx-runtime/src/bootstrap/listeners.rs`:
- Around line 428-430: bind_std_udp_socket currently binds the UDP socket but
does not set it to non-blocking mode; update the function so after creating the
StdUdpSocket via StdUdpSocket::bind(listen_addr) it calls set_nonblocking(true)
and returns any resulting Io error wrapped with Error::Io (mirror the pattern
used in bind_std_listener). Ensure you operate on the StdUdpSocket instance
(from bind_std_udp_socket) before returning so the socket is non-blocking for
Quinn's AsyncUdpSocket requirements.

In `@crates/rginx-runtime/src/restart.rs`:
- Around line 138-140: The InheritedSocketKind::Udp branch creates the UDP
socket with StdUdpSocket::from_raw_fd but doesn't set it non-blocking; add a
call to set_nonblocking(true) on the created StdUdpSocket before inserting it
into udp so the socket meets quinn::Endpoint non-blocking requirements (refer to
InheritedSocketKind::Udp, StdUdpSocket::from_raw_fd, set_nonblocking, and
udp.insert).

In `@scripts/test-slow.sh`:
- Around line 33-35: The find command in scripts/test-slow.sh uses the GNU-only
-printf option which breaks on BSD/macOS; replace that step with a
POSIX-compatible approach so the pipeline still yields filenames without
extensions — i.e., have find emit the file paths (no -printf) and then invoke
basename for each result (via find -exec basename ... or piping to
xargs/basename) and then run the existing sed to strip ".rs" and sort; update
the pipeline around the find invocation to use this POSIX method.

---

Nitpick comments:
In @.github/workflows/release.yml:
- Around line 123-124: 将名为 "Run Clippy" 的步骤(当前调用
./scripts/run-clippy-gate.sh)从其当前位置提前:把它放在 fast tests 之后、slow tests
之前,以便更快失败并减少无谓的慢测开销,同时使步骤顺序与 prepare-release.sh 保持一致;修改 release.yml 中该 job/step
的顺序即可,确保依赖关系(如果有)仍然满足。

In `@crates/rginx-app/tests/admin/snapshot.rs`:
- Around line 126-130: Current assertions rely on fixed ordering in
listener.bindings (using bindings[0], bindings[1]) which is brittle; update
tests to locate the target binding by its transport field instead of by index.
For example, search listener.bindings for the binding where binding.transport ==
"tcp" or "udp" (using iterator/find or equivalent) and then assert on that
binding's protocols, advertise_alt_svc, and alt_svc_max_age_secs; apply the same
change for the other assertions around the listener.bindings usage (including
the block referenced at lines ~146-155).

In `@crates/rginx-app/tests/grpc_http3.rs`:
- Around line 105-109: The test currently calls fs::remove_dir_all on
upstream_temp_dir and shared_dir after server.shutdown_and_wait and
upstream_task.await, which will not run if the test panics and thus leaks temp
directories; replace the manual dirs with tempfile::TempDir (or wrap the paths
in a custom Drop guard) so cleanup happens on Drop even if assertions fail, and
update usages that reference upstream_temp_dir and shared_dir to use
TempDir::path() (or your guard's path accessor); keep server.shutdown_and_wait
and upstream_task.await where appropriate but remove the final
fs::remove_dir_all calls since the TempDir/Drop guard will guarantee removal.

In `@crates/rginx-app/tests/http3.rs`:
- Around line 249-340: The driver task launched in http3_request is aborted and
the endpoint is immediately closed, which can leave background work in an
inconsistent state; update http3_request to attempt a short, bounded wait for
driver_task to finish before calling endpoint.close (e.g., await driver_task
with a timeout or poll for completion and then abort if still running),
referencing the driver_task variable and the endpoint.close call so the cleanup
sequence is: request_stream[*] complete, try to await driver_task with a small
timeout, if it hasn’t finished then abort it, then call endpoint.close to ensure
orderly shutdown.

In `@crates/rginx-http/src/proxy/clients/http3.rs`:
- Around line 130-144: The code currently fully buffers the upstream HTTP/3
response body into buffered_body by looping on request_stream.recv_data() and
then calling request_stream.recv_trailers(), which can cause OOM for large
responses; change the logic in the request handling for request_stream to stream
chunks directly instead of accumulating them: read each chunk from
request_stream.recv_data() and forward/write it to the downstream
responder/response body sink as you receive it (flushing as needed), only
buffering small temporary slices if required, and then handle trailers from
request_stream.recv_trailers() after the stream ends; use the existing
request_stream, recv_data, recv_trailers, and buffered_trailers symbols to
locate and replace the buffering logic.
- Around line 27-53: The code currently creates a new quinn::Endpoint for every
request (see resolve_peer_socket_addr -> endpoint variable and endpoint.connect
usage), which causes repeated handshakes; refactor to reuse a shared Endpoint
and/or maintain a connection pool keyed by upstream/peer (or
SocketAddr/hostname) on the client struct instead of instantiating
quinn::Endpoint per call, initialize that shared Endpoint once (applying
client_config clone) and update connect logic to borrow from the pool or reuse
existing quinn::Connection before falling back to endpoint.connect with respect
to the existing connect_timeout handling.

In `@crates/rginx-runtime/src/ocsp.rs`:
- Around line 429-445: spawn_http_responder currently spawns a thread with
thread::spawn and never returns its JoinHandle, leaving the responder possibly
blocked in accept() and causing flaky tests; modify spawn_http_responder to
return the thread::JoinHandle (or a wrapper that can be joined) and propagate
that handle to the caller (tests) so they can join() the thread (or use a
timeout/wakeup) at test teardown, and update callers to join the handle to
ensure the responder thread is explicitly joined/cleaned up; key symbols:
spawn_http_responder, thread::spawn, JoinHandle.

In `@scripts/nginx_compare/configs.py`:
- Around line 132-176: Both rginx_http3_return_config and
rginx_tls_return_config share nearly identical config bodies; extract the common
template into a helper (e.g., _rginx_base_return_config) that builds the shared
runtime, upstreams, locations, and servers, and accepts a parameter for the
server-specific block (the TLS/http3 server stanza). Update
rginx_http3_return_config and rginx_tls_return_config to call this helper,
passing their respective server extension (tls/http3) strings (or a small
builder function) so future changes to runtime/routes are maintained in one
place.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: afdcb378-f83d-4af1-a312-bba78584fdd3

📥 Commits

Reviewing files that changed from the base of the PR and between 9b416d3 and 508782e.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (88)
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • ARCHITECTURE_HTTP3_PHASE0_FREEZE.md
  • ARCHITECTURE_HTTP3_PLAN.md
  • Cargo.toml
  • README.md
  • clippy.toml
  • configs/conf.d/default.ron
  • configs/rginx.ron
  • crates/rginx-app/Cargo.toml
  • crates/rginx-app/src/admin_cli/status.rs
  • crates/rginx-app/src/main.rs
  • crates/rginx-app/tests/admin/commands.rs
  • crates/rginx-app/tests/admin/snapshot.rs
  • crates/rginx-app/tests/check.rs
  • crates/rginx-app/tests/downstream_mtls.rs
  • crates/rginx-app/tests/grpc_http3.rs
  • crates/rginx-app/tests/grpc_proxy.rs
  • crates/rginx-app/tests/http3.rs
  • crates/rginx-app/tests/ocsp.rs
  • crates/rginx-app/tests/reload/reload_flow.rs
  • crates/rginx-app/tests/reload/restart_flow.rs
  • crates/rginx-app/tests/upstream_http2.rs
  • crates/rginx-app/tests/upstream_http3.rs
  • crates/rginx-app/tests/upstream_mtls.rs
  • crates/rginx-app/tests/upstream_server_name.rs
  • crates/rginx-config/src/compile/server.rs
  • crates/rginx-config/src/compile/tests.rs
  • crates/rginx-config/src/compile/upstream.rs
  • crates/rginx-config/src/model.rs
  • crates/rginx-config/src/validate/server.rs
  • crates/rginx-config/src/validate/tests.rs
  • crates/rginx-config/src/validate/upstream.rs
  • crates/rginx-core/src/config.rs
  • crates/rginx-core/src/config/tests.rs
  • crates/rginx-core/src/config/upstream.rs
  • crates/rginx-core/src/lib.rs
  • crates/rginx-http/Cargo.toml
  • crates/rginx-http/src/handler/dispatch.rs
  • crates/rginx-http/src/handler/mod.rs
  • crates/rginx-http/src/handler/tests.rs
  • crates/rginx-http/src/lib.rs
  • crates/rginx-http/src/pki/certificate.rs
  • crates/rginx-http/src/proxy/clients/http3.rs
  • crates/rginx-http/src/proxy/clients/mod.rs
  • crates/rginx-http/src/proxy/clients/tests.rs
  • crates/rginx-http/src/proxy/clients/tls.rs
  • crates/rginx-http/src/proxy/common.rs
  • crates/rginx-http/src/proxy/forward/mod.rs
  • crates/rginx-http/src/proxy/forward/response.rs
  • crates/rginx-http/src/proxy/health.rs
  • crates/rginx-http/src/proxy/health/registry.rs
  • crates/rginx-http/src/proxy/mod.rs
  • crates/rginx-http/src/proxy/request_body.rs
  • crates/rginx-http/src/proxy/tests/client_profiles.rs
  • crates/rginx-http/src/proxy/tests/mod.rs
  • crates/rginx-http/src/server/graceful.rs
  • crates/rginx-http/src/server/http3.rs
  • crates/rginx-http/src/server/mod.rs
  • crates/rginx-http/src/server/tests.rs
  • crates/rginx-http/src/state/lifecycle.rs
  • crates/rginx-http/src/state/snapshots.rs
  • crates/rginx-http/src/state/tests.rs
  • crates/rginx-http/src/state/tls_runtime/bindings.rs
  • crates/rginx-http/src/state/tls_runtime/listeners.rs
  • crates/rginx-http/src/tls/acceptor.rs
  • crates/rginx-http/src/tls/certificates.rs
  • crates/rginx-http/src/tls/mod.rs
  • crates/rginx-http/src/transition.rs
  • crates/rginx-runtime/Cargo.toml
  • crates/rginx-runtime/src/admin.rs
  • crates/rginx-runtime/src/bootstrap/listeners.rs
  • crates/rginx-runtime/src/bootstrap/mod.rs
  • crates/rginx-runtime/src/bootstrap/reload.rs
  • crates/rginx-runtime/src/bootstrap/shutdown.rs
  • crates/rginx-runtime/src/health.rs
  • crates/rginx-runtime/src/ocsp.rs
  • crates/rginx-runtime/src/restart.rs
  • scripts/nginx_compare/configs.py
  • scripts/nginx_compare/render.py
  • scripts/nginx_compare/scenarios.py
  • scripts/prepare-release.sh
  • scripts/run-benchmark-matrix.py
  • scripts/run-clippy-gate.sh
  • scripts/run-soak.sh
  • scripts/run-tls-gate.sh
  • scripts/test-fast.sh
  • scripts/test-slow.sh

Comment thread crates/rginx-app/src/admin_cli/status.rs Outdated
Comment thread crates/rginx-app/tests/upstream_http3.rs
Comment thread crates/rginx-app/tests/upstream_http3.rs Outdated
Comment thread crates/rginx-http/src/proxy/tests/client_profiles.rs
Comment thread crates/rginx-runtime/src/bootstrap/listeners.rs
Comment thread crates/rginx-runtime/src/restart.rs
Comment thread scripts/test-slow.sh Outdated
@vansour
Copy link
Copy Markdown
Owner Author

vansour commented Apr 12, 2026

Addressed the actionable review items in follow-up commit d0f545f:

  • added deny.toml
  • fixed status bind address fallback when binding inventory is empty
  • set inherited and newly bound UDP sockets to nonblocking for Quinn usage
  • made scripts/test-slow.sh portable by removing GNU find -printf
  • moved release clippy ahead of slow tests
  • made HTTP/3 snapshot assertions order-independent
  • improved temp-dir cleanup and HTTP/3 driver task cleanup in tests
  • tightened upstream_http3 test scaffolding so SNI and client cert validation are real
  • normalized the HTTP/3 protocol cache test and reduced benchmark config duplication

I left the larger architectural suggestions (upstream HTTP/3 connection pooling and fully streaming upstream H3 response bodies) for a later iteration, since the current buffered-response implementation is an intentional stability tradeoff for this PR.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
crates/rginx-app/src/admin_cli/status.rs (1)

203-261: 布尔字段渲染格式存在细微不一致。

status_listenertls/http3 字段(第 144、148 行)使用 "enabled"/"disabled" 字符串,而 status_tls_listenertls/http3_enabled 字段(第 210-211 行)直接使用 .to_string() 输出 "true"/"false"

虽然字段名称不同(带 _enabled 后缀),但对于解析状态输出的消费者可能产生混淆。建议统一布尔字段的输出格式,或在文档中明确说明差异。

♻️ 可选:统一使用 "enabled"/"disabled" 格式
                         ("listener_id", listener.listener_id.clone()),
                         ("listen", listener.listen_addr.to_string()),
-                        ("tls", listener.tls_enabled.to_string()),
-                        ("http3_enabled", listener.http3_enabled.to_string()),
+                        ("tls", if listener.tls_enabled { "enabled" } else { "disabled" }.to_string()),
+                        ("http3_enabled", if listener.http3_enabled { "enabled" } else { "disabled" }.to_string()),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/rginx-app/src/admin_cli/status.rs` around lines 203 - 261, The boolean
rendering for TLS/HTTP3 is inconsistent between the two print blocks: in
status_listener the tls/http3 fields emit "enabled"/"disabled" but in the
status_tls_listener loop the fields ("tls" and "http3_enabled") call
.to_string() producing "true"/"false"; update the status_tls_listener branch
(inside the for listener in &status.tls.listeners loop passed to print_record)
to use the same "enabled"/"disabled" formatting as status_listener (extract or
reuse the existing helper logic—or introduce a small helper like
render_enabled_disabled(bool) and call it for listener.tls_enabled and
listener.http3_enabled) so all boolean fields are rendered consistently. Ensure
you update both "tls" and "http3_enabled" entries accordingly and keep other
fields (render_optional_string_list, client_auth_* etc.) unchanged.
crates/rginx-runtime/src/bootstrap/listeners.rs (1)

319-335: 建议增加防御性断言以明确 HTTP/3 配置与 UDP socket 的对应关系。

当前逻辑:如果 std_udp_socketSome,使用它创建 endpoint;否则回退到 bind_http3_endpoint

根据上游调用点 (build_initial_listener_groupsprepare_added_listener_bindings),当 listener.http3 配置存在时,std_udp_socket 必定为 Some。但当前代码结构允许 std_udp_socketNonelistener.http3Some 的情况,此时 bind_http3_endpoint 可能会创建一个不被 std_udp_socket 追踪的 socket,导致 restart 时无法继承该 UDP socket。

♻️ 可选:增加防御性断言
     let http3_endpoint = match &std_udp_socket {
         Some(socket) => Some(rginx_http::server::bind_http3_endpoint_with_socket(
             &listener,
             default_vhost,
             vhosts,
             socket.try_clone()?,
         )?),
-        None => rginx_http::server::bind_http3_endpoint(&listener, default_vhost, vhosts)?,
+        None => {
+            debug_assert!(
+                listener.http3.is_none(),
+                "http3 config present but std_udp_socket is None; restart inheritance may be broken"
+            );
+            rginx_http::server::bind_http3_endpoint(&listener, default_vhost, vhosts)?
+        }
     };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/rginx-runtime/src/bootstrap/listeners.rs` around lines 319 - 335, The
code may create an HTTP/3 endpoint without a tracked UDP socket which breaks
restart socket inheritance; add a defensive check before calling
bind_http3_endpoint or constructing PreparedListenerWorkerGroup to assert that
if listener.http3 is Some then std_udp_socket.is_some(), and otherwise return a
clear error (or propagate an Err) instead of silently calling
bind_http3_endpoint; update the branch around bind_http3_endpoint_with_socket /
bind_http3_endpoint (and the PreparedListenerWorkerGroup construction) to
enforce this invariant so callers like build_initial_listener_groups and
prepare_added_listener_bindings cannot end up with an untracked UDP socket.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/rginx-app/src/admin_cli/status.rs`:
- Around line 203-261: The boolean rendering for TLS/HTTP3 is inconsistent
between the two print blocks: in status_listener the tls/http3 fields emit
"enabled"/"disabled" but in the status_tls_listener loop the fields ("tls" and
"http3_enabled") call .to_string() producing "true"/"false"; update the
status_tls_listener branch (inside the for listener in &status.tls.listeners
loop passed to print_record) to use the same "enabled"/"disabled" formatting as
status_listener (extract or reuse the existing helper logic—or introduce a small
helper like render_enabled_disabled(bool) and call it for listener.tls_enabled
and listener.http3_enabled) so all boolean fields are rendered consistently.
Ensure you update both "tls" and "http3_enabled" entries accordingly and keep
other fields (render_optional_string_list, client_auth_* etc.) unchanged.

In `@crates/rginx-runtime/src/bootstrap/listeners.rs`:
- Around line 319-335: The code may create an HTTP/3 endpoint without a tracked
UDP socket which breaks restart socket inheritance; add a defensive check before
calling bind_http3_endpoint or constructing PreparedListenerWorkerGroup to
assert that if listener.http3 is Some then std_udp_socket.is_some(), and
otherwise return a clear error (or propagate an Err) instead of silently calling
bind_http3_endpoint; update the branch around bind_http3_endpoint_with_socket /
bind_http3_endpoint (and the PreparedListenerWorkerGroup construction) to
enforce this invariant so callers like build_initial_listener_groups and
prepare_added_listener_bindings cannot end up with an untracked UDP socket.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: bcef60e8-cf9f-439e-85ce-f350025dc677

📥 Commits

Reviewing files that changed from the base of the PR and between e859a69 and 9f15de7.

📒 Files selected for processing (17)
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • crates/rginx-app/src/admin_cli/status.rs
  • crates/rginx-app/tests/admin/snapshot.rs
  • crates/rginx-app/tests/grpc_http3.rs
  • crates/rginx-app/tests/http3.rs
  • crates/rginx-app/tests/reload/reload_flow.rs
  • crates/rginx-app/tests/reload/restart_flow.rs
  • crates/rginx-app/tests/upstream_http3.rs
  • crates/rginx-http/src/proxy/tests/client_profiles.rs
  • crates/rginx-http/src/server/http3.rs
  • crates/rginx-runtime/src/bootstrap/listeners.rs
  • crates/rginx-runtime/src/ocsp.rs
  • crates/rginx-runtime/src/restart.rs
  • deny.toml
  • scripts/nginx_compare/configs.py
  • scripts/test-slow.sh
✅ Files skipped from review due to trivial changes (4)
  • deny.toml
  • crates/rginx-app/tests/grpc_http3.rs
  • crates/rginx-app/tests/http3.rs
  • crates/rginx-http/src/server/http3.rs
🚧 Files skipped from review as they are similar to previous changes (8)
  • .github/workflows/release.yml
  • crates/rginx-http/src/proxy/tests/client_profiles.rs
  • scripts/test-slow.sh
  • crates/rginx-runtime/src/ocsp.rs
  • crates/rginx-app/tests/reload/restart_flow.rs
  • scripts/nginx_compare/configs.py
  • crates/rginx-app/tests/upstream_http3.rs
  • crates/rginx-app/tests/reload/reload_flow.rs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants