You need to enable JavaScript to run this app. Firefox接收Kvarn Web服务器HTTP/2推送时无响应问题求助
优惠活动
大模型
产品
解决方案
定价
更多

Firefox接收Kvarn Web服务器HTTP/2推送时无响应问题求助

我正在开发一款名为Kvarn的Web服务器,它支持HTTP/2协议,我计划将其打造为HTTP Push领域的领先实现。

目前该服务器已托管了kvarn.org和icelk.dev两个站点,你可以自行访问测试。

发送推送请求的相关代码可查看此处。

最近我遇到了一个问题:我实现的推送功能在Firefox上无法正常工作,看起来推送的资源流没有正常关闭,Firefox开发者工具显示对应资源尚未收到服务器返回的首字节。

我的技术栈为Rust + Tokio,使用H2库实现HTTP/2协议。

该功能在Chromium系浏览器和Safari上完全正常,我记得在早前几个版本的Firefox上也能正常运行,我用Tor Browser测试也能正常工作,这点十分奇怪,因为Tor Browser是基于Firefox构建的。

我找到了另一款支持HTTP Push的站点,是Go语言团队提供的测试站点,它在Firefox上可以正常使用HTTP/2推送功能。

之后我使用nghttp工具对Kvarn进行了测试,测试结果如下:

[ERROR] Could not connect to the address ::1
Trying next address 127.0.0.1
[  0.013] Connected
[WARNING] Certificate verification failed: Hostname mismatch
The negotiated protocol: h2
[  0.035] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
          (niv=2)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[  0.035] send PRIORITY frame <length=5, flags=0x00, stream_id=3>
          (dep_stream_id=0, weight=201, exclusive=0)
[  0.035] send PRIORITY frame <length=5, flags=0x00, stream_id=5>
          (dep_stream_id=0, weight=101, exclusive=0)
[  0.035] send PRIORITY frame <length=5, flags=0x00, stream_id=7>
          (dep_stream_id=0, weight=1, exclusive=0)
[  0.035] send PRIORITY frame <length=5, flags=0x00, stream_id=9>
          (dep_stream_id=7, weight=1, exclusive=0)
[  0.035] send PRIORITY frame <length=5, flags=0x00, stream_id=11>
          (dep_stream_id=3, weight=1, exclusive=0)
