Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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レスポンスが構築されます。ResponseWriterWriteHeader メソッドは、ステータスコードを設定し、ヘッダを書き込む役割を担います。

以前の実装では、WriteHeader が呼び出された際に、レスポンスに Content-Length ヘッダが明示的に設定されていない場合、またはレスポンスボディのサイズが不明な場合に、デフォルトで Transfer-Encoding: chunked が適用されるロジックがありました。これは一般的なHTTPレスポンスでは問題ありませんが、204 No Contentレスポンスの場合、RFCの規定に反してチャンクエンコーディングが適用されてしまうという副作用がありました。

具体的には、WriteHeaderStatusNoContent (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レスポンスがチャンクエンコーディングなしで正しく処理されることを検証することです。

テストのロジックは以下の通りです。

  1. httptest.NewServer を使用してテスト用のHTTPサーバーを起動します。このサーバーは、すべてのリクエストに対して w.WriteHeader(StatusNoContent) を呼び出し、204レスポンスを返します。
  2. for _, closeBody := range []bool{true, false} ループにより、レスポンスボディを閉じる場合と閉じない場合の両方でテストを実行します。これは、クライアントがレスポンスボディを読み終えるかどうかにかかわらず、問題が発生しないことを確認するためです。
  3. ループ内で、新しい http.Client を作成し、テストサーバーに対して c.Get(ts.URL) でGETリクエストを送信します。
  4. if err != nil でエラーが発生しないことを確認します。以前のバグでは、クライアントがチャンクエンコーディングされた204レスポンスを処理しようとすると、ここでエラーが発生する可能性がありました。このテストがエラーなく完了することで、修正が正しく機能していることが検証されます。

このテストは、特定のクライアントの挙動(ボディを閉じるか否か)に依存せず、204レスポンスがチャンクエンコーディングなしで正しく受信・処理されることを保証します。

関連リンク

参考にした情報源リンク