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

[インデックス 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.Headermap[string][]string のエイリアスで、キーはヘッダー名(例: "Content-Type", "User-Agent")、値はそのヘッダーの値のリストです。

http.Header 型とヘッダーの正規化

http.Headermap[string][]string のエイリアスですが、HTTPヘッダーフィールド名は大文字・小文字を区別しないというHTTPの仕様があります。しかし、Goのマップはキーの大文字・小文字を厳密に区別します。このため、http.Header にヘッダーを追加したり、ヘッダーを取得したりする際には、ヘッダー名を正規化する必要があります。

Goの net/textproto パッケージには CanonicalMIMEHeaderKey という関数があり、これを使ってヘッダー名を正規化します。例えば、"content-type""Content-Type" に正規化されます。

mapdelete 関数と 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 関数を直接使用しています。これは、以下のいずれかの理由が考えられます。

  1. Host ヘッダーの特殊なパース処理: http.Request のパース処理において、Host ヘッダーは特別に扱われ、その値は req.Host フィールドに格納されます。この際、元のヘッダーマップから Host ヘッダーを削除する目的は、req.Host フィールドに値が移された後に、ヘッダーマップ内に重複する Host エントリが存在しないようにするためです。
  2. 正規化の不要性: req.Header.get("Host") の呼び出しが示唆するように、このコードパスでは Host ヘッダーが常に正規化されたキー "Host" でアクセスされることが保証されている可能性があります。つまり、req.Header.get("Host") が成功した場合、マップ内には "Host" というキーが存在することが確実であり、その後に削除する際も "Host" というキーを直接指定すれば十分である、という判断です。
  3. パフォーマンスの最適化: 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 構造体を生成する役割を担っています。

  1. if req.Host == "": この条件文は、req.Host フィールドがまだ設定されていない場合に実行されます。HTTPリクエストのパース中に Host ヘッダーが見つからなかった場合や、他の方法で req.Host が初期化されていない場合に該当します。

  2. req.Host = req.Header.get("Host"): req.Header.get("Host") は、req.Header マップから "Host" ヘッダーの値を取得します。get メソッドは、内部でヘッダー名を正規化して検索するため、"Host""host""HOST" など、どのようなケースでヘッダーが送られてきても正しく値を取得できます。取得した値は req.Host フィールドに格納されます。

  3. - req.Header.Del("Host"): 変更前のコードでは、req.Header.Del("Host") が呼び出されていました。これは、http.Header 型の Del メソッドを使用して、ヘッダーマップから "Host" ヘッダーを削除するものです。前述の通り、Del メソッドはキーを正規化してから削除を行います。

  4. + delete(req.Header, "Host"): 変更後のコードでは、Goの組み込み関数である delete が直接使用されています。これにより、req.Header マップから "Host" というキーを持つエントリが直接削除されます。この変更は、req.Host = req.Header.get("Host") の行で既に Host ヘッダーが正規化された形で処理され、req.Host フィールドに格納されているため、その後の削除においては、正規化されたキー "Host" を使って直接削除しても問題ないという判断に基づいています。これにより、Del メソッドの正規化処理のオーバーヘッドを避けることができます。

この変更は、Host ヘッダーが req.Host フィールドに抽出された後、ヘッダーマップからそのエントリを削除するというロジックの効率化を目的としています。

関連リンク

参考にした情報源リンク

I have generated the commit explanation as requested.