Rust in Everything
基于 reqwest 0.13.5

响应与策略

send().await 返回 Response 时,只有响应头到了。body 还在连接上,要靠 bytes、text、json、chunk 或 bytes_stream 一块块拉出来;拉的过程中经过 tower-http 的解压,经过 reqwest 自己包的读超时和总超时,读完了 HTTP/1.1 连接才回到池里。这是响应主线。

另一半是挂在请求上的策略:重定向、重试和 cookie 是 tower 栈里的层(栈的顺序见 执行与 Pending),超时不是层,而是分散在 Pending、响应体包装和连接器三处;最后一切失败都变成同一个 reqwest::Error,原因藏在 source() 链里。

代码分布(tag v0.13.5,.rs 行数含测试和文档注释):src/async_impl/response.rs 508 行,src/async_impl/body.rs 486 行(后半是两个超时包装),src/error.rs 543 行,src/redirect.rs 454 行,src/retry.rs 491 行,src/cookie.rs 292 行。解压在 tower-http 0.6.11 的 decompression 模块里,reqwest 只按 feature 打开算法。

  1. 响应体:五种读法,text 怎么从 Content-Type 和 BOM 选字符集;error_for_status 只看状态码;gzip feature 让 tower-http 的 Decompression 加 Accept-Encoding 并按字节精确匹配 Content-Encoding 解压;body 不读完时 HTTP/1.1 和 HTTP/2 的连接各自怎样。
  2. 超时:timeout、read_timeout、connect_timeout 分别在 PendingRequest、响应体包装和连接器里实现;服务器卡在握手、响应头前、响应体中间三个阶段,各自是谁触发、错误长什么样。
  3. 重定向:redirect::Policy(默认最多 10 次)怎么接到 tower-http 的 FollowRedirect 上,跨域时去掉哪些头。
  4. 重试:0.13 的 retry 模块,默认只重试哪些错误,重试预算按什么范围计。
  5. 错误:Error 是一个指针,Kind 七种;哪些 is_* 只看种类、哪些沿 source() 链找;链上的 hyper、io、rustls、serde_json 错误怎么取出来;URL 里的密钥和 without_url。
  6. cookie:cookie_store 开关、Jar 与 CookieStore trait,cookie 怎么跟着重定向走。
flowchart TB
  send["send().await"] --> head["Response:只有头"]
  head --> read["bytes text json chunk"]
  read --> tt["TotalTimeoutBody"]
  tt --> rt["ReadTimeoutBody"]
  rt --> dec["Decompression 体"]
  dec --> inc["hyper Incoming"]
  inc --> pool["读完:连接回池"]
  read --> err["reqwest::Error"]

图里从上往下是一次读调用穿过的包装,从外到内:总超时、读超时(只在设置了时才有)、解压、hyper 的 Incoming。每一层都可能出错,错误最后都被读方法包成 Kind::Decode,见 错误。

💬 关联讨论

发起讨论
加载中...