[インデックス 19019] ファイルの概要
このコミットは、Go言語の標準ライブラリである net/http
パッケージ内の Request
構造体のフィールドに関するドキュメンテーションを明確化することを目的としています。特に、これらのフィールドがクライアントとサーバーのどちらのコンテキストで使用されるかによって、そのセマンティクスがどのように異なるかを詳細に説明しています。これにより、開発者が http.Request
をより正確に理解し、適切に使用できるようになります。
コミット
commit 9dbb185fb6902d9a1308f709deaa67460ccd6c02
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Wed Apr 2 21:05:41 2014 -0700
net/http: clarify Request fields' client-vs-server semantics
Fixes #7682
LGTM=adg
R=golang-codereviews, adg
CC=dsymonds, golang-codereviews, iant
https://golang.org/cl/83800043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/9dbb185fb6902d9a1308f709deaa67460ccd6c02
元コミット内容
net/http: clarify Request fields' client-vs-server semantics
Fixes #7682
変更の背景
このコミットの主な背景は、net/http
パッケージの Request
構造体に含まれる各フィールドのセマンティクスが、HTTPリクエストがクライアントによって送信される場合(クライアントリクエスト)と、サーバーによって受信される場合(サーバーリクエスト)とで異なるにもかかわらず、その違いがドキュメンテーションで十分に明確にされていなかった点にあります。
特に、関連するIssue #7682「net/http: Setting custom "Host" request header doesn't have effect」がこの変更の直接的なトリガーとなりました。このIssueでは、クライアント側で http.Request
の Header
フィールドを介して Host
ヘッダーを設定しようとしても、それが反映されないという問題が報告されていました。これは、Host
フィールドが Header
マップとは別に Request
構造体自体に存在し、クライアントリクエストにおいては Request.Host
フィールドが Host
ヘッダーの値を決定するという、より具体的なセマンティクスを持っていたためです。
このような混乱を解消し、開発者が http.Request
構造体の各フィールドの役割と挙動を、クライアントとサーバーの両方のコンテキストで正確に理解できるようにするために、既存のドキュメンテーションに詳細な説明が追加されました。これにより、APIの誤用を防ぎ、より堅牢なHTTPアプリケーションの開発を促進することが目的です。
前提知識の解説
このコミットの変更内容を深く理解するためには、以下の前提知識が必要です。
1. Go言語の net/http
パッケージ
net/http
パッケージは、Go言語でHTTPクライアントおよびサーバーを実装するための基本的な機能を提供します。このパッケージは、HTTP/1.1プロトコルをサポートし、リクエストのルーティング、ミドルウェアの統合、TLS(Transport Layer Security)のサポートなど、Webアプリケーション開発に必要な多くの機能を含んでいます。
2. HTTPリクエストの基本構造
HTTPリクエストは、クライアントがサーバーに情報を要求する際に使用するメッセージです。その基本的な構造は以下の要素から構成されます。
- リクエストライン (Request-Line):
- メソッド (Method):
GET
,POST
,PUT
,DELETE
など、実行したいアクションを示します。 - リクエストURI (Request-URI): リソースの識別子です。
- HTTPバージョン (HTTP-Version): 使用するHTTPプロトコルのバージョン(例:
HTTP/1.1
)。
- メソッド (Method):
- ヘッダーフィールド (Header Fields): リクエストに関する追加情報を提供します。例:
Host
,Content-Type
,User-Agent
など。 - メッセージボディ (Message Body):
POST
やPUT
リクエストなどで、サーバーに送信するデータが含まれます。
3. http.Request
構造体
net/http
パッケージにおける http.Request
構造体は、受信したHTTPリクエスト(サーバー側)または送信するHTTPリクエスト(クライアント側)を表現します。この構造体には、リクエストライン、ヘッダー、ボディなど、HTTPリクエストのすべての要素がフィールドとして含まれています。
4. クライアントとサーバーのセマンティクス
HTTPプロトコルにおいて、同じ概念(例: リクエスト)であっても、それがクライアントによって生成されるのか、サーバーによって処理されるのかによって、そのフィールドの解釈や挙動が異なる場合があります。
- クライアントリクエスト: クライアントがサーバーに送信するために構築するリクエスト。この場合、
Request
構造体のフィールドは、送信されるHTTPメッセージの各部分を構成するために使用されます。 - サーバーリクエスト: サーバーがクライアントから受信したリクエストを解析して生成するリクエスト。この場合、
Request
構造体のフィールドは、受信したHTTPメッセージの各部分を表現します。
この違いは、特にヘッダーの処理やURLの解釈において重要になります。例えば、クライアントが Host
ヘッダーを明示的に設定したい場合と、サーバーが受信した Host
ヘッダーを解析する場合とでは、Request
構造体の Host
フィールドの役割が異なります。
5. RFC 2616 (Hypertext Transfer Protocol -- HTTP/1.1)
HTTP/1.1プロトコルの主要な仕様を定義するRFC(Request for Comments)文書です。このRFCは、HTTPリクエストの構造、ヘッダーフィールドの定義、各種メソッドのセマンティクスなど、HTTPプロトコルに関する詳細な情報を提供します。特に、リクエストURIの解釈や Host
ヘッダーの役割に関する記述は、このコミットの理解に不可欠です。
技術的詳細
このコミットは、src/pkg/net/http/request.go
ファイル内の http.Request
構造体の定義に、各フィールドがクライアントとサーバーのどちらのコンテキストでどのように解釈されるかについての詳細なコメントを追加しています。これにより、APIの利用者がフィールドの挙動を誤解する可能性を減らし、より正確なプログラミングを促します。
具体的に変更が加えられたフィールドとその説明のポイントは以下の通りです。
-
Request
構造体全体のコメント:// A Request represents an HTTP request received by a server // or to be sent by a client.
という既存の説明に加えて、// The field semantics differ slightly between client and server // usage.
という文が追加され、フィールドのセマンティクスがクライアントとサーバーで異なることが明示されました。- さらに、
// In addition to the notes on the fields below, see the // documentation for Request.Write and RoundTripper.
という指示が追加され、Request.Write
メソッドとRoundTripper
インターフェースのドキュメンテーションも参照するよう促しています。これは、クライアントリクエストの送信メカニズムを理解する上で重要です。
-
Method
フィールド:- 既存の
Method string // GET, POST, PUT, etc.
に加えて、// For client requests an empty string means GET.
という説明が追加されました。これは、クライアントがリクエストメソッドを明示的に指定しない場合に、デフォルトでGET
メソッドとして扱われることを示しています。
- 既存の
-
URL
フィールド:URL
フィールドのコメントが大幅に拡張されました。- サーバーリクエストの場合、
URL
はRequestURI
から解析されること、そしてほとんどのリクエストではPath
とRawQuery
以外のフィールドは空になること(RFC 2616, Section 5.1.2を参照)が明確にされました。 - クライアントリクエストの場合、
URL
のHost
フィールドが接続先のサーバーを指定し、Request
構造体自体のHost
フィールドがオプションで送信するHost
ヘッダーの値を指定することが説明されました。これは、Issue #7682で報告されたHost
ヘッダーの問題に直接関連する重要な説明です。
-
Proto
フィールド:- 既存の
// Outgoing requests always use HTTP/1.1.
が// Client requests always use HTTP/1.1.
に変更されました。これにより、「Outgoing requests」という曖昧な表現が「Client requests」というより具体的な表現に修正され、クライアントが送信するリクエストのプロトコルバージョンが常にHTTP/1.1であることが明確になりました。
- 既存の
-
Header
フィールド:// For client requests certain headers are automatically // added and may override values in Header.
という説明が追加されました。これは、クライアントがリクエストを送信する際に、net/http
パッケージが自動的に特定のヘッダー(例:Content-Length
,Host
など)を追加または上書きする可能性があることを示唆しています。// See the documentation for the Request.Write method.
という指示も追加され、Request.Write
メソッドのドキュメンテーションを参照することで、これらの自動的なヘッダー処理の詳細を理解できることが示されました。
-
Body
フィールド:- クライアントリクエストの場合、
nil
のBody
はリクエストにボディがないことを意味すること(例:GET
リクエスト)が明確化されました。 - サーバーリクエストの場合、
Body
は常にnon-nil
であるが、ボディが存在しない場合はすぐにEOF
を返すこと、そしてサーバーがリクエストボディをクローズする責任があることが明確化されました。
- クライアントリクエストの場合、
-
ContentLength
フィールド:- 既存の
// For outgoing requests, a value of 0 means unknown if Body is not nil.
が// For client requests, a value of 0 means unknown if Body is not nil.
に変更されました。これもProto
フィールドと同様に、「outgoing requests」を「client requests」に修正し、クライアントリクエストにおけるContentLength
のセマンティクスを明確にしています。
- 既存の
-
Close
フィールド:- 既存の
// Close indicates whether to close the connection after // replying to this request.
に加えて、// (for servers) or after sending // the request (for clients).
という説明が追加されました。これにより、Close
フィールドがサーバーとクライアントの両方のコンテキストで接続のクローズを指示する役割を持つことが明確になりました。
- 既存の
-
Host
フィールド:Host
フィールドのコメントが大幅に拡張され、クライアントとサーバーのセマンティクスが明確に区別されました。- サーバーリクエストの場合、
Host
はリクエストされたURLのホストを指定し、RFC 2616に従ってHost
ヘッダーの値またはURL自体のホスト名から取得されることが説明されました。 - クライアントリクエストの場合、
Host
はオプションで送信するHost
ヘッダーを上書きする役割を持つこと、そして空の場合にはRequest.Write
メソッドがURL.Host
の値を使用することが明確にされました。これは、Issue #7682の解決に直接貢献する最も重要な変更点の一つです。
-
Trailer
フィールド:- 既存の
// For server requests, Trailer is only populated after Body has been
に加えて、// closed or fully consumed.
という説明が追加されました。これにより、サーバーリクエストにおけるTrailer
フィールドがいつ設定されるかの条件がより明確になりました。
- 既存の
これらの変更は、http.Request
構造体の各フィールドの役割と挙動を、クライアントとサーバーの異なる利用シナリオにおいて、より正確かつ詳細に記述することで、Goの net/http
パッケージのドキュメンテーションの品質を向上させています。
コアとなるコードの変更箇所
--- a/src/pkg/net/http/request.go
+++ b/src/pkg/net/http/request.go
@@ -69,18 +69,31 @@ var reqWriteExcludeHeader = map[string]bool{\n
// A Request represents an HTTP request received by a server
// or to be sent by a client.
+//
+// The field semantics differ slightly between client and server
+// usage. In addition to the notes on the fields below, see the
+// documentation for Request.Write and RoundTripper.
type Request struct {\n-\tMethod string // GET, POST, PUT, etc.\n+\t// Method specifies the HTTP method (GET, POST, PUT, etc.).\n+\t// For client requests an empty string means GET.\n+\tMethod string\n \n-\t// URL is created from the URI supplied on the Request-Line
-\t// as stored in RequestURI.\n+\t// URL specifies either the URI being requested (for server
+\t// requests) or the URL to access (for client requests).\n+\t//
+\t// For server requests the URL is parsed from the URI
+\t// supplied on the Request-Line as stored in RequestURI. For
+\t// most requests, fields other than Path and RawQuery will be
+\t// empty. (See RFC 2616, Section 5.1.2)\n \t//
-\t// For most requests, fields other than Path and RawQuery
-\t// will be empty. (See RFC 2616, Section 5.1.2)\n+\t// For client requests, the URL\'s Host specifies the server to
+\t// connect to, while the Request\'s Host field optionally
+\t// specifies the Host header value to send in the HTTP
+\t// request.\n \tURL *url.URL\n \n \t// The protocol version for incoming requests.\n-\t// Outgoing requests always use HTTP/1.1.\n+\t// Client requests always use HTTP/1.1.\n \tProto string // \"HTTP/1.0\"\n \tProtoMajor int // 1\n \tProtoMinor int // 0\n@@ -104,15 +117,20 @@ type Request struct {\n \t// The request parser implements this by canonicalizing the\n \t// name, making the first character and any characters\n \t// following a hyphen uppercase and the rest lowercase.\n+\t//\n+\t// For client requests certain headers are automatically\n+\t// added and may override values in Header.\n+\t//\n+\t// See the documentation for the Request.Write method.\n \tHeader Header\n \n \t// Body is the request\'s body.\n \t//\n-\t// For client requests, a nil body means the request has no\n+\t// For client requests a nil body means the request has no\n \t// body, such as a GET request. The HTTP Client\'s Transport\n \t// is responsible for calling the Close method.\n \t//\n-\t// For server requests, the Request Body is always non-nil\n+\t// For server requests the Request Body is always non-nil\n \t// but will return EOF immediately when no body is present.\n \t// The Server will close the request body. The ServeHTTP\n \t// Handler does not need to.\n@@ -122,7 +140,7 @@ type Request struct {\n \t// The value -1 indicates that the length is unknown.\n \t// Values >= 0 indicate that the given number of bytes may\n \t// be read from Body.\n-\t// For outgoing requests, a value of 0 means unknown if Body is not nil.\n+\t// For client requests, a value of 0 means unknown if Body is not nil.\n \tContentLength int64\n \n \t// TransferEncoding lists the transfer encodings from outermost to\n@@ -133,13 +151,18 @@ type Request struct {\n \tTransferEncoding []string\n \n \t// Close indicates whether to close the connection after\n-\t// replying to this request.\n+\t// replying to this request (for servers) or after sending\n+\t// the request (for clients).\n \tClose bool\n \n-\t// The host on which the URL is sought.\n-\t// Per RFC 2616, this is either the value of the Host: header\n-\t// or the host name given in the URL itself.\n+\t// For server requests Host specifies the host on which the\n+\t// URL is sought. Per RFC 2616, this is either the value of\n+\t// the \"Host\" header or the host name given in the URL itself.\n \t// It may be of the form \"host:port\".\n+\t//\n+\t// For client requests Host optionally overrides the Host\n+\t// header to send. If empty, the Request.Write method uses\n+\t// the value of URL.Host.\n \tHost string\n \n \t// Form contains the parsed form data, including both the URL\n@@ -162,7 +185,7 @@ type Request struct {\n \t// Trailer maps trailer keys to values. Like for Header, if the\n \t// response has multiple trailer lines with the same key, they will be\n \t// concatenated, delimited by commas.\n-\t// For server requests, Trailer is only populated after Body has been\n+\t// For server requests Trailer is only populated after Body has been\n \t// closed or fully consumed.\n \t// Trailer support is only partially complete.\n \tTrailer Header\n```
## コアとなるコードの解説
このコミットは、`src/pkg/net/http/request.go` ファイル内の `http.Request` 構造体のコメントを修正・追加することで、そのフィールドのセマンティクスを明確にしています。以下に主要な変更点とその解説を示します。
1. **`Request` 構造体全体のコメントの追加**:
* 変更前:
```go
// A Request represents an HTTP request received by a server
// or to be sent by a client.
```
* 変更後:
```go
// A Request represents an HTTP request received by a server
// or to be sent by a client.
//
// The field semantics differ slightly between client and server
// usage. In addition to the notes on the fields below, see the
// documentation for Request.Write and RoundTripper.
```
* **解説**: `Request` 構造体のフィールドがクライアントとサーバーで異なるセマンティクスを持つことを明示し、さらに `Request.Write` メソッドと `RoundTripper` インターフェースのドキュメンテーションを参照するよう促しています。これは、このコミットの目的である「クライアントとサーバーのセマンティクスの明確化」を構造体レベルで宣言するものです。
2. **`Method` フィールドのコメントの追加**:
* 変更前:
```go
Method string // GET, POST, PUT, etc.
```
* 変更後:
```go
// Method specifies the HTTP method (GET, POST, PUT, etc.).
// For client requests an empty string means GET.
Method string
```
* **解説**: クライアントリクエストにおいて `Method` フィールドが空文字列の場合、それは `GET` メソッドとして扱われるという具体的な挙動が追加されました。これにより、クライアント側での `Method` のデフォルト挙動が明確になります。
3. **`URL` フィールドのコメントの拡張**:
* 変更前:
```go
// URL is created from the URI supplied on the Request-Line
// as stored in RequestURI.
//
// For most requests, fields other than Path and RawQuery
// will be empty. (See RFC 2616, Section 5.1.2)
URL *url.URL
```
* 変更後:
```go
// URL specifies either the URI being requested (for server
// requests) or the URL to access (for client requests).
//
// For server requests the URL is parsed from the URI
// supplied on the Request-Line as stored in RequestURI. For
// most requests, fields other than Path and RawQuery will be
// empty. (See RFC 2616, Section 5.1.2)
//
// For client requests, the URL's Host specifies the server to
// connect to, while the Request's Host field optionally
// specifies the Host header value to send in the HTTP
// request.
URL *url.URL
```
* **解説**: `URL` フィールドがサーバーリクエストとクライアントリクエストで異なる役割を持つことが詳細に説明されました。特に、クライアントリクエストの場合、`URL.Host` が接続先サーバーを、`Request.Host` が `Host` ヘッダーの値を指定するという重要な区別が明確にされました。これは、`Host` ヘッダーの挙動に関する混乱を解消する上で非常に重要です。
4. **`Proto` フィールドのコメントの修正**:
* 変更前:
```go
// Outgoing requests always use HTTP/1.1.
Proto string // "HTTP/1.0"
```
* 変更後:
```go
// Client requests always use HTTP/1.1.
Proto string // "HTTP/1.0"
```
* **解説**: 「Outgoing requests」という曖昧な表現が「Client requests」に修正され、クライアントが送信するリクエストのプロトコルバージョンが常にHTTP/1.1であることがより明確になりました。
5. **`Header` フィールドのコメントの追加**:
* 変更前:
```go
// The request parser implements this by canonicalizing the
// name, making the first character and any characters
// following a hyphen uppercase and the rest lowercase.
Header Header
```
* 変更後:
```go
// The request parser implements this by canonicalizing the
// name, making the first character and any characters
// following a hyphen uppercase and the rest lowercase.
//
// For client requests certain headers are automatically
// added and may override values in Header.
//
// See the documentation for the Request.Write method.
Header Header
```
* **解説**: クライアントリクエストにおいて、特定のヘッダーが自動的に追加または上書きされる可能性があること、そしてその詳細が `Request.Write` メソッドのドキュメンテーションにあることが追記されました。これは、開発者が `Header` フィールドを直接操作する際の注意点を示しています。
6. **`Body` フィールドのコメントの修正**:
* 変更前:
```go
// For client requests, a nil body means the request has no
// body, such as a GET request. The HTTP Client's Transport
// is responsible for calling the Close method.
//
// For server requests, the Request Body is always non-nil
// but will return EOF immediately when no body is present.
// The Server will close the request body. The ServeHTTP
// Handler does not need to.
Body io.ReadCloser
```
* 変更後:
```go
// For client requests a nil body means the request has no
// body, such as a GET request. The HTTP Client's Transport
// is responsible for calling the Close method.
//
// For server requests the Request Body is always non-nil
// but will return EOF immediately when no body is present.
// The Server will close the request body. The ServeHTTP
// Handler does not need to.
Body io.ReadCloser
```
* **解説**: 細かい修正ですが、「For client requests, a nil body...」と「For server requests, the Request Body...」のカンマが削除され、より簡潔な表現になっています。意味的な変更はありません。
7. **`ContentLength` フィールドのコメントの修正**:
* 変更前:
```go
// For outgoing requests, a value of 0 means unknown if Body is not nil.
ContentLength int64
```
* 変更後:
```go
// For client requests, a value of 0 means unknown if Body is not nil.
ContentLength int64
```
* **解説**: `Proto` フィールドと同様に、「outgoing requests」が「client requests」に修正され、クライアントリクエストにおける `ContentLength` のセマンティクスがより明確になりました。
8. **`Close` フィールドのコメントの拡張**:
* 変更前:
```go
// Close indicates whether to close the connection after
// replying to this request.
Close bool
```
* 変更後:
```go
// Close indicates whether to close the connection after
// replying to this request (for servers) or after sending
// the request (for clients).
Close bool
```
* **解説**: `Close` フィールドがサーバーとクライアントの両方のコンテキストで接続のクローズを指示する役割を持つことが明確にされました。
9. **`Host` フィールドのコメントの拡張**:
* 変更前:
```go
// The host on which the URL is sought.
// Per RFC 2616, this is either the value of the Host: header
// or the host name given in the URL itself.
// It may be of the form "host:port".
Host string
```
* 変更後:
```go
// For server requests Host specifies the host on which the
// URL is sought. Per RFC 2616, this is either the value of
// the "Host" header or the host name given in the URL itself.
// It may be of the form "host:port".
//
// For client requests Host optionally overrides the Host
// header to send. If empty, the Request.Write method uses
// the value of URL.Host.
Host string
```
* **解説**: `Host` フィールドのセマンティクスが、サーバーリクエストとクライアントリクエストで明確に区別されました。クライアントリクエストの場合、`Host` フィールドが `Host` ヘッダーを上書きする役割を持つこと、そして空の場合には `URL.Host` が使用されることが明記されました。これは、Issue #7682で報告された問題の根本原因を説明し、解決策を示唆する重要な変更です。
10. **`Trailer` フィールドのコメントの修正**:
* 変更前:
```go
// For server requests, Trailer is only populated after Body has been
// Trailer support is only partially complete.
Trailer Header
```
* 変更後:
```go
// For server requests Trailer is only populated after Body has been
// closed or fully consumed.
// Trailer support is only partially complete.
Trailer Header
```
* **解説**: サーバーリクエストにおいて `Trailer` フィールドがいつ設定されるか(ボディがクローズまたは完全に消費された後)の条件がより明確になりました。
これらの変更は、Goの `net/http` パッケージのドキュメンテーションの品質を大幅に向上させ、開発者が `http.Request` 構造体をより正確かつ効果的に使用できるようにするためのものです。
## 関連リンク
* **Go CL (Code Review) 83800043**: [https://golang.org/cl/83800043](https://golang.org/cl/83800043)
* **GitHub Issue #7682**: [https://github.com/golang/go/issues/7682](https://github.com/golang/go/issues/7682)
## 参考にした情報源リンク
* **RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1**: [https://www.w3.org/Protocols/rfc2616/rfc2616.html](https://www.w3.org/Protocols/rfc2616/rfc2616.html)
* **GoDoc - net/http package**: [https://pkg.go.dev/net/http](https://pkg.go.dev/net/http)
* **GoDoc - net/url package**: [https://pkg.go.dev/net/url](https://pkg.go.dev/net/url)
* **GoDoc - http.Request.Write method**: [https://pkg.go.dev/net/http#Request.Write](https://pkg.go.dev/net/http#Request.Write)
* **GoDoc - http.RoundTripper interface**: [https://pkg.go.dev/net/http#RoundTripper](https://pkg.go.dev/net/http#RoundTripper)