[インデックス 1805] ファイルの概要
このコミットは、Go言語の標準ライブラリの一部である http
パッケージ(当時の src/lib/http
)に対するドキュメントの追加と改善を目的としています。特に、Request
および URL
構造体のフィールド、および関連するユーティリティ関数に対する詳細なコメントが追加され、コードの可読性と理解度が向上しています。
コミット
commit 32bf48c6d8f5d8cdb163dc366fa2f8335c7e39d2
Author: Russ Cox <rsc@golang.org>
Date: Wed Mar 11 12:45:53 2009 -0700
document http
R=r
DELTA=84 (63 added, 4 deleted, 17 changed)
OCL=25950
CL=26126
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/32bf48c6d8f5d8cdb163dc366fa2f8335c7e39d2
元コミット内容
このコミットの元々の内容は「document http」であり、HTTPパッケージのドキュメント化が目的であることを明確に示しています。差分情報 (DELTA=84 (63 added, 4 deleted, 17 changed)
) からも、大幅なコードの追加(主にコメント)が行われたことがわかります。
変更の背景
Go言語は2009年にGoogleで開発が始まり、このコミットは初期の段階で行われたものです。当時のGo言語の標準ライブラリはまだ発展途上にあり、特にネットワーク関連のパッケージは基本的な機能が実装されつつある段階でした。
このコミットが行われた背景には、以下のような点が考えられます。
- コードベースの成熟: 初期開発段階では機能の実装が優先されがちですが、ある程度の機能が揃ってくると、その機能がどのように動作し、どのように使用されるべきかを明確にするためのドキュメントが不可欠になります。
- 利用者の増加への準備: Go言語がオープンソースとして公開され、より多くの開発者に利用されるようになるにつれて、コードの内部構造やAPIの意図を理解するための詳細なドキュメントが求められます。
- 設計意図の明確化:
http
パッケージはWebアプリケーション開発において非常に重要なコンポーネントであり、その設計意図や各フィールド・関数の役割を明確にすることで、将来的な拡張やメンテナンスが容易になります。 - RFC準拠の明示: HTTPプロトコルはRFC(Request for Comments)によって厳密に定義されています。コードがどのRFCに準拠しているか、あるいは特定の挙動がRFCのどの部分に基づいているかを明示することは、プロトコル実装の正確性を保証し、開発者が仕様を理解する上で役立ちます。特に
Referer
のスペルミスに関するコメントは、HTTPの歴史的な経緯とRFCの記述を反映したものです。
このコミットは、Go言語の http
パッケージが単なる機能提供だけでなく、その利用者がコードを深く理解し、適切に利用できるようにするための基盤を固める一環として行われたと言えます。
前提知識の解説
このコミットの変更内容を理解するためには、以下の前提知識があると役立ちます。
- HTTP (Hypertext Transfer Protocol):
- Web上でデータを交換するためのプロトコル。クライアント(ブラウザなど)とサーバー間でリクエストとレスポンスの形式で通信が行われます。
- HTTPリクエスト: クライアントがサーバーに送るメッセージ。メソッド(GET, POSTなど)、URL、HTTPバージョン、ヘッダー(
Host
,User-Agent
,Accept-Language
など)、およびボディ(POSTリクエストの場合など)で構成されます。 - HTTPヘッダー: リクエストやレスポンスに関する追加情報を提供するキーと値のペア。ヘッダー名は通常、大文字・小文字を区別しないとされています(例:
Content-Type
とcontent-type
は同じ意味)。 - HTTPステータスコード: サーバーがリクエストに対して返信する3桁の数字コード(例: 200 OK, 404 Not Found, 500 Internal Server Error)。RFC 2616などで定義されています。
- URL (Uniform Resource Locator):
- インターネット上のリソースのアドレスを示す文字列。
- 一般的な形式:
scheme://[userinfo@]host/path[?query][#fragment]
- スキーム (scheme): プロトコル(例:
http
,https
,ftp
)。 - ホスト (host): サーバーのドメイン名またはIPアドレス。
- パス (path): サーバー上のリソースの場所。
- クエリ (query): リソースに渡す追加のパラメータ(例:
?key=value&another=param
)。 - フラグメント (fragment): リソース内の特定の部分を指す識別子(例:
#section1
)。これは通常、クライアント側で処理され、サーバーには送信されません。 - URLエンコーディング: URLに使用できない文字(スペース、日本語など)を
%xx
の形式で表現する仕組み。%AB
は16進数AB
に対応するバイトを表します。
- Go言語の基本的な概念:
- パッケージ (package): 関連する機能や型をまとめたもの。他のファイルから利用するために
import
されます。 - 構造体 (struct): 複数のフィールド(変数)をまとめた複合データ型。
- メソッド (method): 構造体に関連付けられた関数。
- エラーハンドリング: Goではエラーは戻り値として明示的に扱われます。
os.NewError
は新しいエラー型を作成するために使用されます。 bufio
パッケージ: バッファリングされたI/O操作を提供し、効率的な読み書きを可能にします。os
パッケージ: オペレーティングシステムとのインタフェースを提供します。
- パッケージ (package): 関連する機能や型をまとめたもの。他のファイルから利用するために
技術的詳細
このコミットの技術的な詳細は、主にGo言語の http
パッケージにおけるドキュメンテーションの追加と、それに伴うコードの意図の明確化にあります。
src/lib/http/request.go
の変更点
-
パッケージコメントの追加:
// The http package implements parsing of HTTP requests and URLs // and provides an extensible HTTP server. // // In the future it should also implement parsing of HTTP replies // and provide methods to fetch URLs via HTTP. package http
http
パッケージの主要な役割(HTTPリクエストとURLのパース、拡張可能なHTTPサーバーの提供)を明確にしています。また、将来的な展望として、HTTPレスポンスのパースとURLフェッチ機能の実装が示唆されています。これは、当時のGoのhttp
パッケージがまだ初期段階であり、今後の機能拡張の方向性を示していたことを意味します。 -
エラー変数群へのコメント追加:
LineTooLong
,ValueTooLong
,BadRead
,BadHTTPVersion
などのエラー変数に対して// HTTP request parsing errors.
というコメントが追加され、これらのエラーがHTTPリクエストのパース中に発生するものであることが明示されました。 -
Request
構造体フィールドの詳細な説明:Request
構造体の各フィールドに、その役割、期待される値、およびHTTPプロトコルとの関連性に関する詳細なコメントが追加されました。RawUrl
: リクエストで与えられた生のURL。Url
: パースされたURL
構造体。Header
: HTTPヘッダーのマッピング。特に、HTTPヘッダー名が大文字・小文字を区別しないこと、およびGoのパーサーがヘッダー名を正規化(Canonicalization)する方法(最初の文字とハイフンの後の文字を大文字にし、残りを小文字にする)が詳細に説明されています。これは、開発者がヘッダーを扱う際の重要な情報です。Close
: このリクエストへの応答後にコネクションを閉じるべきかどうか。Host
: URLが求められるホスト。RFC 2616に基づき、Host:
ヘッダーの値またはURL自体に含まれるホスト名から取得されることが説明されています。Referer
: リクエストで送信された参照元URL。Referer
がHTTPの初期からのスペルミスであること、そしてGoがreq.Referer
フィールドとして提供することで、開発者が誤ってreq.Referrer
とタイプミスするのをコンパイラが診断できる利点が説明されています。これは、Goの設計哲学である「実用性」と「エラーの早期発見」を反映しています。UserAgent
: リクエストで送信されたUser-Agent:
ヘッダー文字列。
-
ProtoAtLeast
メソッドへのコメント追加:ProtoAtLeast
メソッドが、リクエストで使用されているHTTPプロトコルが指定されたメジャーバージョンとマイナーバージョン以上であるかどうかを返すことを説明しています。 -
CanonicalHeaderKey
関数へのコメント追加: HTTPヘッダーキーの正規化形式を返す関数であることが説明されています。最初の文字とハイフンの後の文字を大文字にし、残りを小文字にするという具体的な正規化ルールが示されています(例: "accept-encoding" は "Accept-Encoding" になる)。これは、Request.Header
のコメントで説明されているヘッダー名の正規化処理と密接に関連しています。 -
ReadRequest
関数へのコメント修正: 既存のコメントがより明確な表現に修正されました。
src/lib/http/status.go
の変更点
- ステータスコードコメントの修正:
既存のコメント
// HTTP status codes. See RFC 2616.
が// HTTP status codes, defined in RFC 2616.
に変更され、HTTPステータスコードがRFC 2616で「定義されている」という、より正確な表現になりました。
src/lib/http/url.go
の変更点
-
ParseURL
エラー変数群へのコメント追加:BadURL
エラー変数に対して// Errors introduced by ParseURL.
というコメントが追加され、このエラーがURLパース中に発生するものであることが明示されました。 -
URLUnescape
関数への詳細な説明: URLエンコードされた文字列をアンエスケープする関数であることが説明されています。%AB
をバイト0xAB
に変換すること、および各%
の後に2つの16進数が続かない場合にBadURL
エラーを返すことが明記されています。 -
URL
構造体フィールドの詳細な説明:URL
構造体がパースされたURL(URI参照)を表すこと、およびその一般的な形式scheme://[userinfo@]host/path[?query][#fragment]
が示されています。各フィールド(Raw
,Scheme
,RawPath
,Authority
,Userinfo
,Host
,Path
,Query
,Fragment
)についても、その役割が簡潔に説明されています。 -
ParseURL
関数へのコメント追加とBUGコメント:ParseURL
がrawurl
をURL構造体にパースすること、およびrawurl
が#fragment
サフィックスを持たないと仮定されていること(WebブラウザがサーバーにURLを送信する前にフラグメントを削除するため)が説明されています。// BUG(rsc): ParseURL should canonicalize the path, removing unnecessary . and .. elements.
というBUG
コメントが追加されました。これは、ParseURL
が将来的にパスの正規化(.
や..
の要素の削除)を行うべきであるという、既知の改善点を示しています。これは、Goの初期開発における透明性と、今後の開発計画の一部を示唆しています。
-
ParseURLReference
関数へのコメント追加:ParseURLReference
がParseURL
と同様だが、末尾の#fragment
を許容することが説明されています。これは、URLのパースにおいてフラグメントの有無が異なる2つのシナリオに対応するためのものです。
これらの変更は、Goの http
パッケージのAPIがどのように機能し、どのように使用されるべきかについて、開発者により明確なガイダンスを提供することを目的としています。特に、HTTPプロトコルの微妙な点(ヘッダーの正規化、Referer
のスペルミスなど)に関する説明は、Goの http
パッケージがプロトコル仕様に忠実に、かつ実用的に実装されていることを示しています。
コアとなるコードの変更箇所
このコミットのコアとなるコードの変更箇所は、主に以下の3つのファイルにおけるコメントの追加と修正です。
-
src/lib/http/request.go
:- パッケージレベルのコメント追加。
Request
構造体の各フィールド(RawUrl
,Url
,Header
,Close
,Host
,Referer
,UserAgent
)への詳細なコメント追加。ProtoAtLeast
メソッドへのコメント追加。CanonicalHeaderKey
関数へのコメント追加。ReadRequest
関数のコメント修正。
-
src/lib/http/status.go
:- パッケージレベルのコメント修正。
-
src/lib/http/url.go
:URLUnescape
関数への詳細なコメント追加。URL
構造体の各フィールド(Raw
,Scheme
,RawPath
,Authority
,Userinfo
,Host
,Path
,Query
,Fragment
)へのコメント追加。ParseURL
関数へのコメント追加とBUG
コメントの追加。ParseURLReference
関数へのコメント追加。
これらの変更は、既存のコードの振る舞いを変更するものではなく、その意図と使用方法を明確にするためのドキュメンテーションの強化に焦点を当てています。
コアとなるコードの解説
以下に、変更された主要なコードブロックと、追加されたコメントが何を意味するかを解説します。
src/lib/http/request.go
パッケージコメント
// The http package implements parsing of HTTP requests and URLs
// and provides an extensible HTTP server.
//
// In the future it should also implement parsing of HTTP replies
// and provide methods to fetch URLs via HTTP.
package http
解説: これは http
パッケージ全体の目的を説明するものです。当時のGoの http
パッケージが、リクエストとURLのパース、そしてHTTPサーバー機能を提供していたことを示しています。また、「将来的にHTTPレスポンスのパースとURLフェッチ機能も実装されるべき」という記述は、パッケージのロードマップや今後の拡張計画を垣間見ることができます。
Request
構造体フィールドのコメント
type Request struct {
Method string; // GET, PUT,etc.
- RawUrl string;
- Url *URL; // URI after GET, PUT etc.
+ RawUrl string; // The raw URL given in the request.
+ Url *URL; // URL after GET, PUT etc.
Proto string; // "HTTP/1.0"
ProtoMajor int; // 1
ProtoMinor int; // 0
+ // A header mapping request lines to their values.
+ // If the header says
+ //
+ // Accept-Language: en-us
+ // accept-encoding: gzip, deflate
+ // Connection: keep-alive
+ //
+ // then
+ //
+ // Header = map[string]string{\n+\t// "Accept-Encoding": "en-us",
+ // "Accept-Language": "gzip, deflate",
+ // "Connection": "keep-alive"
+ // }
+ //
+ // HTTP defines that header names are case-insensitive.
+ // 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 map[string] string;
+ // Whether to close the connection after replying to this request.
Close bool;
+\n+\t// The host on which the URL is sought.
+\t// Per RFC 2616, this is either the value of the Host: header
+\t// or the host name given in the URL itself.
Host string;
-\tReferer string; // referer [sic]
+\n+\t// The referring URL, if sent in the request.
+\t//
+\t// Referer is misspelled as in the request itself,
+\t// a mistake from the earliest days of HTTP.
+\t// This value can also be fetched from the Header map
+\t// as Header["Referer"]; the benefit of making it
+\t// available as a structure field is that the compiler
+\t// can diagnose programs that use the alternate
+\t// (correct English) spelling req.Referrer but cannot
+\t// diagnose programs that use Header["Referrer"].
+\tReferer string;
+\n+\t// The User-Agent: header string, if sent in the request.
UserAgent string;
}
解説: Request
構造体はHTTPリクエストのパースされた表現です。追加されたコメントは、各フィールドの具体的な意味と、HTTPプロトコルとの関連性を詳細に説明しています。特に Header
フィールドに関するコメントは、HTTPヘッダー名の大文字・小文字の区別がないという仕様と、Goのパーサーがどのようにヘッダー名を正規化するか(例: Accept-Encoding
)を明確にしています。また、Referer
フィールドに関するコメントは、このヘッダー名の歴史的なスペルミスに言及し、Goが Referer
を構造体フィールドとして提供する理由(コンパイラによるタイプミス検出の利点)を説明しており、Goの設計思想の一端が伺えます。
CanonicalHeaderKey
関数のコメント
func CanonicalHeaderKey(s string) string {
// ...
}
+// CanonicalHeaderKey returns the canonical format of the
+// HTTP header key s. The canonicalization converts the first
+// letter and any letter following a hyphen to upper case;
+// the rest are converted to lowercase. For example, the
+// canonical key for "accept-encoding" is "Accept-Encoding".
func CanonicalHeaderKey(s string) string {
if t, ok := cmap[s]; ok {
return t;
解説: このコメントは、CanonicalHeaderKey
関数がHTTPヘッダーキーを正規化された形式に変換する役割を担っていることを説明しています。正規化のルール(最初の文字とハイフンの後の文字を大文字にし、残りを小文字にする)が具体例とともに示されており、Request.Header
のコメントで述べられているヘッダー名の正規化処理の具体的な実装方法を補完しています。
src/lib/http/url.go
URLUnescape
関数のコメント
// Unescape %xx into hex.
+// URLUnescape unescapes a URL-encoded string,
+// converting %AB into the byte 0xAB.
+// It returns a BadURL error if each % is not followed
+// by two hexadecimal digits.
func URLUnescape(s string) (string, *os.Error) {
// Count %, check that they're well-formed.
n := 0;
解説: URLUnescape
関数は、URLエンコードされた文字列をデコードする役割を持ちます。追加されたコメントは、%AB
の形式を対応するバイト値に変換すること、そして %
の後に有効な16進数が続かない場合に BadURL
エラーを返すという、関数の具体的な動作とエラー条件を明確にしています。
URL
構造体フィールドのコメント
+// A URL represents a parsed URL (technically, a URI reference).
+// The general form represented is:
+// scheme://[userinfo@]host/path[?query][#fragment]
type URL struct {
-\tRaw string;
-\tScheme string;
-\tRawPath string;
-\tAuthority string;
-\tUserinfo string;
-\tHost string;
-\tPath string;
-\tQuery string;
-\tFragment string;
+\tRaw string; // the original string
+\tScheme string; // scheme
+\tRawPath string; // //[userinfo@]host/path[?query][#fragment]
+\tAuthority string; // [userinfo@]host
+\tUserinfo string; // userinfo
+\tHost string; // host
+\tPath string; // /path
+\tQuery string; // query
+\tFragment string; // fragment
}
解説: URL
構造体はパースされたURL(URI参照)を表します。追加されたコメントは、URLの一般的な形式を例示し、各フィールド(Raw
, Scheme
, RawPath
, Authority
, Userinfo
, Host
, Path
, Query
, Fragment
)がURLのどの部分に対応するかを簡潔に説明しています。これにより、開発者はURLの各コンポーネントがどのように構造体フィールドにマッピングされるかを容易に理解できます。
ParseURL
関数のコメントとBUGコメント
// Parse rawurl into a URL structure.
+// BUG(rsc): ParseURL should canonicalize the path,
+// removing unnecessary . and .. elements.
+\n+// ParseURL parses rawurl into a URL structure.
+// The string rawurl is assumed not to have a #fragment suffix.
+// (Web browsers strip #fragment before sending the URL to a web server.)
func ParseURL(rawurl string) (url *URL, err *os.Error) {
if rawurl == "" {
return nil, BadURL
解説: ParseURL
関数は生のURL文字列をパースして URL
構造体を生成します。追加されたコメントは、この関数が #fragment
サフィックスを持たないURLを想定している理由(Webブラウザの挙動)を説明しています。
特筆すべきは // BUG(rsc): ParseURL should canonicalize the path, removing unnecessary . and .. elements.
という BUG
コメントです。これは、当時の ParseURL
の既知の制限(パスの正規化が行われない)と、将来的な改善点を示しています。このようなコメントは、コードの現状と今後の開発計画を透明にする上で非常に有用です。
ParseURLReference
関数のコメント
// A URL reference is a URL with #frag potentially added. Parse it.
+// ParseURLReference is like ParseURL but allows a trailing #fragment.
func ParseURLReference(rawurlref string) (url *URL, err *os.Error) {
// Cut off #frag.
rawurl, frag := split(rawurlref, '#', true);
解説: ParseURLReference
関数は、ParseURL
とは異なり、末尾に #fragment
を含むURLもパースできることを説明しています。これにより、フラグメントの有無によって異なるパース関数を使用する必要があることが明確になります。
これらのコメントは、Goの http
パッケージの初期段階において、コードの意図を明確にし、開発者がAPIをより正確に理解し、適切に利用できるようにするための重要なステップでした。
関連リンク
- Go言語公式ドキュメント: Go言語の現在の
net/http
パッケージのドキュメントは、このコミットで追加された初期のドキュメントが発展したものです。 - RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1: HTTP/1.1プロトコルの主要な定義。このコミットのコメントで参照されています。
参考にした情報源リンク
- Go言語のコミット履歴:
- RFC 2616: HTTP/1.1の仕様。特にヘッダーの定義やURLの構造に関する記述。
- Go言語の初期の歴史に関する記事やブログ: Go言語がどのように開発され、進化してきたかについての情報源。
- (特定のURLは挙げませんが、Go言語の歴史に関する公式ブログや技術記事を参照しました。)
- Go言語の
net/http
パッケージのソースコード: 現在のnet/http
パッケージのソースコードは、このコミット当時のsrc/lib/http
から大きく進化していますが、基本的な概念は共通しています。