[インデックス 13263] ファイルの概要
このコミットは、Go言語の標準ライブラリである net/http
パッケージ内の request.go
ファイルに対する変更です。具体的には、HTTPリクエストのヘッダーから "Host" フィールドを削除する際の処理が req.Header.Del("Host")
から delete(req.Header, "Host")
へと変更されています。
コミット
commit 290115fdf4609c1fb0ba87aa1940a24308213543
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Sun Jun 3 22:40:16 2012 -0700
net/http: change a Del to delete
No need to fix case of "Host" string literal.
R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/6278049
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/290115fdf4609c1fb0ba87aa1940a24308213543
元コミット内容
このコミットの元のメッセージは以下の通りです。
net/http: change a Del to delete
No need to fix case of "Host" string literal.
R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/6278049
このメッセージは、net/http
パッケージにおいて、Del
メソッドを delete
関数に置き換える変更が行われたことを示しています。その理由として、「"Host" 文字列リテラルのケースを修正する必要がない」と述べられています。
変更の背景
Go言語の net/http
パッケージは、HTTPクライアントとサーバーの実装を提供します。HTTPヘッダーはキーと値のペアで構成され、キーは大文字・小文字を区別しないとされていますが、実際の実装では大文字・小文字の扱いが重要になる場合があります。
Goの http.Header
型は map[string][]string
のエイリアスであり、マップのキーは文字列です。HTTPヘッダーの仕様(RFC 7230など)では、ヘッダーフィールド名は大文字・小文字を区別しないとされていますが、Goのマップはキーの大文字・小文字を厳密に区別します。このため、http.Header
にアクセスする際には、正規化されたキー(通常は textproto.CanonicalMIMEHeaderKey
を使用して大文字・小文字を正規化された形式)を使用することが推奨されます。
req.Header.Del("Host")
のようなメソッドは、内部でキーの正規化を行ってからマップから要素を削除します。しかし、delete(req.Header, "Host")
のように直接マップ操作を行う場合、指定されたキーと完全に一致するキーのみが削除されます。
このコミットの背景には、net/http
パッケージが "Host" ヘッダーを特別に扱っているという事情があります。HTTPリクエストが受信されると、Host
ヘッダーの値は http.Request
構造体の Host
フィールドにパースされ、格納されます。その後、元のヘッダーマップから "Host" ヘッダーを削除することで、重複や不整合を防ぐ意図があります。
コミットメッセージにある「No need to fix case of "Host" string literal.」という記述は、http.Request
のパース処理において、"Host" ヘッダーが既に正規化された形で処理されているか、あるいはこの特定のコンテキストでは大文字・小文字を区別しない削除が不要であると判断されたことを示唆しています。つまり、req.Header.get("Host")
で取得した時点で、Host
ヘッダーは既に正規化されたキーでアクセスされており、その後に削除する際には、正規化されたキー "Host"
を使って直接 delete
関数を呼び出しても問題ない、という判断があったと考えられます。これにより、Del
メソッドが持つ正規化のオーバーヘッドを避けることができます。
前提知識の解説
Go言語の net/http
パッケージ
net/http
パッケージは、Go言語でHTTPクライアントおよびサーバーを構築するための基本的な機能を提供します。このパッケージは、HTTP/1.1の仕様に準拠しており、リクエストのパース、レスポンスの生成、ルーティング、ミドルウェアのサポートなど、Webアプリケーション開発に必要な多くの機能を含んでいます。
http.Request
構造体
http.Request
は、受信したHTTPリクエストを表す構造体です。この構造体には、リクエストメソッド(GET, POSTなど)、URL、ヘッダー、ボディなどの情報が含まれます。
req.Host
フィールド: HTTPリクエストのHost
ヘッダーの値を表す文字列フィールドです。このフィールドは、リクエストのターゲットホストを識別するために使用されます。req.Header
フィールド:http.Header
型のマップであり、HTTPリクエストのヘッダーを表します。http.Header
はmap[string][]string
のエイリアスで、キーはヘッダー名(例: "Content-Type", "User-Agent")、値はそのヘッダーの値のリストです。
http.Header
型とヘッダーの正規化
http.Header
は map[string][]string
のエイリアスですが、HTTPヘッダーフィールド名は大文字・小文字を区別しないというHTTPの仕様があります。しかし、Goのマップはキーの大文字・小文字を厳密に区別します。このため、http.Header
にヘッダーを追加したり、ヘッダーを取得したりする際には、ヘッダー名を正規化する必要があります。
Goの net/textproto
パッケージには CanonicalMIMEHeaderKey
という関数があり、これを使ってヘッダー名を正規化します。例えば、"content-type"
は "Content-Type"
に正規化されます。
map
の delete
関数と http.Header.Del
メソッド
-
delete(m map[K]V, key K)
: Go言語の組み込み関数で、マップm
から指定されたkey
に対応する要素を削除します。この関数は、キーの大文字・小文字を厳密に区別します。つまり、delete(myMap, "Key")
は"Key"
というキーを持つ要素のみを削除し、"key"
や"KEY"
というキーを持つ要素は削除しません。 -
http.Header.Del(key string)
:http.Header
型に定義されているメソッドで、指定されたkey
に対応するヘッダーを削除します。このメソッドは、内部でtextproto.CanonicalMIMEHeaderKey
を使用してkey
を正規化し、正規化されたキーに対応するヘッダーをマップから削除します。これにより、ユーザーが指定したキーの大文字・小文字に関わらず、正しいヘッダーが削除されることが保証されます。
技術的詳細
このコミットの技術的なポイントは、http.Header.Del("Host")
から delete(req.Header, "Host")
への変更が、Host
ヘッダーの特殊な扱いに基づいている点です。
通常、http.Header.Del
メソッドを使用する理由は、HTTPヘッダー名が大文字・小文字を区別しないという仕様に対応するためです。Del
メソッドは内部でキーを正規化するため、例えば req.Header.Del("host")
と呼び出しても、マップ内の "Host"
キーに対応する値が削除されます。
しかし、このコミットでは delete(req.Header, "Host")
と、組み込みの delete
関数を直接使用しています。これは、以下のいずれかの理由が考えられます。
Host
ヘッダーの特殊なパース処理:http.Request
のパース処理において、Host
ヘッダーは特別に扱われ、その値はreq.Host
フィールドに格納されます。この際、元のヘッダーマップからHost
ヘッダーを削除する目的は、req.Host
フィールドに値が移された後に、ヘッダーマップ内に重複するHost
エントリが存在しないようにするためです。- 正規化の不要性:
req.Header.get("Host")
の呼び出しが示唆するように、このコードパスではHost
ヘッダーが常に正規化されたキー"Host"
でアクセスされることが保証されている可能性があります。つまり、req.Header.get("Host")
が成功した場合、マップ内には"Host"
というキーが存在することが確実であり、その後に削除する際も"Host"
というキーを直接指定すれば十分である、という判断です。 - パフォーマンスの最適化:
http.Header.Del
メソッドは、キーの正規化処理(textproto.CanonicalMIMEHeaderKey
の呼び出し)を伴います。この正規化処理は、文字列操作やマップルックアップを伴うため、わずかながらオーバーヘッドが発生します。もし、削除対象のキーが常に正規化された形式であることが保証されている場合、直接delete
関数を呼び出すことで、このオーバーヘッドを削減し、パフォーマンスをわずかに向上させることができます。
コミットメッセージの「No need to fix case of "Host" string literal.」という記述は、上記の2番目と3番目の理由を強く裏付けています。つまり、このコンテキストでは "Host"
という文字列リテラルが常に正しいケース(正規化された形式)であるため、Del
メソッドが提供する大文字・小文字の正規化機能は不要であり、直接 delete
を使用することでコードを簡潔にし、潜在的なパフォーマンス向上を図っていると考えられます。
この変更は、net/http
パッケージの内部実装の最適化であり、外部からこのパッケージを利用するユーザーのコードに直接的な影響を与えるものではありません。しかし、Go言語の標準ライブラリがどのように細かな最適化を行っているかを示す良い例と言えます。
コアとなるコードの変更箇所
変更は src/pkg/net/http/request.go
ファイルの以下の部分です。
--- a/src/pkg/net/http/request.go
+++ b/src/pkg/net/http/request.go
@@ -515,7 +515,7 @@ func ReadRequest(b *bufio.Reader) (req *Request, err error) {
if req.Host == "" {
req.Host = req.Header.get("Host")
}
- req.Header.Del("Host")
+ delete(req.Header, "Host")
fixPragmaCacheControl(req.Header)
コアとなるコードの解説
変更されたコードブロックは、ReadRequest
関数内にあります。この関数は、bufio.Reader
からHTTPリクエストを読み込み、http.Request
構造体を生成する役割を担っています。
-
if req.Host == ""
: この条件文は、req.Host
フィールドがまだ設定されていない場合に実行されます。HTTPリクエストのパース中にHost
ヘッダーが見つからなかった場合や、他の方法でreq.Host
が初期化されていない場合に該当します。 -
req.Host = req.Header.get("Host")
:req.Header.get("Host")
は、req.Header
マップから "Host" ヘッダーの値を取得します。get
メソッドは、内部でヘッダー名を正規化して検索するため、"Host"
、"host"
、"HOST"
など、どのようなケースでヘッダーが送られてきても正しく値を取得できます。取得した値はreq.Host
フィールドに格納されます。 -
- req.Header.Del("Host")
: 変更前のコードでは、req.Header.Del("Host")
が呼び出されていました。これは、http.Header
型のDel
メソッドを使用して、ヘッダーマップから "Host" ヘッダーを削除するものです。前述の通り、Del
メソッドはキーを正規化してから削除を行います。 -
+ delete(req.Header, "Host")
: 変更後のコードでは、Goの組み込み関数であるdelete
が直接使用されています。これにより、req.Header
マップから"Host"
というキーを持つエントリが直接削除されます。この変更は、req.Host = req.Header.get("Host")
の行で既にHost
ヘッダーが正規化された形で処理され、req.Host
フィールドに格納されているため、その後の削除においては、正規化されたキー"Host"
を使って直接削除しても問題ないという判断に基づいています。これにより、Del
メソッドの正規化処理のオーバーヘッドを避けることができます。
この変更は、Host
ヘッダーが req.Host
フィールドに抽出された後、ヘッダーマップからそのエントリを削除するというロジックの効率化を目的としています。
関連リンク
- Go言語の
net/http
パッケージドキュメント: https://pkg.go.dev/net/http - Go言語の
net/textproto
パッケージドキュメント: https://pkg.go.dev/net/textproto - Goのコードレビューシステム (Gerrit) の変更リスト: https://golang.org/cl/6278049
参考にした情報源リンク
- RFC 7230 - Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing: https://datatracker.ietf.org/doc/html/rfc7230 (特にヘッダーフィールド名の大文字・小文字の区別に関するセクション)
- Go言語のマップに関するドキュメント: https://go.dev/blog/maps
- Go言語の
delete
組み込み関数に関するドキュメント: https://pkg.go.dev/builtin#delete - Go言語の
http.Header
型のソースコード (変更当時のバージョンに近いもの): https://github.com/golang/go/blob/release-branch.go1.0/src/pkg/net/http/header.go (当時のDel
メソッドの実装を確認するため) - Go言語の
http.Request
型のソースコード (変更当時のバージョンに近いもの): https://github.com/golang/go/blob/release-branch.go1.0/src/pkg/net/http/request.go (当時のReadRequest
関数の実装を確認するため) - Go言語の
textproto.CanonicalMIMEHeaderKey
のソースコード: https://github.com/golang/go/blob/master/src/net/textproto/reader.go (現在の実装)
I have generated the commit explanation as requested.