Golang http response 解压缩分析

Golang自带的 http 标准库一直是自己的首选http library,最主要的原因是对标准的细节支持非常到位,兼容性优秀。

前几天遇到一个问题一直没时间处理,晚上花了挺多时间排查,落实到代码上其实也就一行的修改量,但是对于有段时间没有读标准库代码的自己来说倒是值得记录一下。

问题

使用标准库进行 http 请求时,如果在请求 header 中手动设置了 Accept-Encoding, 那么返回的响应内容看起来是乱码(其实是二进制)。

原因

定位的过程其实挺曲折的,分析抓包的时候对比了多个请求数据,最终才确认是header 中的 Accept-Encoding 导致。而这个细节之前在 http.Transport 的 DisableCompression 字段是有文档说明的:

也就是说:

  • 如果设置 DisableCompressiontrue 并且请求中没有设置 Accept-Encoding 的情况下,那么将发送要求服务器返回非压缩的请求;如果 DisableCompressionfalse (默认值), 则会发送允许服务器返回压缩内容的请求,并且会在 transport 层自动把 response 内容解压返回到业务层。这一点很容易理解,屏蔽了解压读取压缩内容细节,使用起来也非常方便。这也是我对golang http 标准库的理解,很多东西都自动处理了。我遇到的问题在于下面说的第二点。
  • 如果用户手动在请求中设置了 Accept-Encoding, 那么标准库会认为业务层是想自己接管 response 内容的处理,因此会返回原始的未解压的内容。也就是上面问题里面提到的乱码数据。

这段话看起来比较绕,我们直接看一下 transport.go 相关代码。

发起请求时,DisableCompressionfalse 且业务层没有设置 Accept-Encoding 的情况下,自动设置header:

读取 response 时,解压内容:

小结

很多问题其实都是魔鬼在细节中,本质上还是基础文档没有仔细阅读。不过找问题的晚上其实也挺开心的,因为通过配置好的 github Copilot 来写代码,整个过程真的非常愉悦。最开始自己还会写几个关键字,引导 Copilot 给出提示代码,后面索性回车就开始等待 Copilot 帮我写代码,跟自己编写代码匹配度能到 80% 以上。目前已知的不足是在 string 的内容推导上有时候不太准确(事实上,人工编码的时候,这部分也容易出现typo或者其他错误),因此要注意这部分代码的review.

而立之年的妥协

生活有时候非常神奇,在特定的时间点会递上特殊的经历。而在自己身上,则是非常戏剧的两年。

去年前的今天,蚂蚁按下了暂停键,当时的自己在楼下看着logo上的霓虹灯,心中毫无波澜,心里想的是今日今岁不唐捐。

今年的今天,我接受了外部的和解,也跟自己做了妥协。我知道我内心并不接受这样的结果。但是,所谓妥协,跟接受和认同无关,只是学会了和解自己,也不再为难他人。

我不知道这两年生活在这两个同一时间点上的经历是礼物还是考验。几天前,脑子里总会浮现出屈原的一些词句,放弃了回忆当时那几句话在课本里面备注的含义,却很自然的理解了那些诗句的感受和含义。

我不知道什么时候才能消解内心深处的焦虑与愤怒,但是我希望自己在越来越车轻路熟处理眼下事情的时候,能保持对自己的持续感受和鞭策。

从今以后,我出发的地方,已无法归来。

可至今日,已是一种眷顾与幸运。

远去,不归。

nginx使用certbot自动更新https免费证书

个人博客的 HTTPS 证书一直使用的是免费的 Let's Encrypt 。证书获取和自动 renew 方案是参考 Jerry Qu 提供的 acme-tiny. 这个方案脚本短小精悍,是自己喜欢的风格。

但前几天发现网站的证书过期了,尝试手动运行更新脚本,发现已经无法运行。想来这个方案已经用了6年多了,索性这次迁移到使用官方的 certbot 方案。简单记录了一下迁移步骤:

https://certbot.eff.org/lets-encrypt/ubuntubionic-other

1. 安装 snapd

需要说明的是 snapd 在 Ubuntu 16.04 及以后版本都是预装的,可以跳过这个步骤。而我的服务器还停留在 14.04, 需要安装手动安装一遍:

apt-get install snapd

If you’re running Ubuntu 16.04 LTS (Xenial Xerus) or later, including Ubuntu 18.04 LTS (Bionic Beaver), Ubuntu 18.10 (Cosmic Cuttlefish) and Ubuntu 19.10 (Eoan Ermine), you don’t need to do anything. Snap is already installed and ready to go.

2. 安装 Certbot

sudo snap install –classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

3. 申请证书

因为服务器上部署了很多服务,有开源的nginx,也有自己编写的web服务,因此我仅使用 certbot 来获取和更新证书即可。证书的使用通过 post hooks 来集成。而我又不想停止我线上的服务,因此,这里使用 certonly 模式来获取证书

sudo certbot certonly –webroot -w /var/www/challenges -d www.liudanking.com -d liudanking.com

需要注意一点,需要映射一下 http 服务 /.well-known/acme-challenge path 与本地 directory 的关系。以nginx为例,可以这样配置:

To use the webroot plugin, your server must be configured to serve files from hidden directories. If /.well-known is treated specially by your webserver configuration, you might need to modify the configuration to ensure that files inside /.well-known/acme-challenge are served by the webserver.

一般,获取的证书会保存到 /etc/letsencrypt/live/DOMAIN_NAME 文件夹下,同时certbot后台也会运行一个定时调度任务,自动更新证书,防止证书过期。

4. 配置后处理钩子脚本

/etc/letsencrypt/renewal-hooks/post 添加后处理脚本renew_post.sh。这里我只需要见到的reload nginx即可:

nginx -s reload

这样,每次更新完证书,最新的证书就可以在nginx中应用生效了。

小结

certbot 整体流程还是比较简单的。但是绑定 snap 的方式不是自己喜欢的风格。考虑到这是 Let's Encrypt 官方推荐的方式,希望能够长期稳定吧。

之前在 Jerry Qu 的网站学习和参考了挺多 HTTPS 的知识和实践经验,挺感谢站长以及他的网站。今天回头看的时候,发现他已经有挺长时间没更新了,心有所触。不知道个人blog时代是不是已经远去不可及,也不知道自己还能坚持,权当是闲暇时候的反观世界时的喃喃自语吧。