Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XHTTP Client: Race Dialer #4320

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open

XHTTP Client: Race Dialer #4320

wants to merge 3 commits into from

Conversation

rPDmYQ
Copy link
Contributor

@rPDmYQ rPDmYQ commented Jan 23, 2025

Add Race dialer for XHTTP:

  • Enable by specifying both h3 and h2 in ALPN.
    • QUIC connection uses only h3 as ALPN.
    • TCP TLS connection uses everything else as ALPN.
  • Try connecting to server using both h3 and h2 connection. The first connection that succeed dialing is used.
  • If there's already established connection, it will be reused.
  • Chrome-like try logic:
    • Delay initial h2 attempt to prefer h3.
    • H3 gets broken mark and won't be attempted for a while upon failure.

@Fangliding
Copy link
Member

看起来不错 不过有点点小问题

对不同的host进行了等待防止多次race 这很好 但是xhttp中的单个client不会有其他目标 所以对不同的host等待是没有必要的
又还有 这个等待压根不会生效 因为这个检查是client级别的 xhttp在连接的初始化阶段会创建多个client 相互之间不共享这个信息 你可能需要创建一个全局变量存放race结果

这样复杂的功能最好有个test(好像有点难写)

最后有俩 type switch 虽然知道是安全的但还是写了panic 一个小建议改成 fmt.Sprintf("unreachable: unexpected response type %T", value) 之类的输出具体类型

@rPDmYQ
Copy link
Contributor Author

rPDmYQ commented Jan 24, 2025

xhttp中的单个client不会有其他目标

You are right that each raceTransport only connects to one endpoint and there can only have one inflight race per transport.

这个等待压根不会生效

The race wait does happen for stream-up and packet-up if there's no separated downloadSettings. Requests of both directions are handled by the one http client (therefore transport).

@Fangliding
Copy link
Member

我的表达可能有点问题 stream up的两个请求和packet up的多个请求确实会在一个客户端 这个等待部分有用 但是假如有多个请求同时进入核心会涉及多个client 这个仍会多次race 不过如果觉得这是正常倒也说得过去

@rPDmYQ
Copy link
Contributor Author

rPDmYQ commented Jan 24, 2025

Multiple clients will create multiple connections. Since they don't share connection, we should let them start race together - otherwise we would waste 1-RTT for all but one connections just to know the race winner.

And Chrome's default parameters are really trying to avoid real racing. H3 handshake uses only 1-RTT, so > 300ms means

  • this is a ready crappy server / crappy route
  • the network is dropping UDP packets

The h3EndpointCatalog doing statistics on H3 connections is global.

@RPRX
Copy link
Member

RPRX commented Jan 25, 2025

感谢 PR,为了防止损坏现有功能,我倾向于这个 PR 放下个版本之后

@rPDmYQ
Copy link
Contributor Author

rPDmYQ commented Feb 15, 2025

Fixed a bug that request being raced would fail, due to failed roundtrip attempts closing the request body.

I also observed some special Firewall behavior against HTTP3: the QUIC handshake packets can pass through, but data packets with h3 payload are dropped. I'm trying to detect and mark h3 as broken for this situation.
Unfortunately this means from the perspective of proxy.Outbound.Process(), dialer.Dial() is successful but getResponse() fails. Retry happens only on dial phase so the one unfortunate request would fail. But at least follow-up requests will avoid h3 and proceed with h2.

放下个版本之后

@RPRX Do you plan to give this feature a try in the next release after v25.1.30, or some release in further future?

@RPRX
Copy link
Member

RPRX commented Feb 21, 2025

首先我不确定服务端能否很好地处理这种情况,不过如果用不同的 sessionId 应该可以

其次就是我不太确定这个东西是否实用,因为对于 XHTTP 首包延迟是无关紧要的,运营商有没有 Q UDP 才是最重要的

就是说后面的请求都是 0-RTT,延迟都差不多,而带宽能跑到多少更影响使用体验,这个目前由用户自己的体感决定

@rPDmYQ
Copy link
Contributor Author

rPDmYQ commented Feb 21, 2025

My main motivation to implement this is not to select the better connection, but to mimic the normal client behavior when connecting to an H3 enabled site.

  • "Serving H3 only" does not sound like something a normal website would do.
  • Similarly, "connects only over H3" does not sound like something a normal browser would do.

Therefore, advanced firewalls can temporarily block UDP QUIC traffic and see if a TCP TLS connection to the same endpoint will be established soon. If not then the firewall can quite confidently say this is not normal traffic.

@RPRX
Copy link
Member

RPRX commented Feb 21, 2025

我觉得在这方面确实会更像,但也会导致带宽不可控,因为有些运营商 Q UDP,或许本就想用 QUIC 的更适合用这个?

@rPDmYQ
Copy link
Contributor Author

rPDmYQ commented Feb 23, 2025

The current logic really prefers QUIC such that I don't expect H2 to be even attempted, let along selected, for a network that allows QUIC to properly function. (H2 will only be attempted if QUIC handshake is not finished in 1.5x the past average, or 300ms for the first time.)

Therefore I don't worry about "bandwidth being uncontrollable" because you'll get H3 if H3 works. And if you don't want H3, don't enable H3.

Instead, the choices are: for UDP-blocked network, do you want, A. no connection, or B. some connection? Only if the user want choice A should they choose H3 only. And based on my previous comment, this incurs the risk to be detected by some proactive firewall.

I would instead argue that enabling race dialer with h3, h2, http/1.1 is the only recommended way to use H3 based XHTTP, and is the strength of XHTTP against other QUIC only protocols.

@RPRX
Copy link
Member

RPRX commented Feb 23, 2025

其实现在的 quic-go 都不是 Chrome 指纹

@RPRX
Copy link
Member

RPRX commented Feb 25, 2025

我又想了一下,如果代码里判断的是谁先完成 TLS 握手,则不涉及 XHTTP session id 的问题,但我直觉是代码似乎写得过于复杂?如果我要写这个的话分别挂个 http trace 即可

还有现在比较大的问题是 quic-go 并非 Chrome 指纹,这个 PR 只能起到类似 balancer leastPing 的效果

@rPDmYQ
Copy link
Contributor Author

rPDmYQ commented Feb 27, 2025

如果我要写这个的话分别挂个 http trace 即可

There are some subtleties that cannot be implemented with httptrace context:

  • "Detect QUIC blackhole after handshake complete" need some hooks on quic connection level that can't reach from http level API.
  • http2.RoundTripOpt{OnlyCachedConn: true} had never worked as documented since its introduction, so I have to insert the guard logic in the dialer.

quic-go 并非 Chrome 指纹

Hope uQuic or similar mitigation could land in foreseeable future.

只能起到类似 balancer leastPing 的效果

At least for users that frequently switch between different networks and prefers H3, this would use H3 whenever possible while automatically fallback to H2 - So I no longer need to switch between configurations manually.

Though being "race", please note that raceDialer is not selecting connection with less RTT - H2 having lower RTT does not make it favorable. Practically, H3 is always used unless it doesn't work.

@RPRX
Copy link
Member

RPRX commented Mar 2, 2025

我是觉得如果预期就是使用 H3,能更像 Chrome 当然更好,但是现在 quic-go 非 Chrome 指纹,现在合的话反而会比较奇怪

所以应该会等到我们有 Chrome 指纹的 QUIC 后再考虑这个,如果有针对 XHTTP 其它方面的想法,欢迎 PR

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.

3 participants