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

[インデックス 18171] ファイルの概要

このコミットは、Go言語の標準ライブラリである net/http パッケージ内のクッキー処理に関するものです。具体的には、クッキーの Domain および Path フィールドのパースに関するTODOコメントが削除されています。これは、これらのフィールドがGo 1.0以降、未処理のワイヤーデータとして扱われてきた現状を維持し、その挙動が多くのアプリケーションにとって問題ないという判断に基づいています。

コミット

commit 939b3fa39eafff3f10c092ef882c935c0bce9c06
Author: Volker Dobler <dr.volker.dobler@gmail.com>
Date:   Mon Jan 6 10:00:58 2014 -0800

    net/http: remove todos from cookie code
    
    The Domain and Path field of a parsed cookie have been
    the unprocessed wire data since Go 1.0; this seems to
    be okay for most applications so let's keep it.
    
    Returning the unprocessed wire data makes it easy to
    handle nonstandard or even broken clients without
    consulting Raw or Unparsed of a cookie.
    
    The RFC 6265 parsing rules for domain and path are
    currently buried in net/http/cookiejar but could be
    exposed in net/http if necessary.
    
    R=bradfitz, nigeltao
    CC=golang-codereviews
    https://golang.org/cl/48060043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/939b3fa39eafff3f10c092ef882c935c0bce9c06

元コミット内容

net/http: クッキーコードからTODOを削除

パースされたクッキーの Domain および Path フィールドは、Go 1.0以降、未処理のワイヤーデータとして扱われてきました。これはほとんどのアプリケーションで問題ないように思われるため、このまま維持することにします。

未処理のワイヤーデータを返すことで、クッキーの RawUnparsed を参照することなく、非標準的または破損したクライアントを簡単に処理できます。

Domain および Path のRFC 6265パースルールは現在 net/http/cookiejar に隠されていますが、必要であれば net/http で公開することも可能です。

変更の背景

このコミットの背景には、Go言語の net/http パッケージにおけるクッキーの Domain および Path フィールドの扱いに関する設計上の決定があります。

元々、src/pkg/net/http/cookie.go のコードには、DomainPath のパース処理を追加するためのTODOコメントが存在していました。これは、HTTPクッキーの仕様を定義するRFC 6265に厳密に従ったパースロジックを将来的に実装する意図があったことを示唆しています。

しかし、Go 1.0のリリース以降、これらのフィールドはHTTPレスポンスヘッダーから受け取ったそのままの文字列(「未処理のワイヤーデータ」)として Cookie 構造体に格納されてきました。この挙動は、多くのWebアプリケーションやクライアントにとって特に問題を引き起こすことなく機能していました。

コミットの著者は、この既存の挙動を維持することに利点があると考えました。その主な理由は以下の通りです。

  1. 後方互換性: Go 1.0以降の既存のコードベースがこの挙動に依存しているため、変更は破壊的となる可能性があります。
  2. 柔軟性: 未処理のデータをそのまま保持することで、RFCに厳密に従わない、あるいは意図的に非標準的な形式でクッキーを送信するクライアント(例えば、古いブラウザや特定のシステム)からのデータも、開発者が柔軟に処理できるようになります。Cookie 構造体の RawUnparsed フィールドを別途参照する必要がなくなるため、コードが簡潔になります。
  3. 実用性: 厳密なRFC 6265のパースルールは、Goの net/http/cookiejar パッケージ(クッキーの保存と管理を行うクライアントサイドのクッキー実装)には既に実装されています。これは、クライアントがクッキーを送信する際に、ブラウザのように厳密なルールに従ってクッキーを生成・管理する必要があるためです。しかし、サーバーサイドで受信したクッキーをパースする net/http パッケージにおいては、必ずしもそこまで厳密なパースが必要ではないという判断がなされました。サーバーは、クライアントが送信してきたデータをそのまま受け取り、必要に応じてアプリケーション層で解釈する方が、より堅牢であるという考え方です。

これらの理由から、DomainPath のパースに関するTODOコメントは削除され、未処理のワイヤーデータをそのまま保持する現在の実装が正式なものとして承認されました。これにより、net/http パッケージは、クッキーの受信と基本的な構造化に焦点を当て、より複雑なRFC準拠のパースは net/http/cookiejar のような上位レイヤーのパッケージに任せるという役割分担が明確になりました。

前提知識の解説

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

1. HTTPクッキー (HTTP Cookies)

HTTPクッキーは、WebサーバーがユーザーのWebブラウザに送信する小さなデータ片です。ブラウザはこれらのクッキーを保存し、同じサーバーへの後続のリクエストでそれらを送り返します。これにより、サーバーはユーザーの状態を記憶したり、ユーザーを識別したりすることができます。

クッキーは主に Set-Cookie HTTPレスポンスヘッダーによってサーバーからクライアントに設定され、Cookie HTTPリクエストヘッダーによってクライアントからサーバーに送信されます。

