
本文介绍了在使用 Go 语言 net/http 包构建 HTTP 服务器时,如何禁用默认的 Chunked 传输编码。通过设置 Content-Length 头部,可以强制服务器使用 Identity 传输编码,从而避免 Chunked 编码的出现。
在 Go 语言中使用 net/http 包创建 HTTP 服务器时,默认情况下,对于 HTTP/1.1 及以上版本的请求,服务器会采用 Chunked 传输编码来发送响应。Chunked 编码允许服务器在不知道响应内容总长度的情况下开始发送数据,这在某些场景下非常有用。然而,有时我们可能需要禁用 Chunked 编码,强制服务器使用 Identity 传输编码。
原理分析
net/http 包的 server.go 文件中的 WriteHeader(code int) 函数负责将 HTTP 头部写入 socket。在该函数中,可以看到以下逻辑:
if hasCL {
w.contentLength = contentLength
w.header.Del("Transfer-Encoding")
} else if w.req.ProtoAtLeast(1, 1) {
// HTTP/1.1 or greater: use chunked transfer encoding
// to avoid closing the connection at EOF.
// TODO: this blows away any custom or stacked Transfer-Encoding they
// might have set. Deal with that as need arises once we have a valid
// use case.
w.chunking = true
w.header.Set("Transfer-Encoding", "chunked")
} else {这段代码表明,如果响应中存在 Content-Length 头部(hasCL 为 true),则会删除 Transfer-Encoding 头部,从而禁用 Chunked 编码,采用 Identity 编码。否则,如果 HTTP 版本大于等于 1.1,则会设置 Transfer-Encoding 为 chunked。
解决方案
要禁用 Chunked 编码,最简单的方法是在响应中设置 Content-Length 头部。这样,net/http 包会自动禁用 Chunked 编码。
代码示例
以下是一个示例代码,演示了如何设置 Content-Length 头部来禁用 Chunked 编码:
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
message := "Hello, World!"
// 设置 Content-Length 头部
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(message)))
// 写入响应
fmt.Fprint(w, message)
}
func main() {
http.HandleFunc("/", handler)
fmt.Println("Server listening on port 8080")
http.ListenAndServe(":8080", nil)
}在这个示例中,我们首先定义了一个 handler 函数,该函数处理所有请求。在 handler 函数中,我们首先获取要发送的消息的长度,然后使用 w.Header().Set("Content-Length", fmt.Sprintf("%d", len(message))) 设置 Content-Length 头部。最后,我们使用 fmt.Fprint(w, message) 将消息写入响应。
注意事项
- 必须在调用 WriteHeader 之前设置 Content-Length 头部。
- Content-Length 的值必须与实际发送的响应体的长度一致。如果不一致,可能会导致客户端解析错误。
- 如果响应体是动态生成的,并且无法预先知道其长度,则无法使用 Content-Length 头部,也就无法禁用 Chunked 编码。
总结
通过设置 Content-Length 头部,可以有效地禁用 Go HTTP 服务器响应中的 Chunked 编码。这在某些特定的应用场景下非常有用,例如需要与不支持 Chunked 编码的客户端进行交互时。理解 net/http 包的内部实现,有助于更好地控制 HTTP 服务器的行为。









