[インデックス 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