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

[インデックス 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言語の標準ライブラリはまだ発展途上にあり、特にネットワーク関連のパッケージは基本的な機能が実装されつつある段階でした。

このコミットが行われた背景には、以下のような点が考えられます。

  1. コードベースの成熟: 初期開発段階では機能の実装が優先されがちですが、ある程度の機能が揃ってくると、その機能がどのように動作し、どのように使用されるべきかを明確にするためのドキュメントが不可欠になります。
  2. 利用者の増加への準備: Go言語がオープンソースとして公開され、より多くの開発者に利用されるようになるにつれて、コードの内部構造やAPIの意図を理解するための詳細なドキュメントが求められます。
  3. 設計意図の明確化: http パッケージはWebアプリケーション開発において非常に重要なコンポーネントであり、その設計意図や各フィールド・関数の役割を明確にすることで、将来的な拡張やメンテナンスが容易になります。
  4. RFC準拠の明示: HTTPプロトコルはRFC(Request for Comments)によって厳密に定義されています。コードがどのRFCに準拠しているか、あるいは特定の挙動がRFCのどの部分に基づいているかを明示することは、プロトコル実装の正確性を保証し、開発者が仕様を理解する上で役立ちます。特に Referer のスペルミスに関するコメントは、HTTPの歴史的な経緯とRFCの記述を反映したものです。

このコミットは、Go言語の http パッケージが単なる機能提供だけでなく、その利用者がコードを深く理解し、適切に利用できるようにするための基盤を固める一環として行われたと言えます。

前提知識の解説

このコミットの変更内容を理解するためには、以下の前提知識があると役立ちます。

  1. HTTP (Hypertext Transfer Protocol):
    • Web上でデータを交換するためのプロトコル。クライアント(ブラウザなど)とサーバー間でリクエストとレスポンスの形式で通信が行われます。
    • HTTPリクエスト: クライアントがサーバーに送るメッセージ。メソッド(GET, POSTなど)、URL、HTTPバージョン、ヘッダー(Host, User-Agent, Accept-Languageなど)、およびボディ(POSTリクエストの場合など)で構成されます。
    • HTTPヘッダー: リクエストやレスポンスに関する追加情報を提供するキーと値のペア。ヘッダー名は通常、大文字・小文字を区別しないとされています(例: Content-Typecontent-type は同じ意味)。
    • HTTPステータスコード: サーバーがリクエストに対して返信する3桁の数字コード(例: 200 OK, 404 Not Found, 500 Internal Server Error)。RFC 2616などで定義されています。
  2. 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 に対応するバイトを表します。
  3. Go言語の基本的な概念:
    • パッケージ (package): 関連する機能や型をまとめたもの。他のファイルから利用するために import されます。
    • 構造体 (struct): 複数のフィールド(変数)をまとめた複合データ型。
    • メソッド (method): 構造体に関連付けられた関数。
    • エラーハンドリング: Goではエラーは戻り値として明示的に扱われます。os.NewError は新しいエラー型を作成するために使用されます。
    • bufio パッケージ: バッファリングされたI/O操作を提供し、効率的な読み書きを可能にします。
    • os パッケージ: オペレーティングシステムとのインタフェースを提供します。

技術的詳細

このコミットの技術的な詳細は、主に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コメント:

    • ParseURLrawurl をURL構造体にパースすること、および rawurl#fragment サフィックスを持たないと仮定されていること(WebブラウザがサーバーにURLを送信する前にフラグメントを削除するため)が説明されています。
    • // BUG(rsc): ParseURL should canonicalize the path, removing unnecessary . and .. elements. という BUG コメントが追加されました。これは、ParseURL が将来的にパスの正規化(... の要素の削除)を行うべきであるという、既知の改善点を示しています。これは、Goの初期開発における透明性と、今後の開発計画の一部を示唆しています。
  • ParseURLReference 関数へのコメント追加: ParseURLReferenceParseURL と同様だが、末尾の #fragment を許容することが説明されています。これは、URLのパースにおいてフラグメントの有無が異なる2つのシナリオに対応するためのものです。

これらの変更は、Goの http パッケージのAPIがどのように機能し、どのように使用されるべきかについて、開発者により明確なガイダンスを提供することを目的としています。特に、HTTPプロトコルの微妙な点(ヘッダーの正規化、Referer のスペルミスなど)に関する説明は、Goの http パッケージがプロトコル仕様に忠実に、かつ実用的に実装されていることを示しています。

コアとなるコードの変更箇所

このコミットのコアとなるコードの変更箇所は、主に以下の3つのファイルにおけるコメントの追加と修正です。

  1. src/lib/http/request.go:

    • パッケージレベルのコメント追加。
    • Request 構造体の各フィールド(RawUrl, Url, Header, Close, Host, Referer, UserAgent)への詳細なコメント追加。
    • ProtoAtLeast メソッドへのコメント追加。
    • CanonicalHeaderKey 関数へのコメント追加。
    • ReadRequest 関数のコメント修正。
  2. src/lib/http/status.go:

    • パッケージレベルのコメント修正。
  3. 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 から大きく進化していますが、基本的な概念は共通しています。