クッキーにはいくつかの属性があります。このコミットで特に重要なのは DomainPath です。

  • Domain 属性: クッキーが送信されるドメインを指定します。例えば、Domain=example.com と設定されたクッキーは、example.com およびそのサブドメイン(www.example.com など)に送信されます。この属性が設定されていない場合、クッキーを設定したサーバーのホスト名がデフォルトのドメインとなります。
  • Path 属性: クッキーが送信されるURLパスを指定します。例えば、Path=/docs と設定されたクッキーは、/docs およびそのサブパス(/docs/faq など)に送信されます。この属性が設定されていない場合、クッキーを設定したリクエストのパスがデフォルトのパスとなります。

2. RFC 6265 (HTTP State Management Mechanism)

RFC 6265は、HTTPクッキーの動作と構文を定義するインターネット標準ドキュメントです。これは、以前のRFC 2965やRFC 2109を置き換えるもので、クッキーのパース、保存、送信に関する詳細なルールを規定しています。

特に、DomainPath 属性のパースルールは複雑で、例えば Domain 属性の値がドットで始まるかどうか、IPアドレスであるかどうか、あるいはホスト名と一致するかどうかなど、様々な条件が考慮されます。これらのルールは、セキュリティ上の理由(例えば、あるドメインのクッキーが別のドメインに誤って送信されるのを防ぐ)や、クッキーの意図しない共有を防ぐために重要です。

3. Go言語の net/http パッケージ

net/http パッケージは、Go言語でHTTPクライアントとサーバーを実装するための基本的な機能を提供します。このパッケージには、HTTPリクエストとレスポンスのパース、ヘッダーの処理、クッキーの読み書きなどの機能が含まれています。

  • http.Cookie 構造体: クッキーの各属性(Name, Value, Domain, Path, Expires など)を保持する構造体です。
  • http.Request.Cookie() / http.Request.Cookies(): 受信したHTTPリクエストからクッキーを読み取るためのメソッドです。
  • http.SetCookie(): HTTPレスポンスにクッキーを設定するための関数です。

4. Go言語の net/http/cookiejar パッケージ

net/http/cookiejar パッケージは、HTTPクライアントがクッキーを保存し、管理するための「クッキージャー」の実装を提供します。これは、Webブラウザがクッキーを扱う方法を模倣しており、RFC 6265に厳密に従ってクッキーの保存、有効期限、ドメインとパスのマッチング、セキュリティポリシーなどを処理します。

このパッケージは、特にHTTPクライアントが複数のリクエスト間で状態を維持する必要がある場合に有用です。net/http パッケージがHTTPプロトコルの低レベルな側面を扱うのに対し、net/http/cookiejar はより高レベルなクッキー管理ロジックを提供します。

このコミットの文脈では、net/http がサーバーサイドで受信したクッキーの「生データ」を扱うのに対し、net/http/cookiejar はクライアントサイドでクッキーを「厳密にパースし、管理する」という役割分担が重要になります。

技術的詳細

このコミットの技術的な核心は、Goの net/http パッケージがHTTPクッキーの Domain および Path フィールドをどのように扱うか、という点にあります。

既存の挙動(Go 1.0以降)

コミットメッセージが述べているように、Go 1.0以降、net/http パッケージは受信したクッキーの Domain および Path フィールドを、HTTPヘッダーから読み取った「未処理のワイヤーデータ」(raw wire data)として http.Cookie 構造体に格納していました。これは、RFC 6265で定義されているような厳密なパースルール(例えば、ドメイン名の正規化、パスの正規化、不正な値の拒否など)を適用せずに、文字列をそのまま保持するという意味です。

例えば、Set-Cookie: name=value; Domain=.example.com; Path=/foo/bar というヘッダーを受け取った場合、http.Cookie.Domain には ".example.com" が、http.Cookie.Path には "/foo/bar" がそのまま格納されます。RFC 6265では、ドメインがドットで始まる場合(.example.com)は、先頭のドットを削除して example.com として扱うなどの正規化ルールが存在しますが、net/http はこれを適用していませんでした。

TODOコメントの存在とその意味

