流式传输与大请求

模块在 h2 流与 nginx 请求之间做的是流式搬运,不是「先收完再处理」。这一页讲清四条路径的 边界与要调的旋钮。

响应:下行流式 + 背压 ​

项行为
高水位CFD_RESP_HWM = 256 KiB:待写给边缘的字节超过它就暂停读源站
恢复边缘把窗口放开、outbound 排空后继续读
大响应定长、分块(chunked)、静态文件三条路径都逐字节对账通过(2 MiB / 1 MiB 规模)
写失败先排空出缓冲再造新字节(nginx 没设 SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER,OpenSSL 要求 WANT_WRITE 后同指针同长度重试)

请求:上行流式 ​

  • content-length 已知:首个 DATA 到达就注入 nginx,后续分片入队并唤醒读侧,不等传完。
  • 长度未知:走 Transfer-Encoding: chunked 流式注入(0.5.0 起没有「缓冲上限 413」这条路)。
  • 现在没有 CFD_MAX_BUFFERED_BODY 这种整体缓冲上限 —— 限制来自背压预算,见下。

接收侧背压五层 ​

严格对齐 cloudflared 的「按真正交给源站的字节记账」,并按预算暂停 / 淘汰 / 拒新流:

层判据默认指令
第 1 层按「真正交给 nginx 的字节」记全局账cfd_tunnel_stream_bq_hwm = 1m单流高水位
第 2 层按预算暂停cfd_tunnel_global_bq_limit = 64m全局预算
第 3 层停滞淘汰(整窗口零接收才算停滞)cfd_tunnel_stream_stall_window = 60000 ms、stall_min_bq = 512k、stall_ratio = 0(关闭)停滞
第 4 层预算淘汰—预算
第 5 层拒新流cfd_tunnel_global_bq_reject_ratio = 90(%)拒绝线

连接级窗口早还默认 on:这是唯一一处有意偏离 cloudflared 的背压行为(off 一键退回严格 对齐)。取舍见 与 cloudflared 的差异。

内存上界(多隧道时按隧道数相乘):

text
请求体缓冲上界 ≈ N × cfd_tunnel_global_bq_limit      # N = 隧道条数,默认 64m

3 条隧道就是把上界顶到 192 MiB —— 要压住就调小 cfd_tunnel_global_bq_limit。

gzip ​

场景行为
源站 Content-Encoding: gzip透明处理(模块用 zlib,CFD_GZ_OUT_CAP 64 KiB / 块 16 KiB)
源站被 gzip on 动态压缩同上,走 event pipe 的 /gzbuf 路径也覆盖过
源站发了坏 gzip按错误流处理,不把坏字节当正常响应

trailer 与头 ​

  • 请求 trailer:合成后交给 nginx($http_trailer 可见);单 name 上限 CFD_REQ_NAME_MAX = 64 B, trailer 名最多 CFD_REQ_MAX_HDRS = 192 条。
  • 响应 trailer:从 nginx 侧收到后写回 h2 流。

什么时候会看到 502 / 503 ​

状态触发
502远程规则里命中了本模块没有能力的 service(unix: / hello_world / 未知 scheme)且本地没写 inject_listen;或远程配置体/外层 JSON 解不出来
503开了 remote_config 但没有任何可用规则、本地也没写 inject_listen(cloudflared 的「没有 ingress 规则」兜底)
413client_max_body_size 超限(nginx 自己的语义)
RST_STREAM(REFUSED_STREAM)裸 TCP 隧道流(不是 HTTP 请求,没有状态码)

模块文档 · 对齐依据是 cloudflared 的源码与实测行为