[インデックス 15231] ファイルの概要
このコミットは、Go言語の標準ライブラリである net/http
パッケージ内の requestwrite_test.go
ファイルに対する変更です。このファイルは、HTTPリクエストの書き込み(シリアライズ)に関するテストケースを定義しており、net/http
クライアントが送信するリクエストがHTTP仕様に準拠し、かつ期待される動作をすることを確認するために使用されます。
コミット
- コミットハッシュ:
cd566958e938c695d09730dfcb7c2b8e76658f89
- Author: Brad Fitzpatrick bradfitz@golang.org
- Date: Wed Feb 13 18:33:15 2013 -0800
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/cd566958e938c695d09730dfcb7c2b8e76658f89
元コミット内容
net/http: test that we preserve Go 1.0 Request.Write Host behavior
Fixes #4792
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7314093
変更の背景
このコミットの主な目的は、Go 1.0における net/http.Request.Write
メソッドの Host
ヘッダーに関する既存の動作を、将来のバージョンでも維持することを保証するためのテストを追加することです。コミットメッセージにある Fixes #4792
は、Goプロジェクトのイシュートラッカーにおける特定のバグまたは振る舞いに関する議論に対応していることを示唆しています。
具体的には、Request.Host
フィールドが空文字列であり、かつ Request.URL.Host
も空である場合に、Host
ヘッダーがどのように生成されるかという点に焦点が当てられています。Go 1.0では、この特定の条件下で Host
ヘッダーが空文字列として送信されるという動作がありました。このコミットは、この動作が意図されたものであり、後方互換性を保つために変更されないことをテストによって保証しようとしています。
Request.Header["Host"]
に値が設定されている場合でも、Request.Host
と Request.URL.Host
が空であれば、Request.Header["Host"]
の値が無視され、空の Host
ヘッダーが送信されるという、一見すると直感に反するかもしれないGo 1.0の振る舞いを固定化することが目的です。これは、HTTPリクエストの Host
ヘッダーの決定ロジックが複雑であり、特に Request
構造体の複数のフィールド(Host
、URL.Host
、Header["Host"]
)が相互に作用するため、このようなテストが重要になります。
前提知識の解説
HTTP Host ヘッダー
HTTP/1.1では、Host
ヘッダーは必須であり、リクエストされたリソースのインターネットホストとポート番号を指定します。これは、単一のIPアドレスで複数のウェブサイト(バーチャルホスト)をホストするサーバーにおいて、どのウェブサイトへのリクエストであるかを識別するために不可欠です。
例: Host: example.com
または Host: example.com:8080
Goの net/http
パッケージ
net/http
パッケージは、Go言語でHTTPクライアントとサーバーを実装するための基本的な機能を提供します。
http.Request
構造体: HTTPリクエストを表す構造体です。この構造体には、リクエストメソッド(GET, POSTなど)、URL、ヘッダー、ボディなどの情報が含まれます。Request.Host
フィールド: このフィールドは、リクエストのHost
ヘッダーの値を明示的に設定するために使用されます。このフィールドが設定されている場合、Request.URL.Host
よりも優先されます。Request.URL
フィールド: リクエストのURLを表すurl.URL
型の構造体です。url.URL.Host
フィールド: URLから解析されたホスト名とポート番号が含まれます。Request.Host
が設定されていない場合、この値がHost
ヘッダーのデフォルト値として使用されます。
Request.Header
フィールド: リクエストヘッダーを表すhttp.Header
型のマップです。Host
ヘッダーは通常、Request.Host
またはRequest.URL.Host
から自動的に生成されますが、Request.Header["Host"]
を介して手動で設定することも可能です。ただし、Request.Host
が設定されている場合は、Request.Header["Host"]
よりも優先されます。
Goにおける後方互換性
Go言語は、後方互換性を非常に重視しています。これは、既存のGoプログラムが新しいバージョンのGoコンパイラやライブラリで引き続き動作することを意味します。APIの変更は慎重に行われ、既存の動作を変更する場合は、その影響を最小限に抑えるように努めます。このコミットのように、特定の振る舞いをテストで固定化することは、将来の変更が意図せず既存の動作を壊すことを防ぐための重要な手段です。
技術的詳細
このコミットで追加されたテストケースは、net/http
パッケージがHTTPリクエストを書き出す際の Host
ヘッダーの生成ロジックの特定のコーナーケースを扱っています。
通常、http.Request
を Write
メソッドでバイトストリームに変換する際、Host
ヘッダーは以下の優先順位で決定されます。
Request.Host
フィールドが設定されている場合、その値が使用されます。Request.Host
が設定されておらず、Request.URL.Host
が設定されている場合、その値が使用されます。- 上記いずれも設定されていない場合、
Request.Header["Host"]
の値が使用されることがあります(ただし、これは推奨される方法ではありません)。
しかし、Go 1.0の特定の振る舞いとして、Request.Host
が明示的に空文字列 (""
) に設定され、かつ Request.URL.Host
も空文字列である場合、たとえ Request.Header["Host"]
に値が設定されていても、Host
ヘッダーは空文字列として送信されるという動作がありました。
このコミットは、この「Go 1.0の動作」を明示的にテストすることで、将来の変更がこの特定の振る舞いを意図せず変更しないことを保証します。これは、既存のGoアプリケーションがこの振る舞いに依存している可能性があるため、後方互換性を維持するために重要です。
テストケースでは、以下の条件が設定されています。
Request.Method
: "GET"Request.Host
:""
(空文字列)Request.URL.Scheme
: "http"Request.URL.Host
:""
(空文字列)Request.URL.Path
: "/search"Request.ProtoMajor
: 1,Request.ProtoMinor
: 1 (HTTP/1.1)Request.Header
:Header{"Host": []string{"bad.example.com"}}
(Host
ヘッダーに「bad.example.com」という値が設定されている)
この設定にもかかわらず、期待される出力 (WantWrite
) では Host:
(空のHostヘッダー) が含まれています。これは、Request.Host
が明示的に空文字列に設定されている場合、Request.Header["Host"]
の値が無視されるというGo 1.0の動作を検証しています。
コアとなるコードの変更箇所
src/pkg/net/http/requestwrite_test.go
ファイルに以下のテストケースが追加されました。
--- a/src/pkg/net/http/requestwrite_test.go
+++ b/src/pkg/net/http/requestwrite_test.go
@@ -328,6 +328,31 @@ var reqWriteTests = []reqWriteTest{\
"User-Agent: Go http package\r\n" +\
"X-Foo: X-Bar\r\n\r\n",
},
+
+ // If no Request.Host and no Request.URL.Host, we send
+ // an empty Host header, and don't use
+ // Request.Header["Host"]. This is just testing that
+ // we don't change Go 1.0 behavior.
+ {
+ Req: Request{
+ Method: "GET",
+ Host: "",
+ URL: &url.URL{
+ Scheme: "http",
+ Host: "",
+ Path: "/search",
+ },
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Header: Header{
+ "Host": []string{"bad.example.com"},
+ },
+ },
+
+ WantWrite: "GET /search HTTP/1.1\r\n" +\
+ "Host: \r\n" +\
+ "User-Agent: Go http package\r\n\r\n",
+ },
}
func TestRequestWrite(t *testing.T) {
コアとなるコードの解説
追加されたテストケースは reqWriteTests
スライスの一部として定義されています。reqWriteTests
は、http.Request
オブジェクトと、そのオブジェクトが Write
メソッドによってシリアライズされた場合に期待されるHTTPリクエストの生文字列 (WantWrite
) のペアを格納するスライスです。
この新しいテストケースは、以下の Request
構造体で構成されています。
Method: "GET"
: HTTP GETメソッドを指定します。Host: ""
:Request
構造体のHost
フィールドを明示的に空文字列に設定します。これがこのテストケースの核心です。URL: &url.URL{Scheme: "http", Host: "", Path: "/search"}
: URLのスキームを "http"、ホストを空文字列、パスを "/search" に設定します。URL.Host
も空である点が重要です。ProtoMajor: 1, ProtoMinor: 1
: HTTP/1.1プロトコルバージョンを指定します。Header: Header{"Host": []string{"bad.example.com"}}
:Request.Header
マップにHost
ヘッダーを「bad.example.com」という値で追加します。
WantWrite
フィールドには、この Request
オブジェクトが Write
メソッドによってシリアライズされた場合に期待されるHTTPリクエストの生文字列が記述されています。注目すべきは、Host: \r\n
の部分です。これは、Request.Host
と Request.URL.Host
が両方とも空である場合、Request.Header["Host"]
に値が設定されていても、Host
ヘッダーが空文字列として出力されるというGo 1.0の動作を正確に反映しています。
TestRequestWrite
関数は、この reqWriteTests
スライスをイテレートし、各テストケースの Req
オブジェクトを Write
メソッドでシリアライズし、その結果が WantWrite
と一致するかどうかを検証します。これにより、Go 1.0の Host
ヘッダーの振る舞いが将来のバージョンでも維持されることが保証されます。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/doc/
net/http
パッケージのドキュメント: https://pkg.go.dev/net/httpurl
パッケージのドキュメント: https://pkg.go.dev/net/url- Goプロジェクトのイシュートラッカー (Go issue 4792は今回の検索では見つかりませんでしたが、通常はここにバグや機能リクエストが記録されます): https://github.com/golang/go/issues
参考にした情報源リンク
- コミット情報:
/home/orange/Project/comemo/commit_data/15231.txt
- GitHub上のコミットページ: https://github.com/golang/go/commit/cd566958e938c695d09730dfcb7c2b8e76658f89
- HTTP/1.1 RFC 2616 (Hostヘッダーに関する情報): https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.23 (これは古いRFCですが、Hostヘッダーの基本的な概念を理解するのに役立ちます。より新しいRFC 7230も参照してください。)
- HTTP/1.1 RFC 7230 (Hostヘッダーに関する最新の情報): https://datatracker.ietf.org/doc/html/rfc7230#section-5.4