削除されたTODOコメント(// TODO: Add domain parsing// TODO: Add path parsing)は、将来的にRFC 6265に準拠した厳密なパースロジックを net/http パッケージに追加する意図があったことを示しています。これは、より堅牢で標準に準拠したクッキー処理を目指す開発者の一般的な考え方と一致します。

TODOコメント削除の理由と影響

このコミットは、これらのTODOコメントを削除することで、未処理のワイヤーデータをそのまま保持する既存の挙動を「意図された設計」として確定させました。その理由は、コミットメッセージに明記されています。

  1. 実用性と柔軟性:

    • 「未処理のワイヤーデータを返すことで、クッキーの RawUnparsed を参照することなく、非標準的または破損したクライアントを簡単に処理できます。」
    • これは、サーバーが様々なクライアント(古いブラウザ、非標準的な実装、あるいはバグのあるクライアント)からのクッキーを受け入れる際に、より寛容であることを意味します。厳密なパースルールを適用すると、わずかなフォーマットのずれでもクッキーが拒否されたり、誤ってパースされたりする可能性があります。未処理のデータをそのまま渡すことで、アプリケーション開発者は、必要に応じて独自のパースロジックを実装したり、特定の非標準的なケースをハンドリングしたりする柔軟性を得られます。
    • http.Cookie 構造体には Raw フィールド(クッキーの生文字列)や Unparsed フィールド(パースできなかった属性のリスト)も存在しますが、DomainPath が未処理のワイヤーデータとして直接利用できることで、これらのフィールドを別途参照して再パースする手間が省けます。
  2. 役割分担の明確化:

    • 「RFC 6265パースルールは現在 net/http/cookiejar に隠されていますが、必要であれば net/http で公開することも可能です。」
    • これは、RFC 6265に厳密に準拠したクッキーのパースと管理は、主にクライアントサイドのクッキー管理を行う net/http/cookiejar パッケージの役割であるという設計思想を示唆しています。
    • net/http パッケージ(特にサーバーサイドの受信処理)は、HTTPプロトコルの基本的な構文解析とデータ構造へのマッピングに焦点を当て、より複雑なセマンティックな解釈や検証は、より高レベルのパッケージやアプリケーションロジックに委ねるというアプローチです。これにより、net/http パッケージのコードベースはよりシンプルに保たれ、パフォーマンスも最適化されます。

結論

このコミットは、net/http パッケージがクッキーの Domain および Path フィールドを「未処理のワイヤーデータ」として扱うという既存の挙動を、意図的な設計上の選択として確定させました。これは、実用性、柔軟性、そしてGo標準ライブラリ内でのパッケージ間の役割分担を考慮した結果です。開発者は、net/http が提供するクッキーデータがRFC 6265に厳密に準拠したパース済みデータではないことを理解し、必要に応じてアプリケーション層で追加の検証や正規化を行う必要があります。

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

変更は src/pkg/net/http/cookie.go ファイルの2箇所です。

--- a/src/pkg/net/http/cookie.go
+++ b/src/pkg/net/http/cookie.go
@@ -94,7 +94,6 @@ func readSetCookies(h Header) []*Cookie {
 			case "domain":
 				c.Domain = val
-				// TODO: Add domain parsing
 				continue
 			case "max-age":
 				secs, err := strconv.Atoi(val)
@@ -121,7 +120,6 @@ func readSetCookies(h Header) []*Cookie {
 			case "path":
 				c.Path = val
-				// TODO: Add path parsing
 				continue
 			}\
 			c.Unparsed = append(c.Unparsed, parts[i])

コアとなるコードの解説

このコミットで削除されたのは、src/pkg/net/http/cookie.go 内の readSetCookies 関数における2つのTODOコメントです。

readSetCookies 関数は、HTTPレスポンスヘッダーから Set-Cookie ヘッダーを読み取り、それを http.Cookie 構造体のスライスにパースする役割を担っています。この関数内で、クッキーの各属性(name=value, expires, domain, path など)がループ処理され、対応する http.Cookie 構造体のフィールドに値が割り当てられます。

削除されたTODOコメントは以下の行にありました。

  1. case "domain": の下:

    				c.Domain = val
    				// TODO: Add domain parsing
    				continue
    

    この箇所は、Set-Cookie ヘッダーから Domain 属性の値 (val) を抽出し、それを c.Domain フィールドに直接割り当てています。削除されたTODOコメントは、ここにRFC 6265に準拠したドメインパースロジック(例えば、先頭のドットの削除や、不正なドメイン名の検証など)を追加する予定があったことを示していました。

  2. case "path": の下:

    				c.Path = val
    				// TODO: Add path parsing
    				continue
    

    同様に、この箇所は Path 属性の値 (val) を抽出し、c.Path フィールドに直接割り当てています。削除されたTODOコメントは、ここにRFC 6265に準拠したパスパースロジック(例えば、パスの正規化や、不正なパスの検証など)を追加する予定があったことを示していました。

これらのTODOコメントが削除されたことで、net/http パッケージは、DomainPath の値をHTTPヘッダーから受け取ったそのままの文字列として http.Cookie 構造体に格納し続けることが、正式な設計上の決定となりました。これは、前述の「変更の背景」と「技術的詳細」で説明したように、実用性、柔軟性、およびパッケージ間の役割分担を重視した結果です。

したがって、このコミットはコードの動作を変更するものではなく、将来の機能追加の計画(TODO)を削除し、既存の動作を「意図されたもの」として明確にするためのものです。

関連リンク

参考にした情報源リンク