[インデックス 14527] ファイルの概要
このコミットは、Go言語の標準ライブラリである net/http
パッケージにおけるHTTPレスポンスの挙動に関する修正です。具体的には、HTTP 204 No Contentレスポンスが誤って Transfer-Encoding: chunked
ヘッダを送信してしまう問題を解決します。
変更されたファイルは以下の通りです。
src/pkg/net/http/server.go
: HTTPサーバーのレスポンス書き込みロジックを定義するファイル。204レスポンスに対するTransfer-Encoding
ヘッダの削除が追加されました。src/pkg/net/http/transport_test.go
:net/http
パッケージのトランスポート層に関するテストファイル。204レスポンスとチャンクエンコーディングの問題を再現し、修正を検証するための新しいテストケースが追加されました。
コミット
- コミットハッシュ:
9c2f410206221473a9773849ab9749c6602ce11a
- Author: Brad Fitzpatrick bradfitz@golang.org
- Date: Thu Nov 29 18:00:51 2012 -0800
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/9c2f410206221473a9773849ab9749c6602ce11a
元コミット内容
net/http: don't send chunked encoding on 204 responses
RFC 2616: "The 204 response MUST NOT include a message-body,
and thus is always terminated by the first empty line after
the header fields."
Previously we'd trigger chunked encoding by default on
responses, and then when finishing the request we'd write the
chunk trailers, which counted as a message-body.
Fixes #4454
R=golang-dev
CC=golang-dev
https://golang.org/cl/6782139
変更の背景
この変更は、HTTP 204 No Contentレスポンスが Transfer-Encoding: chunked
ヘッダを伴って送信されるというバグを修正するために行われました。HTTP/1.1の仕様を定めたRFC 2616では、204 No Contentレスポンスはメッセージボディを含んではならないと明確に規定されています。
Goの net/http
パッケージの以前の実装では、レスポンスのデフォルトの挙動としてチャンクエンコーディングが有効になることがありました。この場合、204レスポンスであっても、レスポンスの終了時にチャンクトレーラー(メッセージボディの一部とみなされる)が書き込まれてしまい、RFCの規定に違反していました。
この問題は、クライアント側で204レスポンスを正しく処理できない原因となっていました。特に、チャンクエンコーディングを期待しないクライアントが、ボディがないはずの204レスポンスでチャンクトレーラーを受け取ると、プロトコルエラーとして扱ってしまう可能性がありました。GitHub Issue #4454で報告された「client fails to handle requests with no body and chunked encoding」という問題が、このコミットによって修正されました。
前提知識の解説
HTTPステータスコード 204 No Content
HTTPステータスコード204 (No Content) は、サーバーがリクエストを正常に処理したが、レスポンスとしてエンティティボディ(メッセージボディ)を返す必要がないことを示す成功ステータスコードです。例えば、PUTリクエストでリソースが更新されたが、クライアントに新しいコンテンツを返す必要がない場合などに使用されます。
RFC 2616 (Hypertext Transfer Protocol -- HTTP/1.1) のセクション 10.2.5 (204 No Content) には、以下の記述があります。
The 204 response MUST NOT include a message-body, and thus is always terminated by the first empty line after the header fields. (204レスポンスはメッセージボディを含んではならず、したがってヘッダフィールドの後の最初の空行で常に終了する。)
この規定は非常に重要であり、204レスポンスの特性を定義しています。メッセージボディがないため、Content-Length
ヘッダも通常は含まれません。
Transfer-Encoding
ヘッダと chunked
エンコーディング
Transfer-Encoding
ヘッダは、メッセージボディを安全に転送するために適用されたエンコーディング形式を指定します。最も一般的な値の一つが chunked
です。
chunked
エンコーディングは、メッセージボディのサイズが事前に不明な場合や、動的に生成される場合に特に有用です。データは「チャンク」と呼ばれる小さなブロックに分割され、各チャンクはサイズ情報(16進数)と実際のデータで構成されます。メッセージの終わりは、サイズが0のチャンク(ゼロチャンク)と、それに続くオプションのトレーラー(追加のヘッダフィールド)で示されます。
しかし、204 No Contentレスポンスのようにメッセージボディが「存在しない」ことが明確に規定されている場合、Transfer-Encoding: chunked
を使用することはプロトコル違反となります。なぜなら、チャンクエンコーディングはメッセージボディの存在を前提とし、ゼロチャンクやトレーラーもメッセージボディの一部とみなされるためです。
技術的詳細
Goの net/http
パッケージでは、http.ResponseWriter
インターフェースを通じてHTTPレスポンスが構築されます。ResponseWriter
の WriteHeader
メソッドは、ステータスコードを設定し、ヘッダを書き込む役割を担います。
以前の実装では、WriteHeader
が呼び出された際に、レスポンスに Content-Length
ヘッダが明示的に設定されていない場合、またはレスポンスボディのサイズが不明な場合に、デフォルトで Transfer-Encoding: chunked
が適用されるロジックがありました。これは一般的なHTTPレスポンスでは問題ありませんが、204 No Contentレスポンスの場合、RFCの規定に反してチャンクエンコーディングが適用されてしまうという副作用がありました。
具体的には、WriteHeader
が StatusNoContent
(204) を受け取った際、net/http
内部の処理でチャンクエンコーディングが有効になり、レスポンスの終了時にゼロチャンクやトレーラーが書き込まれていました。これらのチャンクトレーラーは、RFC 2616が禁止している「メッセージボディ」の一部と解釈されるため、クライアント側で予期せぬエラーやプロトコル違反として扱われる可能性がありました。
このコミットでは、WriteHeader
メソッド内でステータスコードが StatusNoContent
(204) である場合に、明示的に Transfer-Encoding
ヘッダを削除する処理を追加することで、この問題を解決しています。これにより、204レスポンスが常にメッセージボディを含まない、RFCに準拠した形式で送信されるようになります。
コアとなるコードの変更箇所
src/pkg/net/http/server.go
--- a/src/pkg/net/http/server.go
+++ b/src/pkg/net/http/server.go
@@ -369,6 +369,8 @@ func (w *response) WriteHeader(code int) {
if w.req.Method == "HEAD" || code == StatusNotModified {
// do nothing
+ } else if code == StatusNoContent {
+ w.header.Del("Transfer-Encoding")
} else if hasCL {
w.contentLength = contentLength
w.header.Del("Transfer-Encoding")
src/pkg/net/http/transport_test.go
--- a/src/pkg/net/http/transport_test.go
+++ b/src/pkg/net/http/transport_test.go
@@ -857,6 +857,30 @@ func TestIssue3595(t *testing.T) {
}
}
+// From http://golang.org/issue/4454 ,
+// "client fails to handle requests with no body and chunked encoding"
+func TestChunkedNoContent(t *testing.T) {
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.WriteHeader(StatusNoContent)
+ }))
+ defer ts.Close()
+
+ for _, closeBody := range []bool{true, false} {
+ c := &Client{Transport: &Transport{}}
+ const n = 4
+ for i := 1; i <= n; i++ {
+ res, err := c.Get(ts.URL)
+ if err != nil {
+ t.Errorf("closingBody=%v, req %d/%d: %v", closeBody, i, n, err)
+ } else {
+ if closeBody {
+ res.Body.Close()
+ }
+ }
+ }
+ }
+}
+
func TestTransportConcurrency(t *testing.T) {
const maxProcs = 16
const numReqs = 500
コアとなるコードの解説
src/pkg/net/http/server.go
の変更
func (w *response) WriteHeader(code int)
メソッドは、HTTPレスポンスのヘッダとステータスコードを書き込むGoの内部関数です。
追加された以下の行がこのコミットの核心です。
} else if code == StatusNoContent {
w.header.Del("Transfer-Encoding")
このコードは、WriteHeader
が呼び出され、設定されるステータスコードが StatusNoContent
(HTTP 204) である場合に実行されます。w.header.Del("Transfer-Encoding")
は、レスポンスヘッダから Transfer-Encoding
ヘッダを明示的に削除します。これにより、たとえ内部ロジックでチャンクエンコーディングがデフォルトで有効になるような状況であっても、204レスポンスではこのヘッダが送信されなくなり、RFC 2616の規定に準拠するようになります。
src/pkg/net/http/transport_test.go
の新しいテスト
TestChunkedNoContent
という新しいテスト関数が追加されました。このテストの目的は、204 No Contentレスポンスがチャンクエンコーディングなしで正しく処理されることを検証することです。
テストのロジックは以下の通りです。
httptest.NewServer
を使用してテスト用のHTTPサーバーを起動します。このサーバーは、すべてのリクエストに対してw.WriteHeader(StatusNoContent)
を呼び出し、204レスポンスを返します。for _, closeBody := range []bool{true, false}
ループにより、レスポンスボディを閉じる場合と閉じない場合の両方でテストを実行します。これは、クライアントがレスポンスボディを読み終えるかどうかにかかわらず、問題が発生しないことを確認するためです。- ループ内で、新しい
http.Client
を作成し、テストサーバーに対してc.Get(ts.URL)
でGETリクエストを送信します。 if err != nil
でエラーが発生しないことを確認します。以前のバグでは、クライアントがチャンクエンコーディングされた204レスポンスを処理しようとすると、ここでエラーが発生する可能性がありました。このテストがエラーなく完了することで、修正が正しく機能していることが検証されます。
このテストは、特定のクライアントの挙動(ボディを閉じるか否か)に依存せず、204レスポンスがチャンクエンコーディングなしで正しく受信・処理されることを保証します。
関連リンク
- GitHub Issue: Fixes #4454 - client fails to handle requests with no body and chunked encoding
- Go CL (Change List): https://golang.org/cl/6782139
参考にした情報源リンク
- RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1:
- Section 10.2.5: 204 No Content: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.5
- Section 3.6.1: Chunked Transfer Coding: https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
- Go
net/http
package documentation: https://pkg.go.dev/net/http (現在のドキュメント) - HTTP Transfer-Encoding header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding
- HTTP 204 No Content status code: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204