[  0.035] send HEADERS frame <length=38, flags=0x25, stream_id=13>
          ; END_STREAM | END_HEADERS | PRIORITY
          (padlen=0, dep_stream_id=11, weight=16, exclusive=0)
          ; Open new stream
          :method: GET
          :path: /
          :scheme: https
          :authority: localhost:8443
          accept: */*
          accept-encoding: gzip, deflate
          user-agent: nghttp2/1.43.0
[  0.040] recv SETTINGS frame <length=0, flags=0x00, stream_id=0>
          (niv=0)
[  0.040] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
          ; ACK
          (niv=0)
[  0.043] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
          ; ACK
          (niv=0)
[  0.072] recv (stream_id=13) :status: 200
[  0.072] recv (stream_id=13) cache-control: no-store
[  0.072] recv (stream_id=13) server: Kvarn/0.2.0 (macOS)
[  0.072] recv (stream_id=13) content-type: text/html; charset=utf-8
[  0.072] recv (stream_id=13) content-encoding: gzip
[  0.072] recv (stream_id=13) accept-ranges: bytes
[  0.072] recv (stream_id=13) referrer-policy: no-referrer
[  0.073] recv HEADERS frame <length=79, flags=0x04, stream_id=13>
          ; END_HEADERS
          (padlen=0)
          ; First response header
[  0.073] recv DATA frame <length=2349, flags=0x00, stream_id=13>
[  0.073] recv (stream_id=13) :method: GET
[  0.073] recv (stream_id=13) :scheme: https
[  0.073] recv (stream_id=13) :authority: localhost:8443
[  0.073] recv (stream_id=13) :path: /theme.css
[  0.073] recv (stream_id=13) accept-encoding: gzip, deflate
[  0.073] recv PUSH_PROMISE frame <length=28, flags=0x04, stream_id=13>
          ; END_HEADERS
          (padlen=0, promised_stream_id=2)
[  0.073] recv (stream_id=2) :status: 200
[  0.073] recv (stream_id=2) cache-control: no-store
[  0.073] recv (stream_id=2) server: Kvarn/0.2.0 (macOS)
[  0.073] recv (stream_id=2) content-type: text/css; charset=utf-8
[  0.073] recv (stream_id=2) content-encoding: gzip
[  0.073] recv (stream_id=2) accept-ranges: bytes
[  0.073] recv (stream_id=2) referrer-policy: no-referrer
[  0.073] recv HEADERS frame <length=25, flags=0x04, stream_id=2>
          ; END_HEADERS
          (padlen=0)
          ; First push response header
[  0.073] recv (stream_id=13) :method: GET
[  0.073] recv (stream_id=13) :scheme: https
[  0.073] recv (stream_id=13) :authority: localhost:8443
[  0.073] recv (stream_id=13) :path: /style.css
[  0.073] recv (stream_id=13) accept-encoding: gzip, deflate
[  0.073] recv PUSH_PROMISE frame <length=17, flags=0x04, stream_id=13>
          ; END_HEADERS
          (padlen=0, promised_stream_id=4)
[  0.073] recv DATA frame <length=506, flags=0x01, stream_id=2>
          ; END_STREAM
[  0.073] recv (stream_id=4) :status: 200
[  0.073] recv (stream_id=4) cache-control: no-store
[  0.073] recv (stream_id=4) server: Kvarn/0.2.0 (macOS)
[  0.073] recv (stream_id=4) content-type: text/css; charset=utf-8
[  0.073] recv (stream_id=4) content-encoding: gzip
[  0.073] recv (stream_id=4) accept-ranges: bytes
[  0.073] recv (stream_id=4) referrer-policy: no-referrer
[  0.073] recv HEADERS frame <length=7, flags=0x04, stream_id=4>
          ; END_HEADERS
          (padlen=0)
          ; First push response header
[  0.073] recv (stream_id=13) :method: GET
[  0.073] recv (stream_id=13) :scheme: https
[  0.073] recv (stream_id=13) :authority: localhost:8443
[  0.073] recv (stream_id=13) :path: /script.js
[  0.073] recv (stream_id=13) accept-encoding: gzip, deflate
[  0.073] recv PUSH_PROMISE frame <length=17, flags=0x04, stream_id=13>
          ; END_HEADERS
          (padlen=0, promised_stream_id=6)
[  0.073] recv DATA frame <length=2323, flags=0x01, stream_id=4>
          ; END_STREAM
[  0.073] recv (stream_id=6) :status: 200
[  0.073] recv (stream_id=6) cache-control: no-store
[  0.073] recv (stream_id=6) server: Kvarn/0.2.0 (macOS)
[  0.073] recv (stream_id=6) content-type: application/javascript; charset=utf-8
[  0.073] recv (stream_id=6) content-encoding: gzip
[  0.073] recv (stream_id=6) accept-ranges: bytes
[  0.073] recv (stream_id=6) referrer-policy: no-referrer
[  0.073] recv HEADERS frame <length=34, flags=0x04, stream_id=6>
          ; END_HEADERS
          (padlen=0)
          ; First push response header
[  0.073] recv DATA frame <length=0, flags=0x01, stream_id=13>
          ; END_STREAM
[  0.073] recv DATA frame <length=1717, flags=0x01, stream_id=6>
          ; END_STREAM
[  0.074] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
          (last_stream_id=6, error_code=NO_ERROR(0x00), opaque_data(0)=[])

测试日志看起来没有问题,和Go示例站点的同场景测试返回的数据帧结构完全一致。

之前我提到看起来像是流没有关闭,我也做了验证:在我认为流已经关闭后尝试再次向流写入数据,确实触发了H2库的报错,说明流已经正常关闭。

最后我还使用h2spec工具对实现进行了测试,所有测试项全部通过,而Go的示例站点并没有全部通过测试。

我现在已经找不到可能的问题点了,提前感谢大家提供的任何帮助,非常感激。

内容的提问来源于stack exchange,提问作者Icelk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.18 19:06:03