net/http HTTP服务器完全自主地处理某些格式错误的HTTP请求,而不会将控制权传递给非库代码。这意味着应用程序永远不会意识到它们,因此无法采取任何行动,例如记录收到了格式错误的请求。它还阻止应用程序自定义响应。
例如,在localhost:8000上运行一个最小的Go HTTP服务器并执行以下操作:
curl -v "http://localhost:8000/foo bar"
结果是
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /foo bar HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.55.1
> Accept: */*
>
< HTTP/1.1 400 Bad Request
< Content-Type: text/plain; charset=utf-8
< Connection: close
<
* Closing connection 0
400 Bad Request
(错误是由于请求的第一行中的空格引起的 - 解析器天真地在空格处拆分并期望[method, URI, protocol])。
这是一个问题,因为:
- 应用程序无法记录格式错误的请求。
- 我不能保证“我的API总是返回JSON”,因为“400 Bad Request”不是有效的JSON。
- 错误消息对最终用户几乎没有帮助,当最终用户联系我寻求支持时,由于无法记录请求,我在诊断问题时一无所知。
- Nginx中的某些proxy_pass指令会对请求URI进行静默(可能意外的)URL解码。因此,如果您在Nginx反向代理后面运行Go HTTP服务器(当然是一个常见的场景),上面的错误将由一个看起来正确的请求触发,其中某个地方包含%20。
我建议在http.Server中添加一个导出的类型为
func(error, http.ResponseWriter, []byte)
的变量。应用程序可以在调用ListenAndServe或ListenAndServeTLS之前设置它。如果它是nil(默认值),则保持当前行为。如果它不是nil,那么当服务器遇到无法传递给处理程序的格式错误的请求时,它会被调用(带有错误、responseWriter以及可选的字节切片,其中包含请求中的错误部分 - 甚至全部?)。
9条答案
按热度按时间0lvr5msh1#
CC: @bradfitz
qoefvg9y2#
相关bug:#14952(net/http:为接受/发送错误的HTTP添加开关?)
签名
func(error, http.ResponseWriter, []byte)
有点奇怪,尤其是最后一个参数。如果格式错误,那意味着我们并不真正理解它,所以不清楚我们甚至知道哪里出了问题。这与HTTP/2有什么关系?给你整个二进制帧吗?还是这是一个仅适用于HTTP/1的事物,认为HTTP/2请求都是格式正确的,因为它们在多年内还没有退化?我认为我可以添加一些针对格式错误的HTTP/1请求的特定钩子,但我不认为我想涉及到ResponseWriter。我更愿意只提供
net.Conn
和消耗的字节。它可能看起来更像Hijacker
接口。例如:“对于错误的HTTP/1.x请求,连接都归你所有......玩得开心!”hpxqektj3#
如果有人想沿着这些方向制作一个原型并发送CL,我们可以看一下。
k5ifujac4#
关于签名,想法是将控制权交给应用程序去做它想做的事情。它可能会丢弃[]byte(如果,正如你所说,它不知道如何处理格式错误的请求),但为什么不让应用程序决定请求是否如此糟糕以至于无法处理?例如,在我最初给出的“URL中的字面空间”示例中,可以想象应用程序代码实际上可以拯救请求。
我无法评论这将如何与HTTP/2交互,因为我对HTTP/2了解不够(即任何事情,真的)。
对我来说,
Hijacker
风格的解决方案听起来很好。我很乐意敲定初稿并将其发送以供审查。cwxwcias5#
可以想象,应用程序代码实际上可以拯救请求。
也许吧,但你将无法将其传递回
net/http
以继续使用。但是,是的,你可能能够返回一个客户端满意的响应,然后关闭TCP连接。如果你对此感到满意的话。xmjla07d6#
是的,我正是这个意思——当一个格式错误的请求进来时,钩子会被调用,从那时起,它就完全由应用程序来处理了——如果它想要拯救这个请求,它就必须自己完成所有的头部解析等等。
我预计实际使用案例只是简单地用400 Bad Request响应,但比直接在线上放"400 Bad Request"更具有可定制性。还有日志记录。
mbzjlibv7#
https://golang.org/cl/111695提到了这个问题:
net/http: add a malformed HTTP request callback to http.Server
ndasle7k8#
已提交的提议更改。这是一种比我们之前讨论的设计更简单的设计-它执行了最低限度的要求,以便让应用程序在处理格式错误的请求时有合理的发言权。
wnrlj8wa9#
在这里补充一下:
一个用户坚持认为无法抑制这个问题是一个安全漏洞,因为当他们允许用户手动配置URL时,这些用户可以指定实际上不是HTTP端点的URL,导致类似
net/http: HTTP/1.x transport connection broken: malformed HTTP response \"SSH-2.0-OpenSSH_7.4\"
这样的URL。这使得他们允许的用户能够构造泄露给他们关于本地和/或远程系统的信息的URL。提议的更改似乎只影响到
http.Server
。解决这个用户的担忧需要能够为每个请求和/或每个客户端以及全局指定这样一个处理器。