Go:disable HTTP chunk mode
1.结论
应用层显式设置 Content-Length,可以 disable chunk mode。
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
writer.Header().Set("Content-Length", "2049")
writer.Write(bytes.Repeat([]byte("."), 2049))
})
2.跑起来
Go demo:
package main
import (
"bytes"
"net/http"
)
func main() {
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
writer.Write(bytes.Repeat([]byte("."), 2049))
})
http.ListenAndServe(":9080", nil)
}
运行:
$ go run main.go
^Csignal: interrupt
测试:
$ curl -v http://127.0.0.1:9080/
...
>
< HTTP/1.1 200 OK
< Date: Wed, 28 Apr 2021 10:39:50 GMT
< Content-Type: text/plain; charset=utf-8
< Transfer-Encoding: chunked
<
...(2049B)
即,当Go服务端写入2049B时,将使用chunk模式传输(Transfer-Encoding: chunked)。
3.查源码
Go版本:
$ go version
go version go1.13.6 linux/amd64
源码定义:
// net/http/server.go
// This should be >= 512 bytes for DetectContentType,
// but otherwise it's somewhat arbitrary.
const bufferBeforeChunkingSize = 2048
// net/http/server.go
// Read next request from connection.
func (c *conn) readRequest(ctx context.Context) (w *response, err error) {
...
w = &response{
conn: c,
cancelCtx: cancelCtx,
req: req,
reqBody: req.Body,
handlerHeader: make(Header),
contentLength: -1,
...
}
...
w.cw.res = w
w.w = newBufioWriterSize(&w.cw, bufferBeforeChunkingSize)
return w, nil
}
在创建http.response时指定buffer的大小:
// A response represents the server side of an HTTP response.
type response struct {
...
w *bufio.Writer // buffers output in chunks to chunkWriter
cw chunkWriter
...
}
4.disable HTTP chunk mode
源码注释:【重点】
// If the handler didn't declare a Content-Length up front, we either
// go into chunking mode or, if the handler finishes running before
// the chunking buffer size, we compute a Content-Length and send that
// in the header instead.
如果你已经显式声明设置Content-Length,则不会进入chunk mode,否则,受到chunk buffer size限制:
1.超过chunk buffer size,use chunk mode;
2.未超chunk buffer size,Go底层计算Content-Length并填入HTTP应答Header;
因此,如果应用层已知HTTP应答总长度,直接设置Content-Length即可disable chunk:
package main
import (
"bytes"
"net/http"
)
func main() {
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
writer.Header().Set("Content-Length", "2049")
writer.Write(bytes.Repeat([]byte("."), 2049))
})
http.ListenAndServe(":9080", nil)
}
此时,应用层写入2049B,超过chunk buffer size(2048),但是使用Content-Length而非chunk。
$ curl -v http://127.0.0.1:9080/
...
< HTTP/1.1 200 OK
< Content-Length: 2049
< Date: Wed, 28 Apr 2021 11:00:00 GMT
< Content-Type: text/plain; charset=utf-8
<
...(2049B)
5.不建议的方式
修改源码 src/net/http/server.go 中 bufferBeforeChunkingSize 值,例如:
// This should be >= 512 bytes for DetectContentType,
// but otherwise it's somewhat arbitrary.
const bufferBeforeChunkingSize = 1024(原2048)
此时,应用层未设置Content-Length并写入1025B,重新编译运行,也将进入chunk mode:
func main() {
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
writer.Write(bytes.Repeat([]byte("."), 1025))
})
http.ListenAndServe(":9080", nil)
}
$ go build main.go
$ ./main
$ curl -v http://127.0.0.1:9080/
...
>
< HTTP/1.1 200 OK
< Date: Wed, 28 Apr 2021 11:12:53 GMT
< Content-Type: text/plain; charset=utf-8
< Transfer-Encoding: chunked
<
...(1025B)
由于没有API设置 bufferBeforeChunkingSize 值,因此,只能通过源码方式修改。
这种方法不利于后续运维,因此不建议。

本文介绍了如何在 Go 服务端禁用 HTTP Chunked 编码。通过设置 `Content-Length` 头部,即使内容长度超过默认缓冲区大小(2048B),Go 也会计算准确的 Content-Length 并避免使用 Chunked 编码。此外,文章讨论了修改源码设置缓冲区大小的不推荐做法及其原因。
160

被折叠的 条评论
为什么被折叠?



