Content-Length header 没有为具有 empty/nil 负载的 PATCH 请求设置 - GoLang
Content-Length header is not getting set for PATCH requests with empty/nil payload - GoLang
我观察到 Content-Length header 没有为具有 empty/nil 负载的 PATCH 请求设置。即使我们通过 req.Header.Set("content-length", "0")
手动设置它,它实际上并没有在传出请求中设置。
这种奇怪的行为(Go bug?)仅在 PATCH 请求时发生,并且仅在有效负载为空或 nil(或设置为 http.NoBody)
时发生
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 服务器,你自己看看。
有没有solution/workaround发送一个Content-Length设置为0的PATCH请求?
您可以通过将 TransferEncoding 设置为 identity
来告诉 HTTP 客户端包含值为 0 的 Content-Length
header,如下所示:
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"}
- 指定空 body 的惯用方式:
http.NoBody
(不影响发送长度)
- 注释掉
req.Header.Set("Content-Length", "0")
,客户端自行填写
- 也更改为出现错误时恐慌,您可能不想继续
identity
的传输编码没有写入请求,所以除了headerContent-Length = 0
,请求看起来和以前一样。
不幸的是,这没有记录(请随时向 Go 团队提出问题),但可以在以下代码中看到:
繁琐的细节:
transferWriter.writeHeader 检查下面写 Content-Length
header:
// 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 _, err := io.WriteString(w, 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" })
我观察到 Content-Length header 没有为具有 empty/nil 负载的 PATCH 请求设置。即使我们通过 req.Header.Set("content-length", "0")
手动设置它,它实际上并没有在传出请求中设置。
这种奇怪的行为(Go bug?)仅在 PATCH 请求时发生,并且仅在有效负载为空或 nil(或设置为 http.NoBody)
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 服务器,你自己看看。
有没有solution/workaround发送一个Content-Length设置为0的PATCH请求?
您可以通过将 TransferEncoding 设置为 identity
来告诉 HTTP 客户端包含值为 0 的 Content-Length
header,如下所示:
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"}
- 指定空 body 的惯用方式:
http.NoBody
(不影响发送长度) - 注释掉
req.Header.Set("Content-Length", "0")
,客户端自行填写 - 也更改为出现错误时恐慌,您可能不想继续
identity
的传输编码没有写入请求,所以除了headerContent-Length = 0
,请求看起来和以前一样。
不幸的是,这没有记录(请随时向 Go 团队提出问题),但可以在以下代码中看到:
繁琐的细节:
transferWriter.writeHeader 检查下面写 Content-Length
header:
// 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 _, err := io.WriteString(w, 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" })