最近业余时间用 Go 写一个 API 测试工具时, 遇到一个很奇怪的问题。处理请求返回内容的时候, 遇到error malformed HTTP response "<html>"
.
搜了下 Go 的标准库代码,发现是 net/http
ReadResponse
函数里面返回的错误。里面涉及到的代码是这样的:
1 | // Parse the first line of the response. |
其意思大概是,解析response的第一行,如果不合法,就返回 malformed HTTP response xxx
。 按理说,正常的response 第一行,应该是这样的
1 | HTTP/1.1 200 OK |
而且返回的内容是提到了 <html>
。 这是不是意味着没有返回header, 返回内容才被判定为畸形(malformed)的response。
抓包试试, 果然没有HTTP Header, 且返回的正文是 Nginx 返回的。
1 | <html> |
因为我访问的服务是从Nginx proxy pass过去的,所以请求并没有转发到后台服务去,而是在 Nginx 这层就被拦截了。为何拦截,我百思不得其解。
不过有一点是明白的, 我发的请求不合法,被Nginx拦截了,不然也不会 400 bad request了。 一开始我以为是Header不合法, 就逐一删掉Header(如何修改请求?请继续阅读),发现并不是Header的问题。 后来请教了同事,他说会不会是请求的Method有问题呢? 我看了下, 好着呢!
1 | get /get HTTP/1.1 |
其实不然, 这里就是HTTP Method 出了问题。赶紧查阅RFC, 发现真是我搞错了。
相关文档如下:
1 | The Method token indicates the method to be performed on the |
坑爹啊! The method is case-sensitive.. 这就是不熟读文档的后果!有时候确实看到有些工具的请求Method是小写的,但是不代表实际上就是这样,也许程序里面做了转换。
Tips:
抓包工具用了 Charles。我记得 Charles 并不会自动自动捕获HTTP报文,需要自己设置代理。由于 Go 的 net/http
包支持代理,所以实现起码并不难。
1 | tr := &http.Transport{ |
http.ProxyFromEnvironment
说明是使用系统环境变量里面的设定。
ProxyFromEnvironment returns the URL of the proxy to use for a
given request, as indicated by the environment variables
HTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the lowercase versions
thereof). HTTPS_PROXY takes precedence over HTTP_PROXY for https
requests.
由于我写的是命令行工具,所以直接在终端里面设定即可。譬如这样:
1 | $ export HTTP_PROXY=127.0.0.1:8888 # 8888 是Charles的默认代理端口 |
然后启动我的程序,所发请求就自动被Charles捕获了。
最后:
(还是个半成品!)