[インデックス 15071] ファイルの概要
このコミットは、Go言語の実験的なパッケージである exp/cookiejar
における重要な変更を導入しています。具体的には、Cookie Jarの「外部ストレージ」の概念を完全に削除し、それに伴いAPIを最小化しています。これにより、cookiejar
パッケージは、永続化機能を持たない純粋な「インメモリ」のCookie Jarとして機能するようになります。
コミット
commit 7d8cc587b2f459d773d71ec2e220173de6e60f86
Author: Volker Dobler <dr.volker.dobler@gmail.com>
Date: Fri Feb 1 10:56:08 2013 +1100
exp/cookiejar: remove external storage
This CL removes the external storage of a cookie jar
and minimized the exported API as discussed in [1].
[1] https://groups.google.com/d/topic/golang-dev/ygDB3nbir00/discussion
Update #1960.
R=nigeltao
CC=golang-dev
https://golang.org/cl/7235065
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/7d8cc587b2f459d773d71ec2e220173de6e60f86
元コミット内容
exp/cookiejar: remove external storage
This CL removes the external storage of a cookie jar
and minimized the exported API as discussed in [1].
[1] https://groups.google.com/d/topic/golang-dev/ygDB3nbir00/discussion
Update #1960.
R=nigeltao
CC=golang-dev
https://golang.org/cl/7235065
変更の背景
この変更の背景には、Go言語のexp/cookiejar
パッケージの設計に関する議論があります。元々、cookiejar
パッケージは、Cookieの永続化を可能にするための「外部ストレージ」の概念を持っていました。これは、Storage
インターフェースを定義し、ユーザーが独自のストレージ実装(例: ファイルシステム、データベース)を提供できるようにすることで、Cookieの状態をアプリケーションの再起動後も維持できるようにする意図がありました。
しかし、Goコミュニティ内での議論(特にコミットメッセージで参照されている[1]のgolang-devメーリングリストの議論や、Issue #1960)により、この設計アプローチにはいくつかの問題があるという結論に至りました。主な懸念事項は以下の通りです。
- APIの複雑性: 外部ストレージの概念を導入することで、
cookiejar
パッケージのAPIが不必要に複雑になっていました。特に、Storage
インターフェースの設計は、スレッドセーフティ(Lock
/Unlock
メソッド)やエラーハンドリングなど、実装者に多くの責任を負わせるものでした。 - セキュリティとプライバシー: Cookieの永続化は、セキュリティとプライバシーに深く関わる問題です。外部ストレージの実装によっては、Cookieが安全でない方法で保存されたり、意図しない情報漏洩のリスクが生じる可能性がありました。パッケージ自体がストレージの実装の詳細に踏み込むことは、その責任範囲を広げ、潜在的な脆弱性を生む可能性がありました。
- Goの設計哲学との整合性: Goの標準ライブラリは、シンプルさと明瞭さを重視する傾向があります。Cookieの永続化のような特定のユースケースに特化した機能は、コアパッケージの責務から外し、より汎用的なインメモリの機能を提供し、永続化が必要な場合はユーザーがその上にレイヤーを構築する方が、Goの設計哲学に合致するという考えがありました。
- 実装の困難さ: 外部ストレージの概念は、
cookiejar
の内部ロジックを複雑にし、テストやメンテナンスを困難にしていました。
これらの議論の結果、exp/cookiejar
パッケージは、純粋なインメモリのCookie Jarとして再設計されることになりました。これにより、APIが簡素化され、パッケージの責務が明確になり、より堅牢で使いやすいものになることが期待されました。永続化が必要な場合は、http.CookieJar
インターフェースを実装する別のパッケージや、cookiejar
パッケージのインスタンスをシリアライズ/デシリアライズするなどの方法で、アプリケーションレベルで対応することになります。
前提知識の解説
このコミットを理解するためには、以下の概念について基本的な知識が必要です。
-
HTTP Cookie:
- HTTP Cookieは、ウェブサーバーがユーザーのウェブブラウザに送信し、ブラウザが保存する小さなデータのことです。ブラウザは、同じサーバーへの後続のリクエストでこのCookieをサーバーに送り返します。
- 主にセッション管理(ログイン状態の維持)、パーソナライゼーション(ユーザー設定の記憶)、トラッキング(ユーザー行動の追跡)などに使用されます。
- RFC 6265でその仕様が定義されています。
-
http.CookieJar
インターフェース (Go言語):- Go言語の標準ライブラリ
net/http
パッケージには、CookieJar
というインターフェースが定義されています。 - このインターフェースは、HTTPクライアントがCookieを保存し、後続のリクエストで適切なCookieを送信するために使用されます。
- 主なメソッドは
SetCookies(u *url.URL, cookies []*Cookie)
(Cookieを保存する)とCookies(u *url.URL) []*Cookie
(URLに対応するCookieを取得する)です。 exp/cookiejar
パッケージは、このhttp.CookieJar
インターフェースのRFC 6265に準拠した実装を提供することを目的としていました。
- Go言語の標準ライブラリ
-
RFC 6265 (HTTP State Management Mechanism):
- HTTP Cookieの動作を定義するIETFの標準仕様です。
- Cookieの属性(
Expires
,Max-Age
,Domain
,Path
,Secure
,HttpOnly
,SameSite
など)や、ブラウザがCookieをどのように処理すべきか(例: ドメインマッチング、パスマッチング、有効期限)について詳細に規定しています。 exp/cookiejar
パッケージは、このRFCに厳密に準拠することを目指していました。
-
Public Suffix List (PSL):
- Public Suffix Listは、ドメイン名の「パブリックサフィックス」(例:
.com
,.co.jp
,.github.io
など)を列挙したリストです。 - これは、セキュリティとプライバシーのために非常に重要です。例えば、
example.com
のウェブサイトがattacker.co.uk
のCookieを設定することを防ぐために使用されます。もしco.uk
がパブリックサフィックスとしてリストされていなければ、attacker.co.uk
はexample.co.uk
のCookieを設定できてしまい、セキュリティ上の問題が発生します。 cookiejar
パッケージは、このPSLを使用して、Cookieが設定されるドメインの妥当性を検証します。
- Public Suffix Listは、ドメイン名の「パブリックサフィックス」(例:
-
exp
パッケージ (Go言語):- Go言語の標準ライブラリには、
exp
(experimental)というプレフィックスを持つパッケージが存在することがあります。 - これらのパッケージは、まだ安定版としてリリースされていない、実験的な機能やAPIを含んでいます。
- APIが変更される可能性があり、後方互換性が保証されないことがあります。
- この
exp/cookiejar
もその一つであり、このコミットのような大規模なAPI変更が行われる可能性があることを示唆しています。
- Go言語の標準ライブラリには、
-
外部ストレージの概念:
- 一般的なソフトウェア設計において、データ永続化のメカニズムをコアロジックから分離するパターンです。
- このコミット以前の
exp/cookiejar
では、Storage
インターフェースを定義し、Cookieの保存・取得・削除といった永続化操作をこのインターフェースに委譲していました。これにより、ユーザーはファイル、データベース、メモリなど、任意のバックエンドでCookieを永続化できる柔軟性を持つことができました。 - しかし、前述の「変更の背景」で述べたように、この柔軟性がAPIの複雑性やセキュリティ上の懸念を引き起こし、結果として削除されることになりました。
技術的詳細
このコミットの技術的な詳細は、主にexp/cookiejar
パッケージから外部ストレージの概念を完全に排除することに集約されます。これにより、パッケージの責務が「RFC 6265に準拠したインメモリのCookie Jar」に限定され、APIが大幅に簡素化されました。
具体的な変更点は以下の通りです。
-
src/pkg/exp/cookiejar/storage.go
の削除:- このファイルは、
Storage
インターフェースとその関連型(Entry
,Action
)およびヘルパー関数(ValidStorageKey
)を定義していました。 - 外部ストレージの概念が不要になったため、このファイル全体が削除されました。これは、Cookie Jarの永続化メカニズムがパッケージの外部に完全に委ねられることを意味します。
- このファイルは、
-
src/pkg/exp/cookiejar/storage_test.go
の削除:storage.go
の削除に伴い、そのテストファイルも不要になったため削除されました。
-
src/pkg/exp/cookiejar/jar.go
の変更:- パッケージコメントの変更:
- 変更前:
// Package cookiejar implements an RFC 6265-compliant http.CookieJar.
- 変更後:
// Package cookiejar implements an in-memory RFC 6265-compliant http.CookieJar.
in-memory
という記述が追加され、このパッケージが永続化機能を持たないことが明確に示されました。
- 変更前:
PublicSuffixList
インターフェースのコメント変更:String()
メソッドに関するコメントから、A Jar will store its PublicSuffixList's description in its storage, and update the stored cookies if its list has a different description than the stored list.
という外部ストレージに関連する記述が削除されました。これは、Jarがもはやストレージを持たないため、その記述を保存する必要がなくなったためです。
Options
構造体の変更:Storage Storage
フィールドが削除されました。これにより、New
関数でCookie Jarを作成する際に、外部ストレージの実装を指定する必要がなくなりました。PublicSuffixList
フィールドのコメントが修正され、A nil value is valid and may be useful for testing but it is not secure: it means that the HTTP server for foo.com can set a cookie for bar.com.
という注意書きが追加されました。これは、PSLがセキュリティ上いかに重要であるかを強調しています。
Jar
構造体の変更:storage Storage
フィールドが削除されました。Jar
構造体は、もはや外部ストレージへの参照を保持しません。- 残るフィールドは
psList PublicSuffixList
のみとなり、Jar
が純粋にCookieのメモリ内管理とPSLによるドメイン検証に特化していることを示しています。
New
関数の変更:- シグネチャが
func New(o *Options) *Jar
からfunc New(o *Options) (*Jar, error)
に変更されました。これは、将来的にNew
関数がエラーを返す可能性があることを示唆していますが、このコミット時点ではreturn nil, nil
となっており、まだ実装が完了していないことを示しています(TODO
コメントも残されています)。 - 関数内部で
o.Storage
を参照してJar
のstorage
フィールドを初期化するロジックが削除されました。 // TODO.
コメントが追加され、New
関数の具体的な実装がまだ残っていることが示されています。
- シグネチャが
- パッケージコメントの変更:
これらの変更により、exp/cookiejar
パッケージは、Cookieの永続化という複雑な責務から解放され、純粋にRFC 6265に準拠したCookieのメモリ内管理に特化するようになりました。これにより、パッケージのコードベースは大幅に削減され、理解しやすくなりました。永続化が必要なアプリケーションは、このcookiejar
インスタンスをラップするか、独自の永続化ロジックを実装する必要があります。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は、主にsrc/pkg/exp/cookiejar/jar.go
ファイルにおけるJar
構造体、Options
構造体、およびNew
関数の定義の変更、そしてsrc/pkg/exp/cookiejar/storage.go
ファイルの削除です。
src/pkg/exp/cookiejar/jar.go
の差分:
--- a/src/pkg/exp/cookiejar/jar.go
+++ b/src/pkg/exp/cookiejar/jar.go
@@ -2,10 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package cookiejar implements an RFC 6265-compliant http.CookieJar.
-//
-// TODO: example code to create a memory-backed cookie jar with the default
-// public suffix list.
+// Package cookiejar implements an in-memory RFC 6265-compliant http.CookieJar.
package cookiejar
import (
@@ -32,47 +29,35 @@ type PublicSuffixList interface {
// for IDN/Punycode.
PublicSuffix(domain string) string
-\t// String returns a description of the source of this public suffix list.\n-\t// A Jar will store its PublicSuffixList\'s description in its storage,\n-\t// and update the stored cookies if its list has a different description\n-\t// than the stored list. The description will typically contain something\n-\t// like a time stamp or version number.\n+\t// String returns a description of the source of this public suffix\n+\t// list. The description will typically contain something like a time\n+\t// stamp or version number.\n \tString() string
}\n \n // Options are the options for creating a new Jar.\n type Options struct {\n-\t// Storage is the cookie jar storage. It may not be nil.\n-\tStorage Storage\n-\n-\t// PublicSuffixList is the public suffix list that determines whether an\n-\t// HTTP server can set a cookie for a domain. It may not be nil.\n+\t// PublicSuffixList is the public suffix list that determines whether\n+\t// an HTTP server can set a cookie for a domain.\n+\t//\n+\t// A nil value is valid and may be useful for testing but it is not\n+\t// secure: it means that the HTTP server for foo.com can set a cookie\n+\t// for bar.com.\n \tPublicSuffixList PublicSuffixList\n-\n-\t// TODO: ErrorFunc for handling storage errors?\n }\n \n // Jar implements the http.CookieJar interface from the net/http package.\n type Jar struct {\n-\tstorage Storage\n-\tpsList PublicSuffixList\n+\tpsList PublicSuffixList\n }\n \n-// New returns a new cookie jar.\n-func New(o *Options) *Jar {\n-\treturn &Jar{\n-\t\tstorage: o.Storage,\n-\t\tpsList: o.PublicSuffixList,\n-\t}\n+// New returns a new cookie jar. A nil *Options is equivalent to a zero\n+// Options.\n+func New(o *Options) (*Jar, error) {\n+\t// TODO.\n+\treturn nil, nil\n }\n \n-// TODO(nigeltao): how do we reject HttpOnly cookies? Do we post-process the\n-// return value from Jar.Cookies?\n-//\n-// HttpOnly cookies are those for regular HTTP(S) requests but should not be\n-// visible from JavaScript. The HttpOnly bit mitigates XSS attacks; it\'s not\n-// for HTTP vs HTTPS vs FTP transports.\n-\n // Cookies implements the Cookies method of the http.CookieJar interface.\n //\n // It returns an empty slice if the URL\'s scheme is not HTTP or HTTPS.\n```
**`src/pkg/exp/cookiejar/storage.go` の削除:**
```diff
--- a/src/pkg/exp/cookiejar/storage.go
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package cookiejar
-
-import (
- "time"
-)
-
-// Storage is a Jar's storage. It is a multi-map, mapping keys to one or more
-// entries. Each entry consists of a subkey, creation time, last access time,
-// and some arbitrary data.
-//
-// The Add and Delete methods have undefined behavior if the key is invalid.
-// A valid key must use only bytes in the character class [a-z0-9.-] and
-// must have at least one non-. byte. Note that this excludes any key
-// containing a capital ASCII letter as well as the empty string.
-type Storage interface {
- // A client must call Lock before calling other methods and must call
- // Unlock when finished. Between the calls to Lock and Unlock, a client
- // can assume that other clients are not modifying the Storage.
- Lock()
- Unlock()
-
- // Add adds entries to the storage. Each entry's Subkey and Data must
- // both be non-empty.
- //
- // If the Storage already contains an entry with the same key and
- // subkey then the new entry is stored with the creation time of the
- // old entry, and the old entry is deleted.
- //
- // Adding entries may cause other entries to be deleted, to maintain an
- // implementation-specific storage constraint.
- Add(key string, entries ...Entry) error
-
- // Delete deletes all entries for the given key.
- Delete(key string) error
-
- // Entries calls f for each entry stored for that key. If f returns a
- // non-nil error then the iteration stops and Entries returns that
- // error. Iteration is not guaranteed to be in any particular order.
- //
- // If f returns an Update action then that stored entry's LastAccess
- // time will be set to the time that f returned. If f returns a Delete
- // action then that entry will be deleted from the Storage.
- //
- // f may call a Storage's Add and Delete methods; those modifications
- // will not affect the list of entries visited in this call to Entries.
- Entries(key string, f func(entry Entry) (Action, time.Time, error)) error
-
- // Keys calls f for each key stored. f will not be called on a key with
- // zero entries. If f returns a non-nil error then the iteration stops
- // and Keys returns that error. Iteration is not guaranteed to be in any
- // particular order.
- //
- // f may call a Storage's Add, Delete and Entries methods; those
- // modifications will not affect the list of keys visited in this call
- // to Keys.
- Keys(f func(key string) error) error
-}
-
-// Entry is an entry in a Storage.
-type Entry struct {
- Subkey string
- Data string
- Creation time.Time
- LastAccess time.Time
-}
-
-// Action is an action returned by the function passed to Entries.
-type Action int
-
-const (
- // Pass means to take no further action with an Entry.
- Pass Action = iota
- // Update means to update the LastAccess time of an Entry.
- Update
- // Delete means to delete an Entry.
- Delete
-)
-
-// ValidStorageKey returns whether the given key is valid for a Storage.
-func ValidStorageKey(key string) bool {
- hasNonDot := false
- for i := 0; i < len(key); i++ {
- switch c := key[i]; {
- case 'a' <= c && c <= 'z':
- fallthrough
- case '0' <= c && c <= '9':
- fallthrough
- case c == '-':
- hasNonDot = true
- case c == '.':
- // No-op.
- default:
- return false
- }
- }
- return hasDot
-}
コアとなるコードの解説
このコミットの核心は、exp/cookiejar
パッケージからCookieの永続化機能(外部ストレージ)を完全に削除し、純粋なインメモリのCookie Jarに特化させることです。
-
storage.go
とstorage_test.go
の削除:- これら2つのファイルは、
Storage
インターフェースとその実装、および関連するテストを定義していました。Storage
インターフェースは、Cookieの追加、削除、列挙といった永続化操作を抽象化するためのものでした。 - これらのファイルの削除は、
cookiejar
パッケージがもはやCookieの永続化の責任を負わないことを明確に示しています。永続化が必要な場合は、このパッケージの外部で、http.CookieJar
インターフェースを実装する別のメカニズムを構築する必要があります。
- これら2つのファイルは、
-
jar.go
の変更:- パッケージコメントの変更:
// Package cookiejar implements an RFC 6265-compliant http.CookieJar.
から// Package cookiejar implements an in-memory RFC 6265-compliant http.CookieJar.
へと変更されました。これは、このパッケージが提供するCookie Jarが「インメモリ」である、つまりアプリケーションの実行中にのみCookieを保持し、永続化しないことを明示しています。 PublicSuffixList
インターフェースのString()
メソッドに関するコメントの変更: 以前のコメントには、「JarがそのPublicSuffixListの記述をストレージに保存し、リストが異なる記述を持つ場合に保存されたCookieを更新する」という記述がありました。外部ストレージが削除されたため、この記述は不要となり、単に「Public Suffixリストのソースの説明を返す」という簡潔な説明に修正されました。Options
構造体からのStorage
フィールドの削除:Options
構造体は、New
関数でJar
を作成する際のオプションを定義していました。以前はStorage Storage
フィールドがあり、ユーザーが独自のストレージ実装を提供できるようになっていましたが、これが削除されました。これにより、New
関数を呼び出す際にストレージを指定する必要がなくなり、APIが簡素化されました。Jar
構造体からのstorage
フィールドの削除:Jar
構造体は、Cookie Jarの実際の状態を保持するものです。以前はstorage Storage
フィールドがあり、これがCookieの永続化を担当するストレージへの参照でした。このフィールドが削除されたことで、Jar
はもはや外部ストレージと直接連携せず、Cookieは純粋にメモリ内で管理されることになります。New
関数のシグネチャと実装の変更:- シグネチャが
func New(o *Options) *Jar
からfunc New(o *Options) (*Jar, error)
に変更されました。これは、New
関数がエラーを返す可能性があることを示唆していますが、このコミット時点ではreturn nil, nil
となっており、まだ完全な実装がなされていないことを示しています(TODO
コメントも残されています)。 - 関数内部で
o.Storage
を使用してJar
のstorage
フィールドを初期化するロジックが削除されました。これは、Jar
がもはや外部ストレージを持たないため当然の変更です。
- シグネチャが
- パッケージコメントの変更:
これらの変更は、exp/cookiejar
パッケージの設計思想を根本的に変更するものです。永続化の複雑さを取り除き、パッケージの責務を明確にすることで、よりシンプルで堅牢なインメモリCookie Jarを提供することを目指しています。
関連リンク
- golang-devメーリングリストの議論: https://groups.google.com/d/topic/golang-dev/ygDB3nbir00/discussion
- Go Issue #1960: https://go.dev/issue/1960 (このコミットが解決したIssue)
- Go CL 7235065: https://golang.org/cl/7235065 (このコミットに対応するGerrit Change-ID)
参考にした情報源リンク
- RFC 6265 - HTTP State Management Mechanism: https://datatracker.ietf.org/doc/html/rfc6265
- Public Suffix List: https://publicsuffix.org/
- Go言語
net/http
パッケージドキュメント: https://pkg.go.dev/net/http - Go言語
exp
パッケージの慣習に関する情報 (一般的な知識) - Go言語の設計哲学に関する一般的な情報 (シンプルさ、責務の分離など)
- Go言語のIssueトラッカーとGerritコードレビューシステムに関する一般的な知識