某次测试一个用 Go 编写的 HTTP 服务(使用 nginx 进行了反向代理)的时候,发现请求直接返回了 400 Bad Request: invalid header value
。而在 handler 入口处的调试日志并没有打印。同时,nginx 日志显示,请求已经被转发到了后面的 HTTP 服务。也就是说,这个 400 错误是 Go 在接受请求后,进入 handler 之前返回的。故而在此记录下,Go 的 HTTP 标准库对请求 header 的处理方式。
httplex
golang 的标准库 net/http
使用 golang.org/x/net/lex/httplex
对请求头进行校验。其中,有三个校验函数:
func ValidHeaderFieldName(v string) bool
用以检验
v
是否为一个有效的 HTTP/1.x 的 header 字段名。(对于 HTTP/2 有额外的限制:不允许大写的 ASCII 字母)检验标准:RFC 7230。不能为空,并且字符范围为:"!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" / DIGIT(数字) / ALPHA(字母,不区分大小写)
func ValidHeaderFieldValue(v string) bool
用以检验
v
是哦否为一个有效的 header 字段值。检验标准:http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
func ValidHostHeader(h string) bool
用以检验
h
是否为一个有效的Host
头。当前的校验标准:不完全受 rfc7230 限制,只要字符范围为 "!" / "$" / "%" / "&" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / ";" / "=" / "[" / "'" / "]" / "_" / "~" / DIGIT(数字) / ALPHA(字母,不区分大小写)即可。
net/http
对请求头的校验
使用 go 1.9.2,HTTP/1.x。代码位于
net/http/server.go
中
Host
头的数目不能超过 1,否则:1
2$ curl http://localhost:18080/ -H "Host: 123" -H "Host: 456"
400 Bad Request: too many Host headersHost
头的值可以通过httplex.ValidHostHeader
方法的校验,否则:1
2$ curl http://localhost:18080/ -H "Host: 123@"
400 Bad Request: malformed Host header- 每一个 header 的字段名都可以通过
httplex.ValidHeaderFieldName
的校验,否则:1
2$ curl http://localhost:18080/ -H "Host: 123" -H "X-Custome@tes123: 123abc"
400 Bad Request: invalid header name - 每一个 header 的字段值都可以通过
httplex.ValidHeaderFieldValue
的校验,否则返回:1
400 Bad Request: invalid header value
碎碎念
当发送 HTTP 请求后,收到 Go 自身标准库的错误返回,而不是自己编写的错误返回时,如果错误信息模糊无法确定真正的异常原因。那么只好拿着错误信息到 net/http
包代码所在到位置直接暴力搜索了。有时候可以加上日志打印,把错误抛出的上下文打印出来(记得备份源代码,记得删除 http.a
文件,记得重新编译!!)。这样,就可以让服务“直接”告诉你根因所在了。