问题描述
我观察到没有为有效载荷为空/无的PATCH请求设置Content-Length标头。即使我们通过req.Header.Set("content-length","0")
手动设置它,实际上也不在外发请求中设置它。
仅当PATCH请求且仅当有效载荷为空或为零(或设置为http.NoBody)时才会发生这种奇怪的行为(Go bug?)
package main
import (
"fmt"
"io/IoUtil"
"net/http"
"strings"
)
func main() {
url := "http://localhost:9999"
method := "PATCH"
payload := strings.NewReader("")
client := &http.Client {
}
req,err := http.NewRequest(method,url,payload)
if err != nil {
fmt.Println(err)
}
req.Header.Set("Authorization","Bearer my-token")
req.Header.Set("Content-Length","0") //this is not honoured
res,err := client.Do(req)
defer res.Body.Close()
body,err := IoUtil.ReadAll(res.Body)
fmt.Println(string(body))
}
即使在最新的go版本1.15
中,此操作也可以重现。
只需在简单的http服务器上运行上述代码,然后亲自查看即可。
是否有解决方案/解决方案来发送Content-Length设置为0的PATCH请求?
解决方法
您可以通过将TransferEncoding设置为Content-Length
来告诉HTTP客户端包括值为0的identity
头:
url := "http://localhost:9999"
method := "PATCH"
client := &http.Client{}
req,err := http.NewRequest(method,url,http.NoBody)
if err != nil {
panic(err)
}
req.TransferEncoding = []string{"identity"}
req.Header.Set("Authorization","Bearer my-token")
// req.Header.Set("Content-Length","0")
请注意对原始代码的以下更改:
- 重要的人:
req.TransferEncoding = []string{"identity"}
- 指定空正文的惯用方式:
http.NoBody
(不影响发送长度) - 评论
req.Header.Set("Content-Length","0")
,客户端自行填写 - 也因错误而惊慌失措,您可能不想继续
identity
的传输编码未未写入请求,因此,除了标头Content-Length = 0
之外,请求的外观与以前相同。
很遗憾,这没有记录(可以随时向Go团队提出问题),但是可以在以下代码中看到:
乏味的细节:
transferWriter.writeHeader检查以下内容以编写Content-Length
标头:
// Write Content-Length and/or Transfer-Encoding whose values are a
// function of the sanitized field triple (Body,ContentLength,// TransferEncoding)
if t.shouldSendContentLength() {
if _,err := io.WriteString(w,"Content-Length: "); err != nil {
return err
}
if _,strconv.FormatInt(t.ContentLength,10)+"\r\n"); err != nil {
return err
}
依次,shouldCheckContentLength在长度为零的情况下查看传输编码:
if t.ContentLength == 0 && isIdentity(t.TransferEncoding) {
if t.Method == "GET" || t.Method == "HEAD" {
return false
}
return true
}
isIdentity验证TransferEncoding
是否准确 []string{"identity"}
:
func isIdentity(te []string) bool { return len(te) == 1 && te[0] == "identity" })