Skip to content

Fix response metadata and upstream authority handling#53

Merged
vansour merged 3 commits intomainfrom
fix/response-metadata-http3-upstream-authority
Apr 25, 2026
Merged

Fix response metadata and upstream authority handling#53
vansour merged 3 commits intomainfrom
fix/response-metadata-http3-upstream-authority

Conversation

@vansour
Copy link
Copy Markdown
Owner

@vansour vansour commented Apr 25, 2026

Summary

  • generate downstream x-request-id values with UUIDv7 instead of monotonic counters
  • add configurable server_header and emit date, server, and x-request-id on downstream responses
  • treat clean HTTP/3 client ApplicationClose: 0x0 as debug-level connection closure
  • preserve logical upstream authority while dialing resolved endpoints for HTTPS/H2 upstreams

Verification

  • cargo fmt --all
  • cargo check --workspace --all-targets --message-format short
  • cargo test -p rginx-config server_header -- --nocapture
  • cargo test -p rginx-http finalize_downstream_response -- --nocapture
  • cargo test -p rginx --test phase1 return_responses_generate_and_preserve_request_id_headers -- --nocapture
  • cargo test -p rginx --test http3 serves_return_handler_over_http3 -- --nocapture
  • cargo test -p rginx --test upgrade -- --nocapture
  • ./scripts/test-fast.sh
  • ./scripts/run-tls-gate.sh
  • ./scripts/run-http3-gate.sh

Deployment Smoke Test

  • deployed release binary to 162.141.130.176
  • verified HTTP/1.1, HTTP/2, and HTTP/3 /healthz return 200 with date, server: rginx, and UUIDv7 x-request-id
  • verified WebSocket upgrade over ws:// and wss:// with 101 handshake and echo-frame round trip
  • confirmed clean HTTP/3 close no longer logs http3 request accept failed WARN

Copilot AI review requested due to automatic review settings April 25, 2026 14:23
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 25, 2026

Warning

Rate limit exceeded

@vansour has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 46 minutes and 45 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 46 minutes and 45 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: ASSERTIVE

Plan: Pro

Run ID: cc2c44ed-0f59-4f50-878f-76ab1c686d05

📥 Commits

Reviewing files that changed from the base of the PR and between 0c7bf1f and bb6e257.

📒 Files selected for processing (4)
  • crates/rginx-app/tests/phase1.rs
  • crates/rginx-http/src/handler/dispatch.rs
  • crates/rginx-http/src/handler/tests.rs
  • crates/rginx-http/src/server/http3.rs

Walkthrough

本次 PR:将请求 ID 生成改为 UUID v7;引入可配置的 Server 响应头并在响应中注入 Date/Server;重构 HTTP 代理客户端为基于 socket 地址的端点客户端缓存;增加上游 authority 追踪;新增/调整相关配置验证与测试;添加/更新工作区依赖(uuid、httpdate、webpki 等)。

Changes

