Skip to content

http3: implement phase 7 early-data, retry, and telemetry (review replay of #41)#43

Closed
vansour wants to merge 8 commits intoreview-pr-41-basefrom
review-pr-41-head
Closed

http3: implement phase 7 early-data, retry, and telemetry (review replay of #41)#43
vansour wants to merge 8 commits intoreview-pr-41-basefrom
review-pr-41-head

Conversation

@vansour
Copy link
Copy Markdown
Owner

@vansour vansour commented Apr 13, 2026

This is a temporary review-only PR that reproduces the original diff from PR #41 against the pre-merge base commit.

Reason:

  • PR release: prepare v0.1.3-rc.11 #41 was already merged, so GitHub cannot reopen it.
  • CodeRabbit reported its review was interrupted because the PR was closed or merged during review.

Intent:

  • Let CodeRabbit complete a review on an open PR with essentially the same changeset.
  • No new code changes are introduced here beyond the original release branch.

Original PR: #41

Copilot AI review requested due to automatic review settings April 13, 2026 08:40
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

Warning

Rate limit exceeded

@vansour has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 49 minutes and 33 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 49 minutes and 33 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro

Run ID: 356009c0-6ebb-47a1-8241-3b479bdce108

📥 Commits

Reviewing files that changed from the base of the PR and between 86a567d and e69c198.

📒 Files selected for processing (5)
  • crates/rginx-config/src/compile/server.rs
  • crates/rginx-config/src/validate/server.rs
  • crates/rginx-http/src/proxy/clients/http3.rs
  • crates/rginx-http/src/server/http3.rs
  • crates/rginx-runtime/src/bootstrap/listeners.rs

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

此 PR 大规模引入并启用 HTTP/3 功能:配置与验证扩展、QUIC/HTTP3 会话复用与流式响应、0‑RTT/early‑data 策略与计数、多个 UDP socket/accept 工作器、运行时快照/遥测扩展、测试与浸泡/发布门控脚本,以及文档和版本更新。

Changes

Cohort / File(s) Summary
Docs & Release
README.md, RELEASE_NOTES_v0.1.3-rc.11.md, ARCHITECTURE_HTTP3_PLAN.md (deleted), ARCHITECTURE_HTTP3_PHASE0_FREEZE.md (deleted), ARCHITECTURE_REMEDIATION_PLAN.md (deleted), ARCHITECTURE_REMEDIATION_RELEASE_NOTE.md (deleted), Cargo.toml
版本更新为 v0.1.3-rc.11,移除若干旧架构文档,新增/更新发布说明与 README 中的 HTTP/3 指南与示例命令。
CI & Release Scripts
.github/workflows/release.yml, scripts/prepare-release.sh, scripts/run-http3-gate.sh, scripts/run-http3-release-gate.sh, scripts/run-http3-soak.sh, scripts/run-http3-phase0.sh
在 release verify job 中新增 HTTP/3 release‑gate 步骤;新增 HTTP/3 回归/浸泡/发布门控脚本,并在 prepare-release 中调用。
配置模型与编译/验证
crates/rginx-config/src/model.rs, crates/rginx-config/src/compile/..., crates/rginx-config/src/validate/..., crates/rginx-config/src/compile/tests.rs, crates/rginx-config/src/validate/tests.rs
为 Http3Config 与 LocationConfig 添加早期数据、并发流、缓冲、active CID、retry、host_key_path、GSO 等字段;编译路径解析、基于 base_dir 的 host_key_path 解析与边界/兼容性校验;新增多个验证单元测试。
核心配置与路由
crates/rginx-core/src/config.rs, crates/rginx-core/src/config/route.rs, crates/rginx-core/src/config/tests.rs
ListenerHttp3、ListenerTransportBinding 与 Route 增加 HTTP/3 参数与 allow_early_data 字段,并在绑定构建与测试中传递/断言这些值。
HTTP/3 接入与连接处理
crates/rginx-http/src/server/http3.rs, crates/rginx-http/src/server/connection.rs, crates/rginx-http/src/tls/acceptor.rs
实现 QUIC host_key 管理、active CID 策略、HTTP/3 retry、0‑RTT/early‑data 标记与处理、请求/响应/关闭错误计数;TLS accept 构建扩展以支持 early‑data 参数。
HTTP/3 代理客户端
crates/rginx-http/src/proxy/clients/http3.rs
引入 Endpoint/Session 缓存与等待逻辑以支持会话复用,改为响应流式转发(背景读取任务 + mpsc),更新错误与生命周期处理并增加测试。
请求分发与响应
crates/rginx-http/src/handler/mod.rs, .../dispatch.rs, .../response.rs
新增请求扩展类型 EarlyDataFlag;在路由分发中根据 route.allow_early_data 接受或拒绝 early data(记录计数并返回 425 / grpc Unavailable);新增 too_early_response
运行时状态、计数与快照
crates/rginx-http/src/state/{counters,connections,helpers,lifecycle,snapshots,tests}.rs
新增大量 HTTP/3 原子计数器(活跃连接/流、retry、各种错误、early‑data 接受/拒绝、连接关闭细分等);新增 Guard 类型自动管理活跃计数并触发快照版本;扩展 snapshot schema 与聚合字段(schema 版本上升)。
Admin CLI、应用输出与测试支持
crates/rginx-app/src/admin_cli/{status,counters,traffic}.rs, crates/rginx-app/src/main.rs, crates/rginx-app/tests/*, crates/rginx-app/tests/support/mod.rs
Admin 输出(status/counters/traffic/check)增加 HTTP/3 配置与运行时计数;测试扩展验证新字段;测试支持改为增量读取 HTTP 响应(支持 Content-Length、chunked、流式)。
运行时引导、重启与监听绑定
crates/rginx-runtime/src/bootstrap/listeners.rs, crates/rginx-runtime/src/restart.rs, crates/rginx-runtime/src/bootstrap/shutdown.rs
从单 UDP socket 改为按 accept_workers 绑定多个 UDP sockets,使用 socket2 可选启用 SO_REUSEPORT;为每个 socket 创建 quinn endpoint 并为每个 endpoint spawn accept worker;继承/重启逻辑改为支持多 UDP FDs。
依赖与工作区
crates/rginx-http/Cargo.toml, crates/rginx-runtime/Cargo.toml, crates/rginx-app/Cargo.toml
新增/调整依赖:aws-lc-rsquinn-proto(rginx-http),socket2(rginx-runtime),并在 rginx-app 中添加本地 path 依赖 rginx-core

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Server as HTTP/3 Server
    participant State as SharedState
    participant Handler as RouteHandler
    participant Upstream

    Client->>Server: QUIC Initial (含 0‑RTT)
    Note over Server: attach EarlyDataFlag(true)
    Server->>State: record retry/early-data 事件
    Server->>Handler: dispatch(request with EarlyDataFlag)
    alt early_data && !route.allow_early_data
        Handler->>State: record_http3_early_data_rejected_request
        Handler-->>Client: 425 Too Early / grpc Unavailable
    else early_data && route.allow_early_data
        Handler->>State: record_http3_early_data_accepted_request
        Handler->>Upstream: forward request
        Upstream-->>Handler: response
        Handler-->>Client: 流式返回 response frames
    end
    Note over State: 快照版本递增并 notify waiters
Loading
sequenceDiagram
    participant Bootstrap as RuntimeBootstrap
    participant Socket as UDP Socket Layer
    participant Endpoint as QuinnEndpoint
    participant Worker as AcceptWorker

    Bootstrap->>Bootstrap: 计算 accept_workers N
    Bootstrap->>Socket: bind_std_udp_sockets(addr, N)
    loop 为每个 socket
        Socket->>Socket: 创建 StdUdpSocket (可能启用 SO_REUSEPORT)
        Socket-->>Bootstrap: 返回 socket 列表
    end
    Bootstrap->>Endpoint: 为每个 socket 创建 quinn::Endpoint
    Bootstrap-->>Worker: spawn accept worker per endpoint
    Worker->>Endpoint: 接收 QUIC 包并处理 retry/0‑RTT/handshake
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

🐰 我是小兔子,网络上轻轻跳,

0‑RTT 风来时,我先行又不扰,
会话复用像胡萝卜串,流式回包暖又牢,
计数与快照记录着每次小跳跃,
Release gate 守护,放心去跑跳!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.02% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The description is directly related to the changeset, explaining why this PR exists and its purpose as a temporary review-only reproduction.
Title check ✅ Passed The title accurately describes the primary focus of the changeset: implementing HTTP/3 Phase 7 features including early-data support, retry mechanisms, and comprehensive telemetry collection across multiple modules.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch review-pr-41-head

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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 PR reproduces the original changeset from PR #41 (release prep for v0.1.3-rc.11) so automated review tooling can complete review against the pre-merge base commit. The changes primarily expand HTTP/3 release gating assets, QUIC/HTTP/3 runtime telemetry, and early-data (0-RTT) replay-safety controls across config, runtime, CLI, and tests.

Changes:

  • Add HTTP/3 release-gate scripts (gate, soak, Phase 0 baseline, release wrapper) and wire them into release preflight + GitHub Release workflow.
  • Extend HTTP/3 runtime model: per-worker UDP sockets (reuse-port), QUIC runtime telemetry, retry support, and 0-RTT early-data policy with route-level replay safety.
  • Update control-plane snapshots/status/check outputs and integration/unit tests; bump snapshot schema version to 13 and workspace version to 0.1.3-rc.11.

Reviewed changes

Copilot reviewed 56 out of 57 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
scripts/run-http3-soak.sh New focused HTTP/3 soak runner with optional netem/MTU shaping.
scripts/run-http3-release-gate.sh New wrapper to run HTTP/3 regression gate + soak + optional nginx compare.
scripts/run-http3-phase0.sh New Phase 0 baseline runner for HTTP/3 regression/soak subset.
scripts/run-http3-gate.sh New dedicated HTTP/3 regression gate script.
scripts/prepare-release.sh Run HTTP/3 release gate during release preflight.
RELEASE_NOTES_v0.1.3-rc.11.md New release notes for v0.1.3-rc.11 focusing on HTTP/3 maturity.
README.md Update displayed current version and HTTP/3 plan/docs pointers + new gate commands.
crates/rginx-runtime/src/restart.rs Preserve multiple UDP sockets across restart for HTTP/3 listeners.
crates/rginx-runtime/src/bootstrap/shutdown.rs Update test fixtures for new allow_early_data route field.
crates/rginx-runtime/src/bootstrap/listeners.rs Bind one UDP socket per accept worker (reuse-port) and start multiple HTTP/3 accept workers.
crates/rginx-runtime/src/admin.rs Bump admin snapshot schema version to 13.
crates/rginx-runtime/Cargo.toml Add socket2 dependency for UDP socket options/binding.
crates/rginx-http/src/transition.rs Update transition tests for expanded HTTP/3 listener config fields.
crates/rginx-http/src/tls/acceptor.rs Allow HTTP/3 server TLS config to enable early-data via max_early_data_size.
crates/rginx-http/src/state/traffic.rs Add HTTP/3 runtime telemetry into traffic snapshots.
crates/rginx-http/src/state/tls_runtime/listeners.rs Expose more HTTP/3 listener transport settings in TLS status snapshots.
crates/rginx-http/src/state/tests.rs Extend status/traffic snapshot tests for HTTP/3 telemetry and settings.
crates/rginx-http/src/state/snapshots.rs Add snapshot fields for HTTP/3 runtime telemetry and early-data counters/settings.
crates/rginx-http/src/state/lifecycle.rs Aggregate HTTP/3 runtime counters into top-level runtime status snapshot.
crates/rginx-http/src/state/helpers.rs Add helper to build per-listener HTTP/3 runtime telemetry snapshots.
crates/rginx-http/src/state/counters.rs Add HTTP/3 runtime/early-data counters (listener + global).
crates/rginx-http/src/state/connections.rs Add guards + APIs to track active HTTP/3 connections/streams and record telemetry events.
crates/rginx-http/src/server/http3.rs Implement HTTP/3 retry, runtime telemetry hooks, 0-RTT early-data propagation, and host-key based token/reset keys.
crates/rginx-http/src/server/connection.rs Track early_data in connection metadata and export TLS handshake failure classifier.
crates/rginx-http/src/router.rs Update tests to include new allow_early_data field on routes.
crates/rginx-http/src/proxy/clients/http3.rs Rework upstream HTTP/3 client to reuse endpoints/sessions and stream response bodies.
crates/rginx-http/src/handler/tests.rs Update handler tests for new route field and connection metadata.
crates/rginx-http/src/handler/response.rs Add a 425 Too Early convenience response builder.
crates/rginx-http/src/handler/mod.rs Attach early-data info into request extensions for routing/policy.
crates/rginx-http/src/handler/dispatch.rs Enforce route-level early-data replay safety and record accept/reject counters.
crates/rginx-http/src/client_ip.rs Extend connection metadata to include early-data flag.
crates/rginx-http/Cargo.toml Add aws-lc-rs and quinn-proto for QUIC host-key derived token/reset/cid behavior.
crates/rginx-core/src/config/tests.rs Update config tests for expanded HTTP/3 binding fields and new route field.
crates/rginx-core/src/config/route.rs Add allow_early_data to compiled route model.
crates/rginx-core/src/config.rs Extend ListenerHttp3 and transport bindings with QUIC/HTTP3 tuning + early-data fields.
crates/rginx-config/src/validate/tests.rs Add validation coverage for HTTP/3 transport settings and early-data constraints.
crates/rginx-config/src/validate/server.rs Validate HTTP/3 tuning fields, host_key_path, retry prerequisites, and early-data TLS requirements.
crates/rginx-config/src/model.rs Extend config model for HTTP/3 tuning + route allow_early_data.
crates/rginx-config/src/compile/tests.rs Update compile tests to include new route field defaults.
crates/rginx-config/src/compile/server.rs Compile HTTP/3 tuning fields, host_key_path resolution, and defaults.
crates/rginx-config/src/compile/route.rs Compile allow_early_data into the route model.
crates/rginx-app/tests/support/mod.rs Improve HTTP response parsing helper to handle content-length and chunked responses robustly.
crates/rginx-app/tests/http3.rs Add extensive integration tests for HTTP/3 telemetry, retry host-key, mTLS, and early-data replay safety.
crates/rginx-app/tests/check.rs Expand check output expectations for HTTP/3 binding/early-data fields and accept workers.
crates/rginx-app/tests/admin/snapshot.rs Update snapshot tests for schema 13 and new HTTP/3 fields/telemetry.
crates/rginx-app/tests/admin/commands.rs Update admin CLI tests for new HTTP/3 runtime and early-data counters/fields.
crates/rginx-app/src/main.rs Extend check summary/output with HTTP/3 binding settings and early-data-enabled listener count.
crates/rginx-app/src/admin_cli/traffic.rs Print per-listener HTTP/3 runtime telemetry in traffic output.
crates/rginx-app/src/admin_cli/status.rs Print aggregated + per-listener HTTP/3 runtime telemetry and binding settings in status output.
crates/rginx-app/src/admin_cli/counters.rs Print global HTTP/3 early-data accept/reject counters.
Cargo.toml Bump workspace version to 0.1.3-rc.11.
Cargo.lock Lockfile updates for new deps (aws-lc-rs, quinn-proto, socket2 v0.5.10, etc.).
ARCHITECTURE_REMEDIATION_RELEASE_NOTE.md Remove obsolete remediation release note doc.
ARCHITECTURE_REMEDIATION_PLAN.md Remove obsolete remediation plan doc.
ARCHITECTURE_HTTP3_PLAN.md Remove superseded HTTP/3 plan doc (replaced by nginx-alignment docs referenced elsewhere).
ARCHITECTURE_HTTP3_PHASE0_FREEZE.md Remove superseded Phase 0 freeze doc (replaced by newer phase docs).
.github/workflows/release.yml Run HTTP/3 release gate during release verification job.

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

Comment thread scripts/run-http3-soak.sh
Comment on lines +98 to +101
if [[ "${need_privileged}" -eq 1 ]]; then
have tc || die "tc is required when --netem-profile is used"
have ip || die "ip is required when --mtu is used"
[[ "${EUID}" -eq 0 ]] || die "root privileges are required for netem/mtu operations"
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

tc is required unconditionally whenever need_privileged=1, which includes the --mtu-only case. As written, ./scripts/run-http3-soak.sh --mtu ... will fail with “tc is required…” even though netem isn’t being used. Consider checking tc only when NETEM_PROFILE != none, and checking ip only when MTU is set (keeping the root check gated the same way).

Copilot uses AI. Check for mistakes.
Comment on lines +431 to +437
fn bind_std_udp_sockets(
listen_addr: std::net::SocketAddr,
count: usize,
) -> Result<Vec<Arc<StdUdpSocket>>> {
let count = count.max(1);
(0..count).map(|_| bind_std_udp_socket(listen_addr, count > 1).map(Arc::new)).collect()
}
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

bind_std_udp_sockets will attempt to bind count UDP sockets to the same listen_addr. Outside Linux, the SO_REUSEPORT setsockopt is not applied (it’s behind #[cfg(target_os = "linux")]), so using accept_workers > 1 with HTTP/3 is likely to fail with EADDRINUSE on other Unix platforms. Consider either enabling the equivalent reuse-port option for other supported OSes (via socket2 APIs / per-OS cfg) or forcing count=1 (and/or returning a config error) when reuse-port can’t be enabled.

Copilot uses AI. Check for mistakes.
Comment on lines +325 to +327
fn is_end_stream(&self) -> bool {
self.rx.is_closed()
}
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

StreamingResponseBody::is_end_stream returns rx.is_closed(), which becomes true as soon as the sender is dropped even if the receiver still has queued frames to yield. That violates the hyper::body::Body contract and can cause truncated upstream HTTP/3 responses. Consider tracking completion explicitly (e.g., set a done flag when poll_frame returns None, or use rx.is_closed() && rx.is_empty() if available) so is_end_stream only returns true once all frames have been drained.

Copilot uses AI. Check for mistakes.
Comment on lines 44 to 51
pub(crate) fn attach_connection_metadata<B>(
request: &mut Request<B>,
connection: &ConnectionPeerAddrs,
) {
request.extensions_mut().insert(connection.early_data);
if let Some(identity) = connection.tls_client_identity.clone() {
request.extensions_mut().insert(identity);
}
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

Storing early_data as a bare bool in Request extensions is fragile because extensions are keyed only by type; any future feature that also inserts a bool would collide and could cause incorrect early-data routing decisions. Consider introducing a dedicated newtype (e.g., struct EarlyData(pub bool)) and using that for both insertion and lookup.

Copilot uses AI. Check for mistakes.
@vansour
Copy link
Copy Markdown
Owner Author

vansour commented Apr 13, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 6

🧹 Nitpick comments (11)
crates/rginx-http/src/proxy/clients/http3.rs (1)

414-418: 避免靠错误字符串识别 H3_NO_ERROR

这里把“干净关闭”的判断绑定到了 Display 文本,升级 h3/quinn 后很容易失效。更稳妥的做法是直接匹配结构化错误码/枚举,否则一次普通依赖升级就可能把正常关闭误判成流错误。

🤖 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 414 - 418, The
current is_clean_http3_response_shutdown function relies on Display text
matching which is brittle; change it to inspect concrete error types/enums
instead: update the function signature (is_clean_http3_response_shutdown) to
accept &dyn std::error::Error (or the concrete error type used in call sites),
attempt downcasting (error.downcast_ref::<h3::Error>() or
downcast_ref::<quinn::ConnectionError>() / quinn::TransportError>() as
appropriate) and match the structured variants (e.g. h3::Error::ApplicationClose
or Error::Application { code } and compare against h3::ErrorCode::H3_NO_ERROR or
the quinn close-code enum) to return true for clean shutdowns; keep a Display
fallback only as a last resort and update all call sites to pass the error as an
error trait object or concrete type.
crates/rginx-http/src/handler/mod.rs (1)

44-50: 不要把 0-RTT 标记直接存成裸 bool 扩展。

http::Extensions 是按具体类型做 key 的,直接插入 bool 只有一个槽位。后面只要别的中间件也往扩展里放 bool,这里的 early_data 就会被覆盖或读错。更稳妥的做法是定义一个专用 newtype。

♻️ 建议改法
+#[derive(Clone, Copy, Debug)]
+pub(crate) struct EarlyData(pub bool);
+
 pub(crate) fn attach_connection_metadata<B>(
     request: &mut Request<B>,
     connection: &ConnectionPeerAddrs,
 ) {
-    request.extensions_mut().insert(connection.early_data);
+    request.extensions_mut().insert(EarlyData(connection.early_data));
     if let Some(identity) = connection.tls_client_identity.clone() {
         request.extensions_mut().insert(identity);
     }
 }

对应读取侧再改成 get::<EarlyData>().map(|v| v.0).unwrap_or(false)

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

In `@crates/rginx-http/src/handler/mod.rs` around lines 44 - 50, 当前实现把 0-RTT
的标记直接以裸 bool 插入 Request 的 extensions(在 attach_connection_metadata 中使用
connection.early_data),这会与其它 bool 扩展冲突;请定义一个专用 newtype(例如 EarlyData(bool))并在
attach_connection_metadata 用
request.extensions_mut().insert(EarlyData(connection.early_data)) 插入;同时更新读取方改为
request.extensions().get::<EarlyData>().map(|v| v.0).unwrap_or(false);保持对
tls_client_identity 的处理不变(仍在 attach_connection_metadata 中插入其具体类型)。
crates/rginx-http/src/state/connections.rs (1)

314-416: 把重复的 HTTP/3 计数+快照更新逻辑抽成一个 helper。

这些方法除了具体递增的字段不同外,mark_snapshot_changed_componentsmark_traffic_targets_changednotify_snapshot_waiters 的流程几乎完全一致。现在这块已经有不少重复,后面只要改一次版本标记或通知顺序,就很容易漏掉某个分支。

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

In `@crates/rginx-http/src/state/connections.rs` around lines 314 - 416, All these
record_http3_* methods repeat the same snapshot/notification sequence; extract a
helper like bump_http3_counter_and_snapshot(listener_id: &str, op: impl
FnOnce(&ListenerTrafficCounters)) that (1) looks up
listener_traffic_counters(listener_id), (2) runs the provided closure to
increment the specific atomic counter, and (3) calls
mark_snapshot_changed_components(true, false, true, false, false),
mark_traffic_targets_changed(..., Some(listener_id), None, None) and
notify_snapshot_waiters(); then refactor record_http3_retry_issued,
record_http3_retry_failed, record_http3_request_* and
record_http3_response_stream_error to call this helper with a small closure that
increments the right field, and refactor record_http3_connection_close to either
call the same helper inside each match arm or use the helper with a closure that
matches on the quinn::ConnectionError and increments the appropriate counter.
crates/rginx-app/src/admin_cli/traffic.rs (1)

130-189: 建议将 traffic_listener_http3 输出块提取为独立 helper。

当前字段列表很长,后续新增/改名指标时更容易与其他输出路径出现维护漂移。提取函数可降低认知负担。

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

In `@crates/rginx-app/src/admin_cli/traffic.rs` around lines 130 - 189, Extract
the long traffic_listener_http3 print logic into a helper function (e.g.,
print_listener_http3 or print_http3_metrics) that takes the listener id and a
reference to listener.http3_runtime and calls print_record with the same metric
keys/values; replace the inline block in the original code with a single call to
that helper. Ensure the helper lives near other CLI printing utilities, accepts
(&str or String) listener_id and the http3 runtime struct (match its type),
converts the fields to strings as currently done, and preserves all metric names
exactly so other output paths remain consistent.
crates/rginx-core/src/config/tests.rs (1)

148-177: 建议补充 early_data_enabled 的 binding 断言覆盖。

当前在 Line 154 已设置 early_data_enabled: false,但此测试里没有看到对应映射结果的显式校验(若 binding 暴露该字段,建议断言它)。

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

In `@crates/rginx-core/src/config/tests.rs` around lines 148 - 177, The test sets
early_data_enabled: false on the UDP listener but doesn't assert the mapped
binding exposes it; add an assertion that the binding produced by
listener.transport_bindings() reflects this flag (e.g. check bindings[1] for the
http3 early-data field — something like bindings[1].http3_early_data_enabled ==
Some(false) or the appropriate field name used by the binding struct) to ensure
early_data_enabled is covered by the test.
scripts/run-http3-release-gate.sh (1)

88-92: 建议对 --netem-profile 做枚举校验并提前失败。

当前仅校验了 soak-iterationsnetem-profile 与帮助文档声明的允许值不一致时,建议在本脚本直接报错。

建议修改
 [[ "${SOAK_ITERATIONS}" =~ ^[0-9]+$ ]] || die "--soak-iterations must be a positive integer"
 [[ "${SOAK_ITERATIONS}" -ge 1 ]] || die "--soak-iterations must be >= 1"
+case "${NETEM_PROFILE}" in
+    none|loss|reorder|jitter) ;;
+    *) die "--netem-profile must be one of: none|loss|reorder|jitter" ;;
+esac

Also applies to: 123-124

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

In `@scripts/run-http3-release-gate.sh` around lines 88 - 92, The --netem-profile
parsing currently only assigns NETEM_PROFILE without validating allowed values;
add an explicit enum check after parsing NETEM_PROFILE (e.g., compare
NETEM_PROFILE against the documented allowed values) and call die with a clear
error if it is not one of them. Update the parsing branch that sets
NETEM_PROFILE (and the other occurrence around the same code) to perform this
validation immediately after shift, using the existing die helper for failure
messages and referencing NETEM_PROFILE in the error so users see the invalid
value and the allowed set.
crates/rginx-http/src/handler/dispatch.rs (1)

61-61: 建议用专用类型包装 bool extension,而非直接使用裸 bool

当前仅 early_data 使用 bool 作为 extension 键,暂无实际冲突。但建议创建专用 newtype(如 struct EarlyDataFlag(bool))作为 extension 键,遵循类型安全最佳实践,防止未来其他代码误插入 bool 类型导致隐蔽的键冲突。

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

In `@crates/rginx-http/src/handler/dispatch.rs` at line 61, Create a small newtype
wrapper for the extension instead of using a raw bool: define a simple struct
EarlyDataFlag(pub bool) (derive Copy, Clone, Debug) and update any places that
insert the extension to use EarlyDataFlag(true/false). Then change the retrieval
in dispatch (where early_data is set from request.extensions()) to use
request.extensions().get::<EarlyDataFlag>().map(|f| f.0).unwrap_or(false)
(replacing get::<bool>().copied().unwrap_or(false)) so the extension key is
type-safe and avoids collisions.
crates/rginx-app/src/main.rs (2)

48-74: 提取 CheckSummary 构建逻辑。

check-t 分支现在维护两份几乎相同的 CheckSummary 初始化。本次新增的 http3_early_data_enabled_listeners 已经复制了一次,后面继续扩字段时很容易只改到一个分支。

Also applies to: 82-108

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

In `@crates/rginx-app/src/main.rs` around lines 48 - 74, Extract the duplicated
CheckSummary construction into a single helper function (e.g., fn
build_check_summary(config: &Config) -> CheckSummary) and call it from both the
check and -t branches instead of duplicating the struct literal; move the
http3_early_data_enabled_listeners iterator logic (the .iter().filter(|listener|
listener.http3.as_ref().is_some_and(|http3| http3.early_data_enabled)).count())
and all other fields (listener_model, listener_count, listener_binding_count,
listeners, tls_enabled, http3_enabled, total_vhost_count, total_route_count,
upstream_count, worker_threads, accept_workers, tls) into that helper so future
fields are changed in one place and both branches simply call
build_check_summary(&config) when invoking print_check_success.

576-578: 这里直接比较枚举会更稳。

binding.kind.as_str() == "udp" 把逻辑耦合到了字符串表示;同一个 PR 里的 crates/rginx-http/src/state/lifecycle.rs 已经直接比较 ListenerTransportKind::Udp 了,建议统一成枚举比较。

可参考的修改
-                    reuse_port_enabled: (binding.kind.as_str() == "udp")
-                        .then_some(config.runtime.accept_workers > 1),
+                    reuse_port_enabled: (binding.kind
+                        == rginx_core::ListenerTransportKind::Udp)
+                        .then_some(config.runtime.accept_workers > 1),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/rginx-app/src/main.rs` around lines 576 - 578, The code compares
binding.kind via its string form; replace the string comparison with an enum
comparison (e.g., use matches!(binding.kind, ListenerTransportKind::Udp) or
direct equality against ListenerTransportKind::Udp) when setting
reuse_port_enabled so it’s consistent with other code (see
ListenerTransportKind::Udp) and still combine with config.runtime.accept_workers
> 1 for the .then_some(...) logic; update the expression that computes
reuse_port_enabled (which references binding.kind and
config.runtime.accept_workers) to use the enum-based check.
crates/rginx-config/src/compile/tests.rs (1)

2248-2258: 补上 early_data 默认值断言。

这个用例已经把本次新增的大部分 HTTP/3 默认值都钉住了,但 early_data 还没覆盖。若默认值以后意外变成开启,这组测试不会报错。

可参考的补充断言
     assert_eq!(http3.max_concurrent_streams, 128);
     assert_eq!(http3.stream_buffer_size, 64 * 1024);
     assert_eq!(http3.active_connection_id_limit, 2);
     assert!(!http3.retry);
     assert_eq!(http3.host_key_path, None);
     assert!(!http3.gso);
+    assert!(!http3.early_data_enabled);
 }

Also applies to: 2286-2292

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

In `@crates/rginx-config/src/compile/tests.rs` around lines 2248 - 2258, The
test's expected Http3Config currently lists early_data but does not assert the
runtime default; update the test in crates/rginx-config/src/compile/tests.rs
(both occurrences around the shown diff and the one at 2286-2292) to explicitly
assert that Http3Config::early_data is None (i.e., the default is disabled) by
adding a check against None for the early_data field when validating the
compiled config so the test will fail if the default flips to enabled.
crates/rginx-http/src/state/lifecycle.rs (1)

28-79: 把 HTTP/3 汇总计数改成单次遍历。

这里对同一组 listener/counter 做了多次扫描。status_snapshot() 属于控制面热路径,改成一次 fold/accumulate 会更省锁持有时间,也能减少后续再加字段时漏改某一处的风险。

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

In `@crates/rginx-http/src/state/lifecycle.rs` around lines 28 - 79, Multiple
separate iterations over config.listeners to collect HTTP/3 counters should be
replaced with a single loop/fold to avoid repeated lookups and reduce lock/CPU
overhead in status_snapshot(); implement by iterating once over config.listeners
(or using an iterator.fold) and for each listener do
listener_traffic.listeners.get(&listener.id).map(|entry| { increment local
accumulators: http3_active_connections, http3_active_request_streams,
http3_retry_issued_total, http3_retry_failed_total,
http3_request_accept_errors_total, http3_request_resolve_errors_total,
http3_request_body_stream_errors_total, http3_response_stream_errors_total using
the same .load(...) calls }); at the end assign or return the accumulated
totals; keep the existing variable names (listener_traffic,
http3_active_connections, http3_active_request_streams,
http3_retry_issued_total, http3_retry_failed_total,
http3_request_accept_errors_total, http3_request_resolve_errors_total,
http3_request_body_stream_errors_total, http3_response_stream_errors_total) so
callers like status_snapshot() continue to work.
🤖 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-http/src/proxy/clients/http3.rs`:
- Around line 369-388: The code currently skips reading HTTP trailers based on
expect_trailers (Content-Type), which silently drops valid non-gRPC trailers;
change the logic so that after the response body finishes you always call
request_stream.recv_trailers().await once (remove the conditional check on
expect_trailers) and keep the existing match arms (sending Frame::trailers via
tx, treating Ok(None) as no trailers, and the same error handling including
is_clean_http3_response_shutdown, session.mark_closed and tx.send of the error).
Ensure you update the block around request_stream.recv_trailers(),
Frame::trailers, tx.send and session.mark_closed so trailers are attempted
unconditionally.
- Around line 325-327: The is_end_stream() implementation returns true too early
because tokio::mpsc::Receiver::is_closed() only means no more sends will occur,
not that the buffer is empty; change is_end_stream() to check both closure and
emptiness by using self.rx.is_closed() && self.rx.len() == 0 (since Receiver has
no is_empty()), or implement and consult existing server-side flags (e.g.
data_finished and trailers_finished) and return true only when both data and
trailers are finished; update the is_end_stream() method accordingly to
reference self.rx and/or the data_finished/trailers_finished flags so the
response body isn't ended prematurely.

In `@crates/rginx-http/src/server/http3.rs`:
- Around line 424-468: The current read-then-create flow in
load_or_create_host_key_material/create_host_key_material allows a race where
two processes both observe NotFound and generate different keys, causing one to
overwrite the other; fix by using O_EXCL/create_new semantics when creating the
on-disk key: in create_host_key_material (called by
load_or_create_host_key_material) first ensure the parent dir exists, then
attempt to create the target file atomically with
OpenOptions::new().write(true).create_new(true).open(path) and write the
generated bytes into that handle, set permissions (on unix) and flush; if open
fails with AlreadyExists, fall back to reading the file (std::fs::read) and
validate via validate_host_key_material and return that content instead;
propagate other errors as before. Reference: load_or_create_host_key_material,
create_host_key_material, validate_host_key_material.

In `@crates/rginx-http/src/state/connections.rs`:
- Around line 145-182: The guards currently silently no-op if
listener_traffic_counters(listener_id) returns None; change
retain_http3_connection and retain_http3_request_stream to fail fast like
retain_connection_slot by returning a Result (e.g.
Result<ActiveHttp3ConnectionGuard, SomeError> /
Result<ActiveHttp3RequestStreamGuard, SomeError>) and return Err when
listener_traffic_counters(listener_id) is None instead of constructing a guard
with None counters; keep the existing increment/notify logic when counters is
Some and construct the Ok(guard) only then, and update callers to handle the
Result.

In `@crates/rginx-http/src/tls/acceptor.rs`:
- Around line 156-158: The current TLS config unconditionally sets
config.max_early_data_size = u32::MAX when http3_only &&
http3_early_data_enabled, which allows up to 4GB of 0-RTT and wastes memory;
change this to clamp the early-data limit to the application's
max_request_body_bytes (or a conservative default) instead. In acceptor.rs,
where http3_only and http3_early_data_enabled are checked, replace the
unconditional u32::MAX assignment with logic that computes a safe u32 cap from
max_request_body_bytes (e.g., min(max_request_body_bytes, u32::MAX) cast to u32)
or a configured default, and assign that value to config.max_early_data_size so
TLS buffers cannot exceed the application request body limit.

In `@crates/rginx-runtime/src/bootstrap/listeners.rs`:
- Around line 83-89: The code currently reuses inherited UDP sockets for
listener.http3 without reconciling their count with
config.runtime.accept_workers; update the branch that handles Some(sockets) so
you normalize the number to accept_workers: wrap existing sockets with Arc::new
and if sockets.len() > accept_workers truncate to accept_workers, if
sockets.len() < accept_workers call
bind_std_udp_sockets(listener.http3.listen_addr, missing) to create the missing
sockets and append them, then collect into the std_udp_sockets Vec; ensure you
reference listener.http3, inherited.udp, bind_std_udp_sockets and
config.runtime.accept_workers when making the change and preserve existing Arc
wrapping.

---

Nitpick comments:
In `@crates/rginx-app/src/admin_cli/traffic.rs`:
- Around line 130-189: Extract the long traffic_listener_http3 print logic into
a helper function (e.g., print_listener_http3 or print_http3_metrics) that takes
the listener id and a reference to listener.http3_runtime and calls print_record
with the same metric keys/values; replace the inline block in the original code
with a single call to that helper. Ensure the helper lives near other CLI
printing utilities, accepts (&str or String) listener_id and the http3 runtime
struct (match its type), converts the fields to strings as currently done, and
preserves all metric names exactly so other output paths remain consistent.

In `@crates/rginx-app/src/main.rs`:
- Around line 48-74: Extract the duplicated CheckSummary construction into a
single helper function (e.g., fn build_check_summary(config: &Config) ->
CheckSummary) and call it from both the check and -t branches instead of
duplicating the struct literal; move the http3_early_data_enabled_listeners
iterator logic (the .iter().filter(|listener|
listener.http3.as_ref().is_some_and(|http3| http3.early_data_enabled)).count())
and all other fields (listener_model, listener_count, listener_binding_count,
listeners, tls_enabled, http3_enabled, total_vhost_count, total_route_count,
upstream_count, worker_threads, accept_workers, tls) into that helper so future
fields are changed in one place and both branches simply call
build_check_summary(&config) when invoking print_check_success.
- Around line 576-578: The code compares binding.kind via its string form;
replace the string comparison with an enum comparison (e.g., use
matches!(binding.kind, ListenerTransportKind::Udp) or direct equality against
ListenerTransportKind::Udp) when setting reuse_port_enabled so it’s consistent
with other code (see ListenerTransportKind::Udp) and still combine with
config.runtime.accept_workers > 1 for the .then_some(...) logic; update the
expression that computes reuse_port_enabled (which references binding.kind and
config.runtime.accept_workers) to use the enum-based check.

In `@crates/rginx-config/src/compile/tests.rs`:
- Around line 2248-2258: The test's expected Http3Config currently lists
early_data but does not assert the runtime default; update the test in
crates/rginx-config/src/compile/tests.rs (both occurrences around the shown diff
and the one at 2286-2292) to explicitly assert that Http3Config::early_data is
None (i.e., the default is disabled) by adding a check against None for the
early_data field when validating the compiled config so the test will fail if
the default flips to enabled.

In `@crates/rginx-core/src/config/tests.rs`:
- Around line 148-177: The test sets early_data_enabled: false on the UDP
listener but doesn't assert the mapped binding exposes it; add an assertion that
the binding produced by listener.transport_bindings() reflects this flag (e.g.
check bindings[1] for the http3 early-data field — something like
bindings[1].http3_early_data_enabled == Some(false) or the appropriate field
name used by the binding struct) to ensure early_data_enabled is covered by the
test.

In `@crates/rginx-http/src/handler/dispatch.rs`:
- Line 61: Create a small newtype wrapper for the extension instead of using a
raw bool: define a simple struct EarlyDataFlag(pub bool) (derive Copy, Clone,
Debug) and update any places that insert the extension to use
EarlyDataFlag(true/false). Then change the retrieval in dispatch (where
early_data is set from request.extensions()) to use
request.extensions().get::<EarlyDataFlag>().map(|f| f.0).unwrap_or(false)
(replacing get::<bool>().copied().unwrap_or(false)) so the extension key is
type-safe and avoids collisions.

In `@crates/rginx-http/src/handler/mod.rs`:
- Around line 44-50: 当前实现把 0-RTT 的标记直接以裸 bool 插入 Request 的 extensions(在
attach_connection_metadata 中使用 connection.early_data),这会与其它 bool 扩展冲突;请定义一个专用
newtype(例如 EarlyData(bool))并在 attach_connection_metadata 用
request.extensions_mut().insert(EarlyData(connection.early_data)) 插入;同时更新读取方改为
request.extensions().get::<EarlyData>().map(|v| v.0).unwrap_or(false);保持对
tls_client_identity 的处理不变(仍在 attach_connection_metadata 中插入其具体类型)。

In `@crates/rginx-http/src/proxy/clients/http3.rs`:
- Around line 414-418: The current is_clean_http3_response_shutdown function
relies on Display text matching which is brittle; change it to inspect concrete
error types/enums instead: update the function signature
(is_clean_http3_response_shutdown) to accept &dyn std::error::Error (or the
concrete error type used in call sites), attempt downcasting
(error.downcast_ref::<h3::Error>() or downcast_ref::<quinn::ConnectionError>() /
quinn::TransportError>() as appropriate) and match the structured variants (e.g.
h3::Error::ApplicationClose or Error::Application { code } and compare against
h3::ErrorCode::H3_NO_ERROR or the quinn close-code enum) to return true for
clean shutdowns; keep a Display fallback only as a last resort and update all
call sites to pass the error as an error trait object or concrete type.

In `@crates/rginx-http/src/state/connections.rs`:
- Around line 314-416: All these record_http3_* methods repeat the same
snapshot/notification sequence; extract a helper like
bump_http3_counter_and_snapshot(listener_id: &str, op: impl
FnOnce(&ListenerTrafficCounters)) that (1) looks up
listener_traffic_counters(listener_id), (2) runs the provided closure to
increment the specific atomic counter, and (3) calls
mark_snapshot_changed_components(true, false, true, false, false),
mark_traffic_targets_changed(..., Some(listener_id), None, None) and
notify_snapshot_waiters(); then refactor record_http3_retry_issued,
record_http3_retry_failed, record_http3_request_* and
record_http3_response_stream_error to call this helper with a small closure that
increments the right field, and refactor record_http3_connection_close to either
call the same helper inside each match arm or use the helper with a closure that
matches on the quinn::ConnectionError and increments the appropriate counter.

In `@crates/rginx-http/src/state/lifecycle.rs`:
- Around line 28-79: Multiple separate iterations over config.listeners to
collect HTTP/3 counters should be replaced with a single loop/fold to avoid
repeated lookups and reduce lock/CPU overhead in status_snapshot(); implement by
iterating once over config.listeners (or using an iterator.fold) and for each
listener do listener_traffic.listeners.get(&listener.id).map(|entry| { increment
local accumulators: http3_active_connections, http3_active_request_streams,
http3_retry_issued_total, http3_retry_failed_total,
http3_request_accept_errors_total, http3_request_resolve_errors_total,
http3_request_body_stream_errors_total, http3_response_stream_errors_total using
the same .load(...) calls }); at the end assign or return the accumulated
totals; keep the existing variable names (listener_traffic,
http3_active_connections, http3_active_request_streams,
http3_retry_issued_total, http3_retry_failed_total,
http3_request_accept_errors_total, http3_request_resolve_errors_total,
http3_request_body_stream_errors_total, http3_response_stream_errors_total) so
callers like status_snapshot() continue to work.

In `@scripts/run-http3-release-gate.sh`:
- Around line 88-92: The --netem-profile parsing currently only assigns
NETEM_PROFILE without validating allowed values; add an explicit enum check
after parsing NETEM_PROFILE (e.g., compare NETEM_PROFILE against the documented
allowed values) and call die with a clear error if it is not one of them. Update
the parsing branch that sets NETEM_PROFILE (and the other occurrence around the
same code) to perform this validation immediately after shift, using the
existing die helper for failure messages and referencing NETEM_PROFILE in the
error so users see the invalid value and the allowed set.
🪄 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: 129e9f02-a8bc-4f69-889c-9f4928d3b2fc

📥 Commits

Reviewing files that changed from the base of the PR and between 222af3f and 64b6cc9.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (56)
  • .github/workflows/release.yml
  • ARCHITECTURE_HTTP3_PHASE0_FREEZE.md
  • ARCHITECTURE_HTTP3_PLAN.md
  • ARCHITECTURE_REMEDIATION_PLAN.md
  • ARCHITECTURE_REMEDIATION_RELEASE_NOTE.md
  • Cargo.toml
  • README.md
  • RELEASE_NOTES_v0.1.3-rc.11.md
  • crates/rginx-app/src/admin_cli/counters.rs
  • crates/rginx-app/src/admin_cli/status.rs
  • crates/rginx-app/src/admin_cli/traffic.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/http3.rs
  • crates/rginx-app/tests/support/mod.rs
  • crates/rginx-config/src/compile/route.rs
  • crates/rginx-config/src/compile/server.rs
  • crates/rginx-config/src/compile/tests.rs
  • crates/rginx-config/src/model.rs
  • crates/rginx-config/src/validate/server.rs
  • crates/rginx-config/src/validate/tests.rs
  • crates/rginx-core/src/config.rs
  • crates/rginx-core/src/config/route.rs
  • crates/rginx-core/src/config/tests.rs
  • crates/rginx-http/Cargo.toml
  • crates/rginx-http/src/client_ip.rs
  • crates/rginx-http/src/handler/dispatch.rs
  • crates/rginx-http/src/handler/mod.rs
  • crates/rginx-http/src/handler/response.rs
  • crates/rginx-http/src/handler/tests.rs
  • crates/rginx-http/src/proxy/clients/http3.rs
  • crates/rginx-http/src/router.rs
  • crates/rginx-http/src/server/connection.rs
  • crates/rginx-http/src/server/http3.rs
  • crates/rginx-http/src/state/connections.rs
  • crates/rginx-http/src/state/counters.rs
  • crates/rginx-http/src/state/helpers.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/listeners.rs
  • crates/rginx-http/src/state/traffic.rs
  • crates/rginx-http/src/tls/acceptor.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/shutdown.rs
  • crates/rginx-runtime/src/restart.rs
  • scripts/prepare-release.sh
  • scripts/run-http3-gate.sh
  • scripts/run-http3-phase0.sh
  • scripts/run-http3-release-gate.sh
  • scripts/run-http3-soak.sh
💤 Files with no reviewable changes (4)
  • ARCHITECTURE_HTTP3_PLAN.md
  • ARCHITECTURE_HTTP3_PHASE0_FREEZE.md
  • ARCHITECTURE_REMEDIATION_RELEASE_NOTE.md
  • ARCHITECTURE_REMEDIATION_PLAN.md
👮 Files not reviewed due to content moderation or server errors (8)
  • crates/rginx-app/src/admin_cli/status.rs
  • crates/rginx-app/tests/support/mod.rs
  • crates/rginx-http/src/state/tests.rs
  • crates/rginx-core/src/config.rs
  • crates/rginx-runtime/src/restart.rs
  • scripts/run-http3-soak.sh
  • scripts/run-http3-phase0.sh
  • crates/rginx-config/src/validate/tests.rs

Comment thread crates/rginx-http/src/proxy/clients/http3.rs
Comment thread crates/rginx-http/src/proxy/clients/http3.rs Outdated
Comment thread crates/rginx-http/src/server/http3.rs
Comment thread crates/rginx-http/src/state/connections.rs Outdated
Comment thread crates/rginx-http/src/tls/acceptor.rs Outdated
Comment thread crates/rginx-runtime/src/bootstrap/listeners.rs
@vansour
Copy link
Copy Markdown
Owner Author

vansour commented Apr 13, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/rginx-http/src/tls/acceptor.rs (1)

64-74: ⚠️ Potential issue | 🔴 Critical

Clippy 构建失败:参数过多

Pipeline 日志显示 build_tls_server_config 函数有 9 个参数,超过了 Clippy 的限制(8 个)。建议将相关参数重构为配置结构体。

🔧 建议的重构方案
+struct TlsServerConfigOptions<'a> {
+    default_tls: Option<&'a ServerTls>,
+    default_certificate: Option<&'a str>,
+    tls_termination_enabled: bool,
+    default_vhost: &'a VirtualHost,
+    vhosts: &'a [VirtualHost],
+    alpn_protocols: Vec<String>,
+    http3_only: bool,
+    max_request_body_bytes: Option<usize>,
+    http3_early_data_enabled: bool,
+}

-fn build_tls_server_config(
-    default_tls: Option<&ServerTls>,
-    default_certificate: Option<&str>,
-    tls_termination_enabled: bool,
-    default_vhost: &VirtualHost,
-    vhosts: &[VirtualHost],
-    alpn_protocols: Vec<String>,
-    http3_only: bool,
-    max_request_body_bytes: Option<usize>,
-    http3_early_data_enabled: bool,
-) -> Result<Option<Arc<ServerConfig>>> {
+fn build_tls_server_config(options: TlsServerConfigOptions<'_>) -> Result<Option<Arc<ServerConfig>>> {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/rginx-http/src/tls/acceptor.rs` around lines 64 - 74, The function
build_tls_server_config currently takes nine parameters and triggers Clippy's
"too many arguments" lint; create a new struct (e.g., TlsServerConfig or
BuildTlsConfig) that groups the related fields (default_tls: Option<&ServerTls>,
default_certificate: Option<&str>, tls_termination_enabled: bool, default_vhost:
&VirtualHost, vhosts: &[VirtualHost], alpn_protocols: Vec<String>, http3_only:
bool, max_request_body_bytes: Option<usize>, http3_early_data_enabled: bool),
replace the nine-arg signature with build_tls_server_config(cfg:
&TlsServerConfig) -> Result<Option<Arc<ServerConfig>>>, and update all call
sites to construct and pass the new struct; keep the inner logic of
build_tls_server_config and the types ServerTls, VirtualHost, ServerConfig
unchanged.
♻️ Duplicate comments (1)
crates/rginx-runtime/src/bootstrap/listeners.rs (1)

86-95: ⚠️ Potential issue | 🟠 Major

当 HTTP/3 accept_workers 从 1 扩容到 N 时,继承的 socket 与新绑定 socket 的 SO_REUSEPORT 设置不匹配会导致热重启失败

当上一代 accept_workers=1 时,继承的 UDP socket 由 bind_std_udp_sockets(..., 1) 创建,会传递 reuse_port=false。在热重启时若 accept_workers 增加到 N(N>1),代码会尝试以 reuse_port=true 绑定新 socket,但在 Linux 上无法将开启 SO_REUSEPORT 的 socket 绑定到已被占用但未启用 SO_REUSEPORT 的地址,导致 EADDRINUSE 失败。需在 sockets.len() == 1 && desired_udp_socket_count > 1 时明确拒绝,避免将配置变更变成隐式的 bind 失败。

建议的修复
                 Some(sockets) => {
                     let mut sockets = sockets.into_iter().map(Arc::new).collect::<Vec<_>>();
+                    if sockets.len() == 1 && desired_udp_socket_count > 1 {
+                        return Err(Error::Server(format!(
+                            "listener `{}` cannot increase HTTP/3 accept_workers from 1 to {} during restart; do a cold restart or keep the previous worker count",
+                            listener.name,
+                            desired_udp_socket_count,
+                        )));
+                    }
                     if sockets.len() > desired_udp_socket_count {
                         sockets.truncate(desired_udp_socket_count);
                     } else if sockets.len() < desired_udp_socket_count {
🤖 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 86 - 95, When
upgrading from a single inherited UDP socket to multiple listeners, the code in
the Some(sockets) branch must detect the incompatible SO_REUSEPORT mismatch and
fail early instead of attempting new binds; add a guard that if sockets.len() ==
1 && desired_udp_socket_count > 1 then return an explicit error (with a clear
message) rather than calling
bind_std_udp_sockets_with_reuse_port(http3.listen_addr, ...). Ensure this check
sits before truncation/extension logic so bind_std_udp_sockets_with_reuse_port
is only invoked when all existing sockets already match the intended reuse_port
semantics.
🧹 Nitpick comments (1)
crates/rginx-config/src/compile/tests.rs (1)

2295-2384: 建议补充 early_data 显式开启分支,避免只覆盖默认分支。

当前这个用例在 Line 2350 仍使用 early_data: None,建议直接在该测试里覆盖 Some(true),并断言 early_data_enabled == true

♻️ 建议修改
             http3: Some(Http3Config {
                 listen: Some("127.0.0.1:9443".to_string()),
                 advertise_alt_svc: Some(false),
                 alt_svc_max_age_secs: Some(7200),
                 max_concurrent_streams: Some(256),
                 stream_buffer_size_bytes: Some(131072),
                 active_connection_id_limit: Some(5),
                 retry: Some(true),
                 host_key_path: Some("quic/host.key".to_string()),
                 gso: Some(true),
-                early_data: None,
+                early_data: Some(true),
             }),
@@
     assert_eq!(http3.host_key_path, Some(base_dir.path().join("quic/host.key")));
     assert!(http3.gso);
+    assert!(http3.early_data_enabled);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/rginx-config/src/compile/tests.rs` around lines 2295 - 2384, Test
compile_http3_applies_transport_settings_and_resolves_host_key_path sets
Http3Config.early_data to None which only verifies default behavior; change the
test to set Http3Config { ..., early_data: Some(true), ... } and add an
assertion that the compiled snapshot's http3.early_data_enabled (or the
equivalent field in the compiled Http3 struct) is true to explicitly test
enabling early data; update the test function
compile_http3_applies_transport_settings_and_resolves_host_key_path and the
Http3Config instance and add the assert on http3.early_data_enabled.
🤖 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-http/src/proxy/clients/http3.rs`:
- Around line 191-205: The code currently releases the sessions lock before
calling connect_session(&key, peer_url) allowing concurrent callers to each
start a connection; fix by introducing an in-progress marker so only one
connect_session runs per Http3SessionKey: when sessions lock is held, check for
an existing non-closed session (sessions.get(&key).is_closed()), and if missing
insert a placeholder (e.g., a Pending entry implemented with a oneshot/Notify or
a shared JoinHandle/Future) into self.sessions to indicate "connecting", then
drop the lock and run connect_session(&key, peer_url); upon success replace the
placeholder with the real Arc session (or send it via the oneshot), and ensure
awaiters that see the Pending entry wait for the result instead of starting
their own connect_session; keep using self.sessions, connect_session,
Http3SessionKey, and is_closed to locate the relevant logic.
- Around line 296-304: StreamingResponseBody currently spawns a background task
that awaits request_stream.recv_data() but does not keep its JoinHandle, so
dropping the body won't cancel that task; modify StreamingResponseBody to hold
an Option<JoinHandle<()>> (e.g. field name join_handle), set that handle when
creating the body (where tokio::spawn is called), and implement Drop for
StreamingResponseBody to take() the join_handle and call abort() to cancel the
background task immediately (also consider joining/ignoring the join result if
needed); keep existing rx/size_hint/done behavior but ensure the spawned task
uses the same tx/rx and is aborted on drop to free HTTP/3 streams promptly.

In `@crates/rginx-http/src/server/http3.rs`:
- Around line 392-410: When http3.active_connection_id_limit equals
HTTP3_ACTIVE_CONNECTION_ID_LIMIT_MIGRATION, do not silently skip configuring a
hashed CID generator when host_key_material is None; instead detect
host_key_material == None in that branch (where derive_hashed_connection_id_key
and endpoint_config.cid_generator / quinn_proto::HashedConnectionIdGenerator are
used) and return an explicit Err (or at minimum log a warning and return Err)
indicating that host_key_path/host_key_material is required for migration mode
so callers cannot accidentally run without a stable HashedConnectionIdGenerator.

In `@scripts/run-http3-release-gate.sh`:
- Line 93: The error message in the die call uses backtick-style command
substitution around `${NETEM_PROFILE}` which can lead to command injection;
update the die invocation in the case fallback (the line using die
"--netem-profile must be one of: ... (got `${NETEM_PROFILE}`)") to avoid command
substitution by printing the variable literally and safely—e.g., stop using
backticks and instead include the variable via proper quoting (such as
"'$NETEM_PROFILE'" or printf-style safe expansion) so the NETEM_PROFILE value is
shown without executing it.

In `@scripts/run-http3-soak.sh`:
- Around line 141-143: 当前逻辑在 need_privileged=1 时无条件调用 ip 去读取 orig_mtu,导致在没有 ip
命令的环境(比如仅启用 netem 场景)失败;请在使用 ip 命令前加上可用性检查,修改该判断为同时满足 orig_mtu
为空、need_privileged=1 且 ip 命令存在(例如通过 command -v ip 或 command -v ip >/dev/null
检查)后再运行 ip -o link show dev "${NETEM_DEV}" | awk ...;保留变量名
orig_mtu、need_privileged 和 NETEM_DEV 不变以便定位。

---

Outside diff comments:
In `@crates/rginx-http/src/tls/acceptor.rs`:
- Around line 64-74: The function build_tls_server_config currently takes nine
parameters and triggers Clippy's "too many arguments" lint; create a new struct
(e.g., TlsServerConfig or BuildTlsConfig) that groups the related fields
(default_tls: Option<&ServerTls>, default_certificate: Option<&str>,
tls_termination_enabled: bool, default_vhost: &VirtualHost, vhosts:
&[VirtualHost], alpn_protocols: Vec<String>, http3_only: bool,
max_request_body_bytes: Option<usize>, http3_early_data_enabled: bool), replace
the nine-arg signature with build_tls_server_config(cfg: &TlsServerConfig) ->
Result<Option<Arc<ServerConfig>>>, and update all call sites to construct and
pass the new struct; keep the inner logic of build_tls_server_config and the
types ServerTls, VirtualHost, ServerConfig unchanged.

---

Duplicate comments:
In `@crates/rginx-runtime/src/bootstrap/listeners.rs`:
- Around line 86-95: When upgrading from a single inherited UDP socket to
multiple listeners, the code in the Some(sockets) branch must detect the
incompatible SO_REUSEPORT mismatch and fail early instead of attempting new
binds; add a guard that if sockets.len() == 1 && desired_udp_socket_count > 1
then return an explicit error (with a clear message) rather than calling
bind_std_udp_sockets_with_reuse_port(http3.listen_addr, ...). Ensure this check
sits before truncation/extension logic so bind_std_udp_sockets_with_reuse_port
is only invoked when all existing sockets already match the intended reuse_port
semantics.

---

Nitpick comments:
In `@crates/rginx-config/src/compile/tests.rs`:
- Around line 2295-2384: Test
compile_http3_applies_transport_settings_and_resolves_host_key_path sets
Http3Config.early_data to None which only verifies default behavior; change the
test to set Http3Config { ..., early_data: Some(true), ... } and add an
assertion that the compiled snapshot's http3.early_data_enabled (or the
equivalent field in the compiled Http3 struct) is true to explicitly test
enabling early data; update the test function
compile_http3_applies_transport_settings_and_resolves_host_key_path and the
Http3Config instance and add the assert on http3.early_data_enabled.
🪄 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: 3715897a-c530-43e7-9618-75466a6b51c5

📥 Commits

Reviewing files that changed from the base of the PR and between 64b6cc9 and d08a470.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • crates/rginx-app/Cargo.toml
  • crates/rginx-app/src/main.rs
  • crates/rginx-config/src/compile/tests.rs
  • crates/rginx-core/src/config/tests.rs
  • crates/rginx-http/src/handler/dispatch.rs
  • crates/rginx-http/src/handler/mod.rs
  • crates/rginx-http/src/proxy/clients/http3.rs
  • crates/rginx-http/src/server/http3.rs
  • crates/rginx-http/src/state/connections.rs
  • crates/rginx-http/src/state/lifecycle.rs
  • crates/rginx-http/src/state/tests.rs
  • crates/rginx-http/src/tls/acceptor.rs
  • crates/rginx-runtime/src/bootstrap/listeners.rs
  • scripts/run-http3-release-gate.sh
  • scripts/run-http3-soak.sh
✅ Files skipped from review due to trivial changes (2)
  • crates/rginx-app/Cargo.toml
  • crates/rginx-http/src/state/connections.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • crates/rginx-http/src/handler/mod.rs
  • crates/rginx-http/src/handler/dispatch.rs
  • crates/rginx-app/src/main.rs
  • crates/rginx-http/src/state/lifecycle.rs

Comment thread crates/rginx-http/src/proxy/clients/http3.rs Outdated
Comment thread crates/rginx-http/src/proxy/clients/http3.rs Outdated
Comment thread crates/rginx-http/src/server/http3.rs
Comment thread scripts/run-http3-release-gate.sh Outdated
Comment thread scripts/run-http3-soak.sh Outdated
@vansour
Copy link
Copy Markdown
Owner Author

vansour commented Apr 13, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 2

🧹 Nitpick comments (2)
crates/rginx-config/src/compile/tests.rs (1)

107-107: 建议抽取 LocationConfig 测试构造器,减少重复样板。

这批 allow_early_data: None 的补齐是正确的,但现在 fixture 重复度很高,后续模型再加字段会继续放大改动面。建议在本测试文件内抽一个默认构造 helper。

♻️ 可选重构示例
+fn test_location(matcher: MatcherConfig, handler: HandlerConfig) -> LocationConfig {
+    LocationConfig {
+        matcher,
+        handler,
+        grpc_service: None,
+        grpc_method: None,
+        allow_cidrs: Vec::new(),
+        deny_cidrs: Vec::new(),
+        requests_per_sec: None,
+        burst: None,
+        allow_early_data: None,
+    }
+}
-        locations: vec![LocationConfig {
-            matcher: MatcherConfig::Prefix("/api".to_string()),
-            handler: HandlerConfig::Proxy {
-                upstream: "secure-backend".to_string(),
-                preserve_host: None,
-                strip_prefix: None,
-                proxy_set_headers: std::collections::HashMap::new(),
-            },
-            grpc_service: None,
-            grpc_method: None,
-            allow_cidrs: Vec::new(),
-            deny_cidrs: Vec::new(),
-            requests_per_sec: None,
-            burst: None,
-            allow_early_data: None,
-        }],
+        locations: vec![test_location(
+            MatcherConfig::Prefix("/api".to_string()),
+            HandlerConfig::Proxy {
+                upstream: "secure-backend".to_string(),
+                preserve_host: None,
+                strip_prefix: None,
+                proxy_set_headers: std::collections::HashMap::new(),
+            },
+        )],

Also applies to: 224-224, 319-319, 426-426, 522-522, 633-633, 737-737, 835-835, 940-940, 1047-1047, 1151-1151, 1252-1252, 1346-1346, 1436-1436, 1487-1487, 1539-1539, 1593-1593, 1612-1612, 1691-1691, 1764-1764, 1848-1848, 1902-1902, 1955-1955, 2027-2027, 2042-2042, 2105-2105, 2189-2189, 2275-2275, 2367-2367

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

In `@crates/rginx-config/src/compile/tests.rs` at line 107, Tests repeat
constructing LocationConfig (many places setting allow_early_data: None) causing
boilerplate; add a single test helper that returns a default LocationConfig
(e.g., fn default_location_config() -> LocationConfig or impl LocationConfig {
fn test_default() -> Self }) that sets common fields including allow_early_data:
None, then replace repeated literal constructions with struct update syntax
(LocationConfig { specific_field: X, ..default_location_config() }) or call the
helper and modify fields as needed; update all occurrences (reference symbol:
LocationConfig) in this test file to use the helper to reduce duplication.
crates/rginx-runtime/src/bootstrap/listeners.rs (1)

615-628: 建议把热重启归一化分支也补成测试。

当前新增测试只覆盖 fresh bind 的 3 sockets 路径,但这次改动里最容易回归的是 reload 时的三条分支:继承 socket 截断、补齐,以及 1 -> N 的 fail fast。建议至少把这三条路径补齐,否则这部分问题只能在热重启时才暴露。

🤖 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 615 - 628, The
new test only covers the fresh-bind path for bind_std_udp_sockets; add unit
tests that exercise the hot-restart (reload) branches: (1) inherited socket
truncation when more inherited sockets exist than workers, (2) filling up when
fewer inherited sockets exist than workers, and (3) the fail-fast transition
when going from 1 existing socket to N workers. Locate bind_std_udp_sockets and
bind_std_udp_socket and write three tests that simulate inherited sockets
(create seed sockets via bind_std_udp_socket or otherwise simulate the inherited
set), call bind_std_udp_sockets in reload mode, and assert the resulting socket
list length and local_addr behavior for each case to ensure truncation, filling,
and fail-fast behave as expected.
🤖 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-config/src/validate/server.rs`:
- Around line 421-439: The current validation only rejects tls.session_tickets
== Some(true) when http3.early_data is enabled, but leaves None through (which
defaults to enabling tickets at runtime), so update the check in the
http3.early_data block to require tls.session_tickets be explicitly Some(false)
(i.e. reject None and Some(true)); locate the block referencing http3.early_data
and tls.session_tickets and change the conditional to error unless
tls.session_tickets == Some(false), keeping the existing Error::Config message
(or adjust wording to mention that session_tickets must be explicitly false).

In `@scripts/run-http3-soak.sh`:
- Around line 128-133: 函数 clear_faults 在 need_privileged=1 时无条件执行 tc qdisc
del,这会在用户仅传 --mtu(NETEM_PROFILE=none)时错误地删除接口的 root qdisc;修改 clear_faults:在判断
need_privileged 之前或内部,先检查 NETEM_PROFILE 是否为 "none",仅当 NETEM_PROFILE !=
"none"(即确实使用 netem)时才运行 tc qdisc del dev "${NETEM_DEV}" root;保留对 orig_mtu
的恢复逻辑和现有重定向/容错(>/dev/null 2>&1 || true),并确保对变量
NETEM_DEV/NETEM_PROFILE/need_privileged 的引用与现有实现一致。

---

Nitpick comments:
In `@crates/rginx-config/src/compile/tests.rs`:
- Line 107: Tests repeat constructing LocationConfig (many places setting
allow_early_data: None) causing boilerplate; add a single test helper that
returns a default LocationConfig (e.g., fn default_location_config() ->
LocationConfig or impl LocationConfig { fn test_default() -> Self }) that sets
common fields including allow_early_data: None, then replace repeated literal
constructions with struct update syntax (LocationConfig { specific_field: X,
..default_location_config() }) or call the helper and modify fields as needed;
update all occurrences (reference symbol: LocationConfig) in this test file to
use the helper to reduce duplication.

In `@crates/rginx-runtime/src/bootstrap/listeners.rs`:
- Around line 615-628: The new test only covers the fresh-bind path for
bind_std_udp_sockets; add unit tests that exercise the hot-restart (reload)
branches: (1) inherited socket truncation when more inherited sockets exist than
workers, (2) filling up when fewer inherited sockets exist than workers, and (3)
the fail-fast transition when going from 1 existing socket to N workers. Locate
bind_std_udp_sockets and bind_std_udp_socket and write three tests that simulate
inherited sockets (create seed sockets via bind_std_udp_socket or otherwise
simulate the inherited set), call bind_std_udp_sockets in reload mode, and
assert the resulting socket list length and local_addr behavior for each case to
ensure truncation, filling, and fail-fast behave as expected.
🪄 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: 834001f3-4f09-45f9-b798-5aa5f44444b9

📥 Commits

Reviewing files that changed from the base of the PR and between d08a470 and 590644d.

📒 Files selected for processing (8)
  • crates/rginx-config/src/compile/tests.rs
  • crates/rginx-config/src/validate/server.rs
  • crates/rginx-http/src/proxy/clients/http3.rs
  • crates/rginx-http/src/server/http3.rs
  • crates/rginx-http/src/tls/acceptor.rs
  • crates/rginx-runtime/src/bootstrap/listeners.rs
  • scripts/run-http3-release-gate.sh
  • scripts/run-http3-soak.sh
🚧 Files skipped from review as they are similar to previous changes (3)
  • scripts/run-http3-release-gate.sh
  • crates/rginx-http/src/server/http3.rs
  • crates/rginx-http/src/tls/acceptor.rs

Comment on lines +421 to +439
if matches!(http3.early_data, Some(true)) {
if matches!(tls.session_resumption, Some(false)) {
return Err(Error::Config(format!(
"{owner_label} http3 early_data requires tls session_resumption to remain enabled"
)));
}

if tls.session_cache_size.is_some_and(|size| size == 0) {
return Err(Error::Config(format!(
"{owner_label} http3 early_data requires tls session_cache_size to remain greater than 0"
)));
}

if matches!(tls.session_tickets, Some(true)) {
return Err(Error::Config(format!(
"{owner_label} http3 early_data requires tls session_tickets to remain disabled so stateful resumption is used"
)));
}
}
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Apr 13, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 目的:确认 session_tickets/session_resumption/session_cache_size 在“未配置(None)”时的最终默认行为
# 预期:若 early_data 要求“tickets 必须禁用”,则应能证明 None 最终落到 disabled;否则需要在校验中要求 Some(false)。

echo "== 查找 TLS 配置字段定义 =="
rg -n -C3 'struct ServerTlsConfig|session_tickets|session_resumption|session_cache_size' crates/rginx-config/src/model.rs

echo
echo "== 查找 validate_http3 相关逻辑 =="
rg -n -C4 'early_data|session_tickets|session_resumption|session_cache_size' crates/rginx-config/src/validate/server.rs

echo
echo "== 查找编译阶段默认值/回填逻辑 =="
rg -n -C4 'session_tickets|session_resumption|session_cache_size|unwrap_or|unwrap_or_else' crates/rginx-config/src/compile/server.rs crates/rginx-app/src/admin_cli/*.rs 2>/dev/null || true

Repository: vansour/rginx

Length of output: 48060


🏁 Script executed:

# 查找运行时 TLS 配置应用和 rustls 初始化逻辑
echo "== 查找 TLS 配置在运行时的应用(rginx-http 或 transport 层) =="
find crates -name "*.rs" -type f | xargs rg -l 'session_tickets|ServerTlsIdentity' | head -10

echo
echo "== 查找 rustls 配置或 TLS 参数应用 =="
rg -n 'ServerConfig|session_tickets|session_resumption' crates/rginx-http/src --type rust 2>/dev/null | head -30

echo
echo "== 查找编译后 TLS 配置到 rustls ServerConfig 的映射 =="
rg -n -A5 'fn.*tls|rustls::ServerConfig|session' crates/rginx-http/src/tls.rs 2>/dev/null | head -50

Repository: vansour/rginx

Length of output: 3172


🏁 Script executed:

# 查看 session.rs 的完整逻辑
cat -n crates/rginx-http/src/tls/session.rs

Repository: vansour/rginx

Length of output: 2272


HTTP/3 early_data 约束对 session_tickets=None 的验证不完整。

第434行仅拒绝 Some(true),但当 session_tickets 未设置(为 None)时,运行时在 crates/rginx-http/src/tls/session.rs 第33行的逻辑中不会应用显式禁用,导致 rustls 使用默认行为(启用 session tickets)。这与注释目标"禁用 tickets 以启用有状态恢复"相悖。

建议将验证改为要求 Some(false) 或在 early_data=true 时禁止 None 值,以确保配置合规。

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

In `@crates/rginx-config/src/validate/server.rs` around lines 421 - 439, The
current validation only rejects tls.session_tickets == Some(true) when
http3.early_data is enabled, but leaves None through (which defaults to enabling
tickets at runtime), so update the check in the http3.early_data block to
require tls.session_tickets be explicitly Some(false) (i.e. reject None and
Some(true)); locate the block referencing http3.early_data and
tls.session_tickets and change the conditional to error unless
tls.session_tickets == Some(false), keeping the existing Error::Config message
(or adjust wording to mention that session_tickets must be explicitly false).

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Verified locally against the current rustls/quinn integration: requiring tls.session_tickets = Some(false) for http3.early_data makes 0-RTT resumption unavailable and breaks routes_http3_early_data_by_replay_safety as well as the CI http3 gate. Leaving the validation as-is here is intentional until the runtime/session policy changes to support that configuration.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@vansour, understood — thank you for the detailed verification. Since session_tickets = None at the config layer is compatible with the 0-RTT resumption path in the current rustls/quinn integration (and enforcing Some(false) actively breaks it), keeping the validation at Some(true) rejection only is the right call for now. I'll note this for future reviews.


✏️ Learnings added
Learnt from: vansour
Repo: vansour/rginx PR: 43
File: crates/rginx-config/src/validate/server.rs:421-439
Timestamp: 2026-04-13T10:53:27.443Z
Learning: In `crates/rginx-config/src/validate/server.rs` (`validate_http3`), the check for `tls.session_tickets` when `http3.early_data = true` intentionally only rejects `Some(true)` and allows `None`. Requiring `Some(false)` breaks 0-RTT resumption and the `routes_http3_early_data_by_replay_safety` test and the CI `http3` gate in the current rustls/quinn integration. This is a deliberate design decision and should not be flagged as a bug until the runtime/session policy is updated to support that configuration.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment thread scripts/run-http3-soak.sh
@vansour
Copy link
Copy Markdown
Owner Author

vansour commented Apr 13, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@vansour
Copy link
Copy Markdown
Owner Author

vansour commented Apr 13, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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-config/src/compile/tests.rs (2)

2254-2260: HTTP/3 默认值断言建议避免魔法数字。

这里建议改为断言 super 中的默认常量,避免默认策略调整时测试同时散点修改。

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

In `@crates/rginx-config/src/compile/tests.rs` around lines 2254 - 2260, The
assertions use magic numbers for HTTP/3 defaults; update the tests to reference
the canonical default constants from the parent module instead of
literals—replace assertions on http3.max_concurrent_streams,
http3.stream_buffer_size, http3.active_connection_id_limit, http3.retry,
http3.host_key_path, http3.gso, and http3.early_data_enabled to compare against
the corresponding constants in super (e.g.
super::DEFAULT_MAX_CONCURRENT_STREAMS, super::DEFAULT_STREAM_BUFFER_SIZE,
super::DEFAULT_ACTIVE_CONNECTION_ID_LIMIT, and the boolean/default constants
defined in the super module) so the test follows the module's single source of
truth for defaults.

299-316: 建议继续用 test_location 收敛剩余样板 LocationConfig

Line 299 这类仍在手写默认字段的块可以继续复用 helper,减少重复和漏字段风险。

可选改法示例
-        locations: vec![LocationConfig {
-            matcher: MatcherConfig::Prefix("/".to_string()),
-            handler: HandlerConfig::Proxy {
-                upstream: "backend".to_string(),
-                preserve_host: None,
-                strip_prefix: None,
-                proxy_set_headers: std::collections::HashMap::new(),
-            },
-            grpc_service: None,
-            grpc_method: None,
-            allow_cidrs: Vec::new(),
-            deny_cidrs: Vec::new(),
-            requests_per_sec: None,
-            burst: None,
-            allow_early_data: None,
-        }],
+        locations: vec![test_location(
+            MatcherConfig::Prefix("/".to_string()),
+            HandlerConfig::Proxy {
+                upstream: "backend".to_string(),
+                preserve_host: None,
+                strip_prefix: None,
+                proxy_set_headers: std::collections::HashMap::new(),
+            },
+        )],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/rginx-config/src/compile/tests.rs` around lines 299 - 316, Replace the
manually-constructed LocationConfig block with the existing test_location helper
to remove repetitive default fields: locate the vec![LocationConfig { ... }]
instance (using MatcherConfig::Prefix("/") and HandlerConfig::Proxy { upstream:
"backend", ... }) and call test_location with the prefix matcher and a proxy
handler descriptor (provide upstream "backend" and any non-default proxy fields
like proxy_set_headers), letting test_location supply defaults for grpc_service,
grpc_method, allow_cidrs/deny_cidrs, requests_per_sec, burst, and
allow_early_data; ensure the helper invocation matches the helper's signature so
all previously handwritten fields are omitted.
🤖 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-config/src/compile/tests.rs`:
- Around line 2254-2260: The assertions use magic numbers for HTTP/3 defaults;
update the tests to reference the canonical default constants from the parent
module instead of literals—replace assertions on http3.max_concurrent_streams,
http3.stream_buffer_size, http3.active_connection_id_limit, http3.retry,
http3.host_key_path, http3.gso, and http3.early_data_enabled to compare against
the corresponding constants in super (e.g.
super::DEFAULT_MAX_CONCURRENT_STREAMS, super::DEFAULT_STREAM_BUFFER_SIZE,
super::DEFAULT_ACTIVE_CONNECTION_ID_LIMIT, and the boolean/default constants
defined in the super module) so the test follows the module's single source of
truth for defaults.
- Around line 299-316: Replace the manually-constructed LocationConfig block
with the existing test_location helper to remove repetitive default fields:
locate the vec![LocationConfig { ... }] instance (using
MatcherConfig::Prefix("/") and HandlerConfig::Proxy { upstream: "backend", ...
}) and call test_location with the prefix matcher and a proxy handler descriptor
(provide upstream "backend" and any non-default proxy fields like
proxy_set_headers), letting test_location supply defaults for grpc_service,
grpc_method, allow_cidrs/deny_cidrs, requests_per_sec, burst, and
allow_early_data; ensure the helper invocation matches the helper's signature so
all previously handwritten fields are omitted.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro

Run ID: f4c3b73b-e648-4fb7-82a6-00cd3a807168

📥 Commits

Reviewing files that changed from the base of the PR and between 49c9502 and 25a34f9.

📒 Files selected for processing (1)
  • crates/rginx-config/src/compile/tests.rs

@vansour
Copy link
Copy Markdown
Owner Author

vansour commented Apr 13, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@vansour vansour changed the title review: rerun CodeRabbit for original PR #41 http3: implement phase 7 early-data, retry, and telemetry (review replay of #41) Apr 13, 2026
@vansour
Copy link
Copy Markdown
Owner Author

vansour commented Apr 13, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@vansour
Copy link
Copy Markdown
Owner Author

vansour commented Apr 13, 2026

Superseded by #44. The validated fixes were merged into main and released as v0.1.3-rc.11.

@vansour vansour closed this Apr 13, 2026
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