性能文章>一次 HTTP/2 通信失败的问题分析>

一次 HTTP/2 通信失败的问题分析原创

1947612

背景

某业务上线 HTTP/2 以后,通过 curl 访问某接口一直失败。

业务上线HTTP/2 curl某接口一直失败 - HTTP/2 通信失败的问题分析 - HeapDump性能社区

开发人员怀疑可能是运维的 HTTP/2 配置不当导致访问失败,但是同样是配置 HTTP/2 的其它域名却是正常的,于是来一起看了一下这个问题。

排查

排查第一步:遇事不决先抓包,在没有任何先决信息的情况下,先抓包,看看传输了一些啥。因为 HTTP/2 要求通过 HTTPS 通信,所以这里抓包,还需要用到 wireshark 抓取 HTTPS 包的一些技巧。至于这么做,我在之前的 B 站分享有讲过,大家如果感兴趣可以看看。地址在这里:Wireshark 抓取 HTTPS 流量的 N 种方法「 https://www.bilibili.com/video/BV1ur4y1Y7NB 」

抓取 HTTPS 的包

简单来说,就是通过导出 premaster-secret 来帮助 wireshark 解密数据。wireshark ssl keylog 格式长啥样,具体的定义在 https://github.com/boundary/wireshark/blob/master/epan/dissectors/packet-ssl-utils.c#L4183

wireshark ssl keylog 格式 - HTTP/2 通信失败的问题分析 - HeapDump性能社区
wireshark ssl keylog 格式

curl 要做的就是把 key 打印到文件里,打开 SSLKEYLOGFILE 这部分的源码在:https://github.com/curl/ lib/vtls/keylog.c ,如下图所示。

打开 SSLKEYLOGFILE 的源码 - HTTP/2 通信失败的问题分析 - HeapDump性能社区
curl 源码

对于 curl 而言,我们只需要指定一个环境变量 sslkeylogfile 就可以了,抓取的包我们就可以解密出来了。

export SSLKEYLOGFILE=/Users/arthur/keylog.txt

通过 wireshark 对 sslkeylogfile 解密出来的结果如下:

通过 wireshark 对 sslkeylogfile 解密结果 - HTTP/2 通信失败的问题分析 - HeapDump性能社区
wireshark 解密结果

看起来就是 HTTP2 服务端的问题发了一个错误的包导致客户端回了 rst 帧。

接下来继续看 HTTP/2 服务端回复了什么。通过查看包,果然发现了一些有意思的。

HTTP/2 服务端回复包 - HTTP/2 通信失败的问题分析 - HeapDump性能社区
wireshark 包结果

expires 头部后面多了一个空格,其它的 header 都没有。

通过跟业务确认,确实如此,expires 后面多了一个空格,去掉以后马上访问正常了。

expires 后面多了一个空格去掉以后马上访问正常了 - HTTP/2 通信失败的问题分析 - HeapDump性能社区
Expires 头

在 HTTP/1.1 时代,curl 是合法的,没有问题,在 HTTP/2 中,这里就有问题了。

当然这依然不能直接证明就是这个原因,除非 curl 亲自告诉我。

进一步分析

为什么有空格会出现问题呢?当然要从 curl 的底层去分析,curl 的 HTTP/2 底层是用 nghttp 这个库来实现的,nghttp 本来也可以通过命令行直接发起请求。

使用 nghttp 访问一下,印证了我们的想法。

nghttp 访问结果 - HTTP/2 通信失败的问题分析 - HeapDump性能社区
nghttp 访问结果

探究源码

nghttp 是一个开源项目,可以很方面的把源码 clone 下来编译本地调试,发现他在处理 header 的时候会判定 header 是否合法

nghttp在处理 header 的时候会判定 header 是否合法 - HTTP/2 通信失败的问题分析 - HeapDump性能社区
nghttp在处理 header 的时候会判定 header 是否合法 - HTTP/2 通信失败的问题分析 - HeapDump性能社区

合法非法的 ASCII 字符在这里定义

合法非法的 ASCII 字符定义 - HTTP/2 通信失败的问题分析 - HeapDump性能社区

可以看到空格,也就是下图中的 SPC,ASCII 码值是:32(0x20),对应的 VALID 为 0,表示空格是非法的 header 字符。

通过 GDB 同步确认这一点

GDB 调试 - HTTP/2 通信失败的问题分析 - HeapDump性能社区
GDB 调试

至此,我们就知道了为什么 curl 在处理带有空格头部时的问题,chrome、safari 也有类似的问题,大家感兴趣可以看看。

 

相关阅读

HTTP/2 简介

请先登录,查看6条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

LONG究竟有多长,从皇帝的新衣到海康SDK
转眼之间初中毕业30年了,但我仍清楚的记得初中英语的一篇课文,题目叫《皇帝的新装》(“The king’s new clothes”)。这篇课文的前两句话是:”Long long ago, there
使用虚线程进行同步网络 IO 的不阻塞原理
使用虚线程进行网络 IOProject Loom 主要目标是在 Java 平台上提供一种易于使用、高吞吐量的轻量级并发性和新的编程模型的 JVM 特性和API。这带来了许多有趣和令人兴奋的前景,其中之
虚拟机中GUEST OS时钟(TIMEKEEP)问题的探讨
操作系统的时钟处理按理来说应该是个早已成熟的技术,不必再费口舌讨论什么。事实也的确如此。然而在虚拟环境下(不仅仅是xen,vmware这些虚拟机),对时钟的处理可绝非轻而易举,如果你耐心看看你虚拟环境
分享一次排查CLOSE_WAIT过多的经验
作者:踩刀诗人原文链接:https://www.cnblogs.com/chopper-poet/p/14618391.html 问题背景某日下午有测试人员急匆匆的跑来跟我反馈:“有客户反馈供应商附件
一次 HTTP/2 通信失败的问题分析
背景某业务上线 HTTP/2 以后,通过 curl 访问某接口一直失败。开发人员怀疑可能是运维的 HTTP/2 配置不当导致访问失败,但是同样是配置 HTTP/2 的其它域名却是正常的,于是来一起看了一下这个问题。
Redis 突然变慢了如何排查并解决?
Redis 通常是我们业务系统中一个重要的组件,比如:缓存、账号登录信息、排行榜等。一旦 Redis 请求延迟增加,可能就会导致业务系统“雪崩”。我在单身红娘婚恋类型互联网公司工作,在双十一推出下单就送女朋友的活动。谁曾想,凌晨 12 点之后,用户量暴增,出现了
一次 Redis Unexpected end of stream 的除错分析记录
搬运一篇早期的文章这几天线上出现了偶发性jedis的异常,堆栈如下redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream. at redis.clients.util
Kube-OVN:容器网络性能的自我救赎
本文围绕着打造Kube-OVN的过程中的网络性能问题展开,详细阐述了Kube-OVN怎样进行性能瓶颈分析、怎样设置性能优化测试思路,测试过程,以及令人较为满意的测试结果。在性能“自救”的同时,