连接
请求主线走到 hyper-util 的 legacy Client 时,先去连接池里找一条能用的连接;找不到,才进入这一部分讲的连接主线:reqwest 的 Connector 拿到一个只有 scheme 和 authority 的 Uri,解析主机名,建 TCP,有代理时先打通隧道,是 HTTPS 就做 TLS 握手,最后交回一个 Conn,由池子按 ALPN 的结果决定在上面跑 HTTP/1.1 还是 HTTP/2。
这条线上真正属于 reqwest 的代码不多。TCP 建连和 Happy Eyeballs 在 hyper-util 的 HttpConnector 里,握手在 rustls 或 native-tls 里,连接池也是 hyper-util 的,它们分别在 hyper 卷和 rustls 卷讲过。reqwest 做的是组装和选择:用哪个 resolver、哪个 TLS 后端、哪个 crypto provider、哪个证书校验器、ALPN 报什么、哪个代理拦截这个地址,以及把超时套在哪一层。这些选择几乎都在 ClientBuilder::build 里一次做完,连接时不再变化。
代码分布(按 tag v0.13.5 统计 .rs 行数,含测试和文档注释):src/connect.rs 2139 行,src/tls.rs 922 行,src/dns/ 312 行,src/proxy.rs 948 行;组装它们的代码在 src/async_impl/client.rs 的 ClientBuilder::build 里。
- 连接器:
ConnectorBuilder、ConnectorService、Connector三个类型;connector_layer让用户在外面套 tower 层,层看到的请求类型不可命名,每条新连接才调用一次;Inner的 TLS 分支、Conn带回的代理标记、negotiated_h2和TlsInfo(经 CONNECT 隧道时恒缺);connect_timeout同时约束 hyper-util 里的 TCP 和整个连接器调用。 - DNS:
Resolvetrait 与DynResolver;默认 getaddrinfo 跑在 tokio 的阻塞线程池,hickory-dnsfeature 一打开就成为默认;resolve覆盖的匹配与端口规则;connect_timeout在多个地址之间平分。 - TLS 配置:0.13 默认 rustls 加 aws-lc-rs 和平台校验器,但编进 native-tls 时默认仍是 native-tls;provider 先取进程默认;四种证书校验器与两个 danger 开关各跳过了什么;ALPN 与 HTTP 版本;
tls_backend_preconfigured跳过的配置;实测默认 ClientHello。 - 代理:
Proxy的几种写法和NoProxy规则,系统代理从哪里读,HTTPS 走 CONNECT 隧道、HTTP 写绝对形式的请求行,SOCKS 与代理认证。 - 连接池与 HTTP 版本:reqwest 怎么配置 hyper-util 的连接池,HTTP/2 由 ALPN 决定还是
http2_prior_knowledge指定,HTTP/3 的独立连接池。
flowchart TB
pool["hyper-util 连接池"] -- 没有可用连接 --> conn["Connector"]
conn --> dns["DNS:目标或代理"]
dns --> tcp["TCP"]
tcp --> proxy{"经代理?"}
proxy -- 是 --> tunnel["CONNECT 或 SOCKS"]
proxy -- 否 --> tls["TLS 与 ALPN"]
tunnel --> tls
tls --> c["Conn"]
c --> pool
图里的每个节点都在 build 时配置好:resolver 见 DNS,TLS 见 TLS 配置,代理匹配见 代理,它们怎么被装进 Connector 见 连接器。连接交回以后的复用规则见 连接池与 HTTP 版本;一次请求从头到尾经过这几页的哪些代码,见 一次请求。