我不太清楚这里发生了什么。不久前我做了一个物联网的东西,它构建了如下 HTTP 请求:
POST /update.py HTTP/1.1
Host: iot.example.com
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: [...]
iv=[...]&msg=base64(encrypted_msg)
以及相应的服务器端 python 脚本,它从 POST 数据中提取信息。我最近将防火墙的反向代理从 squid 更改为 HAProxy,突然 Apache 返回HTTP 400
状态,似乎在 IOT 设备有机会发送 POST 数据之前。以下是其中一个交互的 wireshark TCP 流:
POST /update.py HTTP/1.1
Host: iot.example.com
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 100
X-Forwarded-Proto: http
X-Forwarded-For: xx.xx.xx.xx
HTTP/1.1 400 Bad Request
Date: Sat, 17 Jun 2017 22:29:45 GMT
Server: Apache/2.4.25 (FreeBSD) mod_wsgi/4.5.15 Python/2.7
Content-Length: 226
Connection: close
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
</body></html>
iv=En98cormFMA7NO5e-4qh2Q==&msg=ek_JDRJPqPUvNlztUVH6FTtfVVdHgODWMimBcZklos2XntlMOM1RweBjsp5z-zY=
- 注意: 和 之间的所有内容
HTTP/1.1 400 Bad Request
都是</html>
服务器回复,其他所有内容都是服务器正在接收的内容。X-Forwarded* 标头由 HAProxy 设置。据我所知,Squid 没有使用这些
这里真正奇怪的是 Apache 在收到 POST 数据之前就做出了响应。服务器对GET
请求的响应很好,而且我在同一个防火墙后面运行着其他一些 Apache 服务器,它们似乎都正常工作。我能想到的唯一真正的区别是这些 HTTP 请求几乎是硬编码到它们来自的微控制器的,也许它们一开始就不太正确。任何建议都将不胜感激。提前致谢!
答案1
事实证明问题出在我的 HTTP 请求的行尾上。我发送的是 LF 和 CRLF 的混合。RFC2616要求 HTTP/1.1 仅使用 CRLF,但还规定服务器应该实施“容忍条款”。但是,Apache >= 2.4.25 默认向所有格式错误的标头HttpProtocolOptions Strict
返回HTTP/1.1 400
状态。
FreeBSD 自 12 月起就已打包了 Apache 2.4.25,但我认为 Squid 正在为我规范标头,因为当时它没有出现故障。我猜 HAProxy 更加自由,因此从 Squid 到 HAProxy 的更改使服务器暴露于我的旧固件错误。
正确的解决方法是使用修补过的固件重新刷新微控制器,但这很麻烦。一个快速而粗略的解决方法是告诉 Apache 放松。添加到HttpProtocolOptions Unsafe
是httpd.conf
一个快速“修复”,至少现在数据记录可以恢复运行。