Rust in Everything
基于 hyper 1.12.0

HTTP/1 连接

这是本卷的重点。http1::Builder::serve_connection 返回一个 future,一条连接上的所有请求都在这个 future 里处理,不另开任务。读懂这一部分,就读懂了服务端主线的大半;客户端主线用的也是同一套代码,只换了两个 trait 的实现(Dispatch 和 Http1Transaction)。

七章按「从外到内」排列:先是对外的 builder,然后是驱动一切的循环和它依赖的状态机,再往下是消息头、body、缓冲区三层,最后是离开 HTTP 的那条路。

  1. 服务端 Builder 与 Connection:http1::Builder 的选项和默认值(keep-alive 开、header_read_timeout 默认 30 秒、自动加 Date、half_close 关),serve_connection 把它们写进 Conn、包成 Connection future;graceful_shutdown、without_shutdown / into_parts、with_upgrades 都在这一层。
  2. Dispatcher:poll_loop 每次最多转 16 圈,每圈读、写、刷新各一次,转满就唤醒自己再让出。Dispatch 的服务端实现调服务、存 in_flight;同一条连接上的请求严格串行,pipelining 只省往返。
  3. Conn 的读写状态机:Reading、Writing 两个枚举加一个 KA,读写都到 KeepAlive 且 KA 是 Busy 才一起回到 Init;Connection: close、HTTP/1.0、Expect: 100-continue、对端半关闭都落在这几个状态上。
  4. 解析与编码:role.rs 的 Server 和 Client 用 httparse 解析消息头、定出 body 长度,解析失败自动回 400 / 414 / 431;编码时选 Content-Length 还是 chunked,补上缓存的 Date。
  5. body 的解码与编码:Decoder 的 Length、Chunked、Eof 三种,chunked 是逐字节的状态机;Encoder 的 Length、Chunked、CloseDelimited 三种。
  6. 读写缓冲:Buffered 的读缓冲从 8 KiB 起按读到的量伸缩,上限是 max_buf_size;写缓冲在 IO 支持向量写时用 Queue,否则用 Flatten 拷进一块连续内存。
  7. 协议升级:Upgrade 头或 CONNECT 让请求带上 OnUpgrade;101 写完后 Dispatcher 返回 Upgrade,with_upgrades 把 IO 和多读的字节合成 Upgraded 交出去。
flowchart TB
  builder["Builder 与 Connection"] --> disp["Dispatcher:poll_loop"]
  disp --> dispatch["Dispatch:Server 或 Client"]
  disp --> conn["Conn:读写状态机"]
  conn --> role["role:解析与编码消息头"]
  conn --> codec["Decoder 与 Encoder"]
  conn --> buffered["Buffered:读写缓冲"]
  buffered --> io["rt::Read + rt::Write"]
  disp --> upg["Upgrade:交出 IO"]

Dispatcher 只知道「该推哪一边」,Conn 只知道「哪一边能推」,字节格式全在 role、Decoder、Encoder 里,缓冲区是最底下一层。读的时候可以先读 2、3 两章搭起骨架,再按需下钻。

💬 关联讨论

发起讨论
加载中...