wuyongxiu / wuyongxiu.github.io

随便记录一下......
http://wuyongxiu.github.io
6 stars 3 forks source link

Go Http出现 411 Length Required Nginx #29

Open wuyongxiu opened 7 years ago

wuyongxiu commented 7 years ago

问题描述: 前天有用户反应,请求经过我们的API网关转发到URL为 a.b.com/xx 这样的域名(域名被nginx解析),会收到nginx的报错“ 411 Length Required” ,然而直接通过浏览器访问 a.b.com/xx 是没有问题的。

API网关收到请求时会对请求Header以及相应参数进行相应处理,并封装Body,构造成一个新的请求(也就是需要的a.b.com/xx)发送给后端,所以问题一定是在处理请求的时候漏掉了什么或者多做了什么。
查看了下,411 Length Required 这种问题出在请求content length=0 和请求 body 为空的情况下。而我们出问题的请求也是这种Body没有内容的请求(Post请求,body为空)
百转千回对比之后才发现,是因为我们对请求Body进行了封装。封装后的请求 Body 内容为空的指针,(所以body本身反而不是nil了)。而能发送成功的请求Body是nil。


以下写了个小demo

package main
import{
"bytes"
"io/ioutil"
"log"
"net/http"
)

const (
URL = "http://a.kingdee.com/api/patch/patch-list.action?client_id=1"
Method = "POST"
)

func main() {
nilbody := ioutil.NopCloser(bytes.NewReader([]byte("")))
res, err := http.Post(URL, "", nilbody)
if err != nil {
log.Panic(err)
return
}
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
log.Println(res.StatusCode, string(body))
}



当Go语言版本为1.8的时候: 在源码包里加了日志(net/http/client.go 255行打印完整请求,go install -a -v 重新编译源码),打印出请求信息

&{Method:POST URL:http://a.b.com/api/patch/patch-list.action?client_id=1 Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[Content-Type:[]] Body:{Reader:0xc04205e900} GetBody:<nil> ContentLength:0 TransferEncoding:[] Close:false Host:a.b.com Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr: RequestURI: TLS:<nil> Cancel:<nil> Response:<nil> ctx:<nil>}

请求回复信息大致说明 411 Length Required
上面可以看出 ContentLength=0 而 Body是一个指针。


当Go语言版本小于1.8例如为1.7.3的时候:
在源码包里加了日志(net/http/client.go 255行打印完整请求,go install -a -v 重新编译源码),打印出请求信息
&{Method:POST URL:http://a.b.com/api/patch/patch-list.action?client_id=1 Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[Content-Type:[]] Body:{Reader:0xc042054ba0} ContentLength:0 TransferEncoding:[] Close:false Host:a.b.com Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr: RequestURI: TLS:<nil> Cancel:<nil> Response:<nil> ctx:<nil>}
请求能正常返回结果,不存在411 Length Required。

所以是Go1.8对这种情况的处理和之前不一致了。


版本升级之后,Go1.8版本对 请求Body为空内容的指针,ContentLength为0的处理与之前的版本不同了。而我们的API网关在处理请求的时候,对请求的body进行了封装(为了解决http proxy body closed的问题),导致原本是nil的body不再是nil。在之前的Go版本里这些都没有问题,可是在1.8版本里,就会遇到411 Length Required这种情况。解决方法就是当判断请求ContentLength为0的时候,就不再对请求的body进行任何处理。
当然接下来还需要测试之前的问题在新版本里是否已经不存在,这样就可以不对请求body进行任何处理了。

hunterhug commented 7 years ago

good job