keepalive timer also closes a freshly dialed pooled connection during the HTTP/1.1 TLS upgrade

未关闭
#938 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
58/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
erlang

调研方向

Start with the fresh-dial path in hackney_pool.erl:573-587 and 1005-1024, then trace connected and upgrade_to_ssl in hackney_conn.erl:845-869 and 996-1013. Compare the HTTP/1.1 and HTTP/2 upgrade branches, and run the pool-enabled HTTP/1 TLS-upgrade tests with a delayed handshake; done means a fresh connection is not closed by the keepalive timer during that handshake.

由索引模型根据 Issue 内容生成。

描述

hackney 4.7.4 (also current master).

#934 disarms the keepalive timer in the is_ready probe, which covers a reused pooled connection. A fresh dial does not reach is_ready:

  • handle_call({checkout, ...}) takes the none -> start_connection branch (hackney_pool.erl:573-587).
  • The new conn arms idle_timeout = keepalive_timeout on entry to connected (hackney_conn.erl:845-869). keepalive_timeout is capped at 2000 ms by min/2 in hackney_pool.erl:476-482 (an infinity value also collapses to 2000 because of Erlang term order), and start_connection/6 does not take a per-checkout override (hackney_pool.erl:1005-1024).
  • The HTTP/1.1 branch of upgrade_to_ssl returns keep_state and leaves that state_timeout running (hackney_conn.erl:1012-1013). The HTTP/2 branch cancels it via init_h2_after_upgrade (:2995-3007).
  • ssl:connect/3 at :996 blocks the gen_statem. If the TLS handshake takes longer than the remaining timer, the timer fires (:915-917), the conn goes to closed, and the caller's {request, ...} hits the catch-all and gets {error, invalid_state} (:2005-2006 on 4.7.4; {error, closed} on master after #936). No byte of the request has been written at that point.

Reproduced with a TCP proxy in front of api.stripe.com that delays the ClientHello: a 1900 ms delay succeeds, a 2200 ms delay fails with invalid_state, with pool enabled and protocols: [http1]. With prewarm_count: 0 the fresh-dial path is the only path, so the failure rate is simply the fraction of handshakes slower than ~2 s.

Suggested fix: arm the idle timer in connected(enter) only when owner =:= pool_pid (prewarm conns set owner => PoolPid at hackney_pool.erl:1367; fresh-dial conns set owner => Requester at :1024), cancel it in set_owner when the new owner is not the pool, and add the HTTP/2 branch's CancelIdle action to the HTTP/1.1 branch of upgrade_to_ssl.

Related: #916 (stuck in upgrade_to_ssl), #922 (handshake bounded by connect_timeout), #934, #936.

主要语言
Erlang
星标
1.4k
派生
458
平均合并
11 小时 32 分钟
30 天内合并 PR
47

环境准备

  • 没有 Dockerfile 或 Docker Compose 文件
  • 没有 Pull Request 模板
  • 阅读贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

benoitc/hackney 的其他 Issue

查看 benoitc/hackney 的全部 Issue

相似的 Issue

更多 Backend & API Design Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。