Cohort / File(s) Summary
工作区依赖
Cargo.toml, crates/rginx-http/Cargo.toml
新增/固定工作区依赖:httpdate = "1.0"uuid = { version = "1.18", features = ["v7","std"] },并在 rginx-http 中添加 webpki 等依赖。
请求 ID 生成
crates/rginx-http/src/state/lifecycle.rs, crates/rginx-http/src/state/mod.rs, crates/rginx-app/tests/phase1.rs
移除原有原子计数器;SharedState::next_request_id 改为使用 uuid::Uuid::now_v7();测试断言改为校验标准 UUIDv7 格式。
Server 头配置模型
crates/rginx-config/src/model.rs, crates/rginx-core/src/config.rs, crates/rginx-core/src/lib.rs
ServerConfig/ListenerConfig 添加可选 server_header;核心 Server 添加 server_header: HeaderValueDEFAULT_SERVER_HEADERdefault_server_header() 并导出。
配置编译与验证
crates/rginx-config/src/compile/mod.rs, crates/rginx-config/src/compile/server.rs, crates/rginx-config/src/validate/server.rs, crates/rginx-config/src/compile/tests.rs, crates/rginx-config/src/validate/tests.rs
compile_listeners 签名新增 default_server_header 参数;新增 compile_server_header;验证新增 server_header 非空与 HeaderValue 解析,相关单元测试新增/更新。
响应最终化与 HTTP 日期
crates/rginx-http/src/handler/dispatch.rs, crates/rginx-http/src/handler/tests.rs
finalize_downstream_response 接收 server_header,在响应中插入 Date(使用 httpdate::fmt_http_date)和 Server 头,再插入 x-request-id;测试相应更新并断言 Server/Date 存在。
HTTP 代理客户端重构
crates/rginx-http/src/proxy/clients/mod.rs, crates/rginx-http/src/proxy/mod.rs, crates/rginx-http/src/proxy/clients/tests.rs
由单一动态 server-name 客户端改为按 SocketAddr 的端点客户端缓存(带 LRU/容量),移除旧的 DynamicServerNameResolver,引入 FixedEndpointResolver/FixedServerNameResolver,TLS/server-name 覆盖移入 HttpProxyClient 字段。
上游 authority 与 URI 构建
crates/rginx-app/tests/upstream_http2.rs, crates/rginx-http/src/proxy/common.rs, crates/rginx-http/src/proxy/tests/request_headers.rs
ObservedRequest 新增可选 authority,从请求 URI 填充;build_proxy_uri 改为使用 peer.upstream_authority 保留原上游 host/authority;新增单元测试验证。
HTTP/3 接受循环处理
crates/rginx-http/src/server/http3.rs
在 h3 accept 错误时新增 is_clean_http3_accept_close 判定(含 is_h3_no_error() 与 QUIC ApplicationClose 字符串),对干净关闭仅记录调试并退出循环。
测试与基线配置更新
多处测试文件(crates/rginx-http/src/**, crates/rginx-core/src/config/tests.rs, crates/rginx-runtime/**, crates/rginx-http/src/state/** 等)
大量测试构造显式填充 server_header(使用 default_server_header());新增空白 server_header 校验测试。
文档与发布说明
docs/HTTP3_PHASE7_RELEASE.md, docs/README.md, RELEASE_NOTES_v0.1.3-rc.13.md
删除/调整与 nginx 比较 harness 的说明与示例命令,收窄文档范围与同步触发条件。

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Proxy as Proxy/Server
    participant Config as ConfigCompiler
    participant Upstream as Upstream
    participant ResponseFinalizer as FinalizeResponse

    Note over Client,Upstream: 请求流与响应最终化(高层次)
    Client->>Proxy: HTTP Request
    activate Proxy

    Config->>Proxy: 提供 listener.server_header(编译/验证阶段)
    Proxy->>Upstream: 转发请求(build_proxy_uri 使用 peer.upstream_authority)
    Upstream-->>Proxy: Upstream Response
    Proxy->>ResponseFinalizer: 传入 listener.server_header 与响应
    ResponseFinalizer->>ResponseFinalizer: 生成 UUIDv7 请求 ID
    ResponseFinalizer->>ResponseFinalizer: 计算当前 HTTP Date (httpdate::fmt_http_date)
    ResponseFinalizer->>ResponseFinalizer: 插入 Date header
    ResponseFinalizer->>ResponseFinalizer: 插入 Server header (来自配置)
    ResponseFinalizer->>ResponseFinalizer: 插入 x-request-id header
    ResponseFinalizer->>Client: 返回 HTTP Response
    deactivate Proxy
Loading

预计代码审查工作量

🎯 4 (Complex) | ⏱️ ~60 minutes

可能相关的PR

  • feat: complete edge transport streaming phases 0-3 #45: 修改相同函数路径 crates/rginx-http/src/handler/dispatch.rs::finalize_downstream_response,与本 PR 在响应完成化参数传递上存在直接代码级重叠。
  • Prepare v0.1.3-rc.5 release #34: 同步更改了 SharedState 的请求 ID 生成逻辑(从计数器到 UUIDv7),并引入 uuid 依赖,代码层面强相关。
  • Prepare v0.1.3-rc.9 #39: 涉及请求/响应最终化与运行时状态路径的修改,与本 PR 在 finalize_downstream_response 和状态布局上有交集。

诗歌

🐰 我是小兔会数时光,v7 的 UUID 闪亮,
服务器名从配置来,Date 跟着一起上场,
代理按地址存客户端,缓存小巧不慌张,
测试齐刷刷补完善,兔兔鼓掌唱首歌欢畅!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.10% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 标题准确概括了 PR 的主要变更,即修复响应元数据和上游权限处理,涵盖了 UUIDv7 请求 ID、可配置的 server_header、日期/服务器响应头和上游权限保留等核心改进。
Description check ✅ Passed 描述详细说明了 PR 的所有主要目标、具体实现改进、验证步骤和部署烟雾测试结果,与变更集高度相关并提供了充分的上下文。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 fix/response-metadata-http3-upstream-authority

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 updates downstream response metadata handling (request IDs, date/server headers), improves HTTP/3 connection-close logging, and fixes HTTPS/H2 upstream dialing so the logical upstream authority is preserved while still connecting to resolved endpoints.

Changes:

  • Switch downstream x-request-id generation to UUIDv7 and update tests accordingly.
  • Add configurable server_header (default rginx) and inject date, server, and x-request-id into downstream responses.
  • Preserve logical upstream authority in proxy URIs while dialing resolved socket endpoints; add HTTP/3 clean-close handling.

Reviewed changes

Copilot reviewed 32 out of 33 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
crates/rginx-runtime/src/health.rs Update tests to populate new Server.server_header.
crates/rginx-runtime/src/bootstrap/shutdown.rs Update tests to populate new Server.server_header.
crates/rginx-runtime/src/bootstrap/listeners.rs Update tests to populate new Server.server_header.
crates/rginx-http/src/transition.rs Update tests to populate new Server.server_header.
crates/rginx-http/src/state/tls_runtime/bindings.rs Update tests to populate new Server.server_header.
crates/rginx-http/src/state/tests.rs Update tests to populate new Server.server_header.
crates/rginx-http/src/state/mod.rs Remove monotonic request-id counter from shared state.
crates/rginx-http/src/state/lifecycle.rs Generate request IDs using UUIDv7.
crates/rginx-http/src/server/http3.rs Treat clean HTTP/3 closes as debug-level and stop warning/error path.
crates/rginx-http/src/proxy/tests/request_headers.rs Add coverage ensuring proxy URI uses logical upstream authority.
crates/rginx-http/src/proxy/tests/mod.rs Update test helper server construction for server_header.
crates/rginx-http/src/proxy/mod.rs Remove unused hyper-rustls ResolveServerName import.
crates/rginx-http/src/proxy/health/registry.rs Update tests for server_header and continue using dial authority for ordering/display.
crates/rginx-http/src/proxy/common.rs Build proxy URI with upstream_authority instead of dial_authority.
crates/rginx-http/src/proxy/clients/tests.rs Update tests to populate new Server.server_header.
crates/rginx-http/src/proxy/clients/mod.rs Dial fixed resolved endpoints while keeping logical authority; cache clients per endpoint socket.
crates/rginx-http/src/handler/tests.rs Update tests for new finalize signature and assert date/server injection.
crates/rginx-http/src/handler/dispatch.rs Inject date/server headers and thread through server_header to final response pipeline.
crates/rginx-http/src/client_ip.rs Update tests to populate new Server.server_header.
crates/rginx-http/Cargo.toml Add uuid dependency for UUIDv7 request IDs.
crates/rginx-core/src/lib.rs Re-export DEFAULT_SERVER_HEADER / default_server_header.
crates/rginx-core/src/config/tests.rs Update tests to set server_header.
crates/rginx-core/src/config.rs Add Server.server_header and default constant/helper.
crates/rginx-config/src/validate/tests.rs Add validation test for empty server_header; update configs for new field.
crates/rginx-config/src/validate/server.rs Validate server_header for legacy server and explicit listeners.
crates/rginx-config/src/model.rs Add server_header to raw config model (server + listener).
crates/rginx-config/src/compile/tests.rs Assert default/custom compiled server_header behavior; update configs for new field.
crates/rginx-config/src/compile/server.rs Compile server_header into runtime HeaderValue with default.
crates/rginx-config/src/compile/mod.rs Pass legacy server’s server_header as default for explicit listeners.
crates/rginx-app/tests/upstream_http2.rs Add integration test ensuring H2 upstream authority is preserved while dialing resolved IP.
crates/rginx-app/tests/phase1.rs Update request-id assertions to UUIDv7 shape.
Cargo.toml Add workspace uuid dependency with v7 feature.
Cargo.lock Lockfile updates for new uuid dependency tree.

💡 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/mod.rs
Comment thread crates/rginx-http/src/handler/dispatch.rs
Comment thread crates/rginx-http/src/server/http3.rs Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0b3346206f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/rginx-http/src/proxy/clients/mod.rs Outdated
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: 1

🧹 Nitpick comments (4)
crates/rginx-app/tests/phase1.rs (1)

268-280: LGTM!

UUIDv7 字段位置(version nibble 在字符位置 14)和长度断言都正确,而且通过 is_ascii_hexdigit 兜底了非连字符的字符校验。

可选的小改进:还可以校验 RFC 4122 variant nibble(位置 19 应在 8/9/a/b),让格式断言更严格:

♻️ 可选加强
     assert_eq!(value.as_bytes()[14], b'7', "generated request id should be UUIDv7");
     assert_eq!(value.as_bytes()[18], b'-');
+    assert!(
+        matches!(value.as_bytes()[19], b'8' | b'9' | b'a' | b'b'),
+        "generated request id should use the RFC 4122 variant, got {value:?}"
+    );
     assert_eq!(value.as_bytes()[23], b'-');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/rginx-app/tests/phase1.rs` around lines 268 - 280, The test helper
assert_generated_request_id currently checks UUIDv7 length and version nibble
(char pos 14) and hex digits; add an assertion to validate the RFC 4122 variant
nibble at character position 19 is one of '8','9','a','b' to tighten the format
check. Locate assert_generated_request_id and after the existing checks for
position 18 add a single assertion that value.as_bytes()[19] is one of those
byte values (or check value.chars().nth(19) against '8','9','a','b') and include
a clear failure message like "UUID variant nibble must be 8/9/a/b".
crates/rginx-http/src/handler/dispatch.rs (1)

298-301: 建议改用 httpdate 生成 HTTP-date,避免在响应热路径上的 chrono 格式化开销。

current_http_date() 在每个下行响应都调用 chrono::Utc::now().format(...).to_string(),产生多次分配并依赖 chrono 的 strftime 引擎。httpdate 是 hyper 生态的标准库,专门按 RFC 7231 IMF-fixdate 格式化,无 locale/strftime 开销。虽然 httpdate 已作为传递依赖在依赖图中可用,建议显式声明以避免隐式依赖问题。

♻️ 建议改写
fn current_http_date() -> HeaderValue {
-    let value = chrono::Utc::now().format("%a, %d %b %Y %H:%M:%S GMT").to_string();
+    let value = httpdate::fmt_http_date(std::time::SystemTime::now());
     HeaderValue::from_str(&value).expect("formatted HTTP date should be a valid header")
}

crates/rginx-http/Cargo.toml[dependencies] 中添加:

httpdate = "1.0"
🤖 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` around lines 298 - 301,
current_http_date() currently uses chrono::Utc::now().format(...).to_string(),
causing allocation and strftime overhead per response; replace that with
httpdate's formatter and add httpdate = "1.0" to crates/rginx-http Cargo.toml
dependencies. Change the function to call
httpdate::fmt_http_date(std::time::SystemTime::now()) (or the equivalent
httpdate API) and pass the resulting string to HeaderValue::from_str(...)
instead of using chrono::Utc::now(), keeping the same expect message.
crates/rginx-http/src/proxy/clients/mod.rs (2)

367-409: 逐 endpoint 构建 client 让 pool_max_idle_per_host 的语义发生隐式变化,建议在文档中说明。

每个已解析 endpoint 都会得到独立的 HyperProxyClient,因此 pool_max_idle_per_host 实际上变成 “每个 endpoint × 每个 host” 的空闲上限。当一个上游解析到 N 个 IP 时,空闲连接预算近似变为 N × pool_max_idle_per_host,与改造前(authority 共用一个 client、N 个 IP 共享同一池)的资源占用模型不同。建议在配置文档或迁移说明里点出这一点,避免现网用户在升级后出现意料之外的连接数上涨。

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

In `@crates/rginx-http/src/proxy/clients/mod.rs` around lines 367 - 409, The
current implementation of build_hyper_client_for_endpoint constructs a separate
HyperProxyClient per resolved endpoint (see function
build_hyper_client_for_endpoint and HttpProxyClient.profile), which makes
profile.pool_max_idle_per_host apply per endpoint rather than per-authority;
document this behavior change (and its capacity impact: effective idle limit ≈ N
× pool_max_idle_per_host when an upstream resolves to N IPs) in the
configuration/upgrade notes and the doc comment for pool_max_idle_per_host
and/or build_hyper_client_for_endpoint, and mention the alternative (sharing a
single client per authority) and recommended tuning guidance for
pool_max_idle_per_host and pool_max_idle_per_host related settings
(pool_idle_timeout, pool_max_idle_per_host) to avoid unexpected connection
growth.

270-287: endpoint_clients 缓存缺少回收策略,长期运行下可能持续增长。

endpoint_clients 仅以 HashMap 形式缓存,任何被 client_for_peer 命中的 SocketAddr 都会保留对应的 HyperProxyClient 及其连接池。在以下场景会出现问题:

  • 上游 DNS 短 TTL 或周期性变更(滚动发布、Kubernetes Service Endpoints 滚动等)持续返回新 IP,旧 IP 对应的 client 永远不会被清理;
  • 多个上游(或同一上游不同 peer)随时间累积大量历史 endpoint。

后果是内存与空闲连接(以及关联的 FD)随运行时间线性增长。建议结合 UpstreamResolver 的快照,在 endpoint 不再被任何 peer 解析结果引用时主动 evict;或退而求其次给该 cache 设置 LRU/TTL 策略,与 pool_idle_timeout 对齐。

♻️ 思路示例(伪代码,非完整 diff)
// 在每次 resolver 完成解析后,基于当前活跃 endpoint 集合做一次差集清理:
fn prune_endpoint_clients(&self, live: &HashSet<SocketAddr>) {
    let mut guard = self.endpoint_clients
        .lock()
        .unwrap_or_else(|p| p.into_inner());
    guard.retain(|addr, _| live.contains(addr));
}

或者用 lru::LruCache<SocketAddr, HyperProxyClient> 替换 HashMap,以 pool_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/mod.rs` around lines 270 - 287, The
endpoint_clients HashMap in HttpProxyClient.client_for_peer lacks any eviction,
causing unbounded growth; add a pruning/eviction strategy: either replace
endpoint_clients with an LRU/TTL cache (e.g. lru::LruCache keyed by SocketAddr
with capacity tied to pool_max_idle_per_host) or implement a
prune_endpoint_clients(&self, live: &HashSet<SocketAddr>) method that acquires
the endpoint_clients lock and retain()s only live addresses after each
UpstreamResolver snapshot update; ensure new clients are still created via
build_hyper_client_for_endpoint and values remain HyperProxyClient, and align
TTL/eviction timing with pool_idle_timeout to avoid keeping idle connections.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Cargo.toml`:
- Line 59: The Cargo.toml dependency for uuid currently enables only the "v7"
feature but uuid::Uuid::now_v7() requires both "v7" and "std"; update the uuid
entry to include features = ["v7", "std"] (or confirm "std" is pulled in
transitively) so Uuid::now_v7() will compile correctly, and if you determine
exposing millisecond timestamps via UUIDv7 (e.g., x-request-id) is unacceptable
for your threat model, switch usages to Uuid::new_v4() instead.

---

Nitpick comments:
In `@crates/rginx-app/tests/phase1.rs`:
- Around line 268-280: The test helper assert_generated_request_id currently
checks UUIDv7 length and version nibble (char pos 14) and hex digits; add an
assertion to validate the RFC 4122 variant nibble at character position 19 is
one of '8','9','a','b' to tighten the format check. Locate
assert_generated_request_id and after the existing checks for position 18 add a
single assertion that value.as_bytes()[19] is one of those byte values (or check
value.chars().nth(19) against '8','9','a','b') and include a clear failure
message like "UUID variant nibble must be 8/9/a/b".

In `@crates/rginx-http/src/handler/dispatch.rs`:
- Around line 298-301: current_http_date() currently uses
chrono::Utc::now().format(...).to_string(), causing allocation and strftime
overhead per response; replace that with httpdate's formatter and add httpdate =
"1.0" to crates/rginx-http Cargo.toml dependencies. Change the function to call
httpdate::fmt_http_date(std::time::SystemTime::now()) (or the equivalent
httpdate API) and pass the resulting string to HeaderValue::from_str(...)
instead of using chrono::Utc::now(), keeping the same expect message.

In `@crates/rginx-http/src/proxy/clients/mod.rs`:
- Around line 367-409: The current implementation of
build_hyper_client_for_endpoint constructs a separate HyperProxyClient per
resolved endpoint (see function build_hyper_client_for_endpoint and
HttpProxyClient.profile), which makes profile.pool_max_idle_per_host apply per
endpoint rather than per-authority; document this behavior change (and its
capacity impact: effective idle limit ≈ N × pool_max_idle_per_host when an
upstream resolves to N IPs) in the configuration/upgrade notes and the doc
comment for pool_max_idle_per_host and/or build_hyper_client_for_endpoint, and
mention the alternative (sharing a single client per authority) and recommended
tuning guidance for pool_max_idle_per_host and pool_max_idle_per_host related
settings (pool_idle_timeout, pool_max_idle_per_host) to avoid unexpected
connection growth.
- Around line 270-287: The endpoint_clients HashMap in
HttpProxyClient.client_for_peer lacks any eviction, causing unbounded growth;
add a pruning/eviction strategy: either replace endpoint_clients with an LRU/TTL
cache (e.g. lru::LruCache keyed by SocketAddr with capacity tied to
pool_max_idle_per_host) or implement a prune_endpoint_clients(&self, live:
&HashSet<SocketAddr>) method that acquires the endpoint_clients lock and
retain()s only live addresses after each UpstreamResolver snapshot update;
ensure new clients are still created via build_hyper_client_for_endpoint and
values remain HyperProxyClient, and align TTL/eviction timing with
pool_idle_timeout to avoid keeping idle connections.
🪄 Autofix (Beta)

❌ Autofix failed (check again to retry)

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: b731a825-560d-4f01-9a68-a4bd04470b49

📥 Commits

Reviewing files that changed from the base of the PR and between a251936 and 0b33462.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (32)
  • Cargo.toml
  • crates/rginx-app/tests/phase1.rs
  • crates/rginx-app/tests/upstream_http2.rs
  • crates/rginx-config/src/compile/mod.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/tests.rs
  • crates/rginx-core/src/lib.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/tests.rs
  • crates/rginx-http/src/proxy/clients/mod.rs
  • crates/rginx-http/src/proxy/clients/tests.rs
  • crates/rginx-http/src/proxy/common.rs
  • crates/rginx-http/src/proxy/health/registry.rs
  • crates/rginx-http/src/proxy/mod.rs
  • crates/rginx-http/src/proxy/tests/mod.rs
  • crates/rginx-http/src/proxy/tests/request_headers.rs
  • crates/rginx-http/src/server/http3.rs
  • crates/rginx-http/src/state/lifecycle.rs
  • crates/rginx-http/src/state/mod.rs
  • crates/rginx-http/src/state/tests.rs
  • crates/rginx-http/src/state/tls_runtime/bindings.rs
  • crates/rginx-http/src/transition.rs
  • crates/rginx-runtime/src/bootstrap/listeners.rs
  • crates/rginx-runtime/src/bootstrap/shutdown.rs
  • crates/rginx-runtime/src/health.rs
💤 Files with no reviewable changes (1)
  • crates/rginx-http/src/state/mod.rs

Comment thread Cargo.toml Outdated
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 25, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Autofix skipped. No unresolved CodeRabbit review comments with fix instructions found.

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-app/tests/phase1.rs (1)

268-284: 测试断言与 UUIDv7 规范一致,LGTM。

assert_generated_request_id 的校验顺序合理:先断言长度为 36,再用字节索引访问位置 8/13/18/23 的连字符以及版本/变体半字节,避免越界;版本位固定为 '7',变体位限制在 8|9|a|b,并对去除连字符后的字符做十六进制校验,与 uuid::Uuid::now_v7() 的小写规范输出相符。

一个可选的小改进(非阻塞):变体半字节校验只接受小写 a|b,依赖于 uuid crate 默认的小写格式;如果将来打印格式发生变化(例如改为 to_string_upperHyphenated 显式大写),该断言会失败。可考虑在比较前对该字节做 to_ascii_lowercase(),让测试对大小写更鲁棒:

♻️ 可选的鲁棒性增强
-    assert!(
-        matches!(value.as_bytes()[19], b'8' | b'9' | b'a' | b'b'),
-        "generated request id should use the RFC 4122 variant, got {value:?}"
-    );
+    assert!(
+        matches!(
+            value.as_bytes()[19].to_ascii_lowercase(),
+            b'8' | b'9' | b'a' | b'b'
+        ),
+        "generated request id should use the RFC 4122 variant, got {value:?}"
+    );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/rginx-app/tests/phase1.rs` around lines 268 - 284, The test's
assert_generated_request_id currently checks the variant nibble byte against
lowercase b'8'|b'9'|b'a'|b'b', which will fail if UUID hex letters are
uppercase; update the check in assert_generated_request_id to normalize that
byte to lowercase (e.g., call to_ascii_lowercase on the byte or otherwise map
A/B to a/b) before matching, so the variant assertion accepts both cases while
keeping the rest of the UUIDv7 checks unchanged.
crates/rginx-http/src/handler/dispatch.rs (1)

298-301: 可选优化:按秒缓存格式化后的 HTTP-date。

每个响应都会执行一次 SystemTime::now() + httpdate::fmt_http_date() + HeaderValue::from_str。HTTP-date 精度只有秒级,在高 QPS 场景下可以把结果缓存到 ArcSwap<HeaderValue>OnceLock,由后台任务(或 AtomicU64 时间戳惰性比较)每秒刷新一次,能省掉热路径上的格式化与 from_str 校验。这是一个在 Hyper 等高性能 HTTP 服务器中已验证的模式。当前实现功能正确,仅作为后续可选改进。

🤖 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` around lines 298 - 301, The
current_http_date function formats a new HTTP-date on every call (calling
SystemTime::now, httpdate::fmt_http_date and HeaderValue::from_str) which is
wasteful at high QPS; change it to return a cached HeaderValue updated at most
once per second: introduce a global cached container (e.g., ArcSwap<HeaderValue>
or OnceLock<(AtomicU64, HeaderValue)>) and a background/periodic update
mechanism or a lazy compare-and-swap that checks the current epoch seconds and
only recomputes and replaces the HeaderValue when the seconds change; update all
callers of current_http_date to read from the cache and ensure the replacement
uses HeaderValue::from_str once per second and handles any parse errors on
refresh (not on hot path) while keeping function signature current_http_date()
-> HeaderValue semantics by cloning or returning an Arc/clone of the cached
HeaderValue.
🤖 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/handler/dispatch.rs`:
- Line 289: 当前代码在 dispatch.rs 里无条件用
response.headers_mut().insert(http::header::DATE, current_http_date()) 覆盖上游的
Date 头,会影响缓存协商;请修改为仅在响应未包含 Date 头时才填充(检查
response.headers().contains_key(http::header::DATE) 或等价方法),保留现有
current_http_date() 生成逻辑并只在缺失时调用/插入,以避免覆盖上游时间戳。

In `@crates/rginx-http/src/server/http3.rs`:
- Around line 796-810: The current string-based fallback in
is_clean_http3_accept_close is brittle; add a unit test that constructs a real
h3::error::ConnectionError coming from a Quinn peer closing with
ApplicationClose code 0 and assert is_clean_http3_accept_close(&error) returns
true so future display-format changes in h3/quinn break the test instead of
reintroducing noisy warnings; locate is_clean_http3_accept_close in http3.rs and
add a test (e.g., in the same module or tests mod) that performs a minimal
Quinn/h3 handshake or simulates the peer ApplicationClose(0) path to produce the
ConnectionError and pass it to is_clean_http3_accept_close.

---

Nitpick comments:
In `@crates/rginx-app/tests/phase1.rs`:
- Around line 268-284: The test's assert_generated_request_id currently checks
the variant nibble byte against lowercase b'8'|b'9'|b'a'|b'b', which will fail
if UUID hex letters are uppercase; update the check in
assert_generated_request_id to normalize that byte to lowercase (e.g., call
to_ascii_lowercase on the byte or otherwise map A/B to a/b) before matching, so
the variant assertion accepts both cases while keeping the rest of the UUIDv7
checks unchanged.

In `@crates/rginx-http/src/handler/dispatch.rs`:
- Around line 298-301: The current_http_date function formats a new HTTP-date on
every call (calling SystemTime::now, httpdate::fmt_http_date and
HeaderValue::from_str) which is wasteful at high QPS; change it to return a
cached HeaderValue updated at most once per second: introduce a global cached
container (e.g., ArcSwap<HeaderValue> or OnceLock<(AtomicU64, HeaderValue)>) and
a background/periodic update mechanism or a lazy compare-and-swap that checks
the current epoch seconds and only recomputes and replaces the HeaderValue when
the seconds change; update all callers of current_http_date to read from the
cache and ensure the replacement uses HeaderValue::from_str once per second and
handles any parse errors on refresh (not on hot path) while keeping function
signature current_http_date() -> HeaderValue semantics by cloning or returning
an Arc/clone of the cached HeaderValue.
🪄 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: 497eb328-e715-4cda-aab1-9d2cdf142fca

📥 Commits

Reviewing files that changed from the base of the PR and between 0b33462 and 0c7bf1f.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • Cargo.toml
  • RELEASE_NOTES_v0.1.3-rc.13.md
  • crates/rginx-app/tests/phase1.rs
  • crates/rginx-http/Cargo.toml
  • crates/rginx-http/src/handler/dispatch.rs
  • crates/rginx-http/src/proxy/clients/mod.rs
  • crates/rginx-http/src/proxy/clients/tests.rs
  • crates/rginx-http/src/server/http3.rs
  • docs/HTTP3_PHASE7_RELEASE.md
  • docs/README.md
💤 Files with no reviewable changes (1)
  • docs/HTTP3_PHASE7_RELEASE.md
✅ Files skipped from review due to trivial changes (2)
  • RELEASE_NOTES_v0.1.3-rc.13.md
  • docs/README.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/rginx-http/Cargo.toml
  • Cargo.toml

Comment thread crates/rginx-http/src/handler/dispatch.rs Outdated
Comment thread crates/rginx-http/src/server/http3.rs
@vansour vansour enabled auto-merge April 25, 2026 16:40
@vansour vansour merged commit 30344a3 into main Apr 25, 2026
6 checks passed
@vansour vansour deleted the fix/response-metadata-http3-upstream-authority branch April 25, 2026 16:43
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