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

[インデックス 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)により、この設計アプローチにはいくつかの問題があるという結論に至りました。主な懸念事項は以下の通りです。

  1. APIの複雑性: 外部ストレージの概念を導入することで、cookiejarパッケージのAPIが不必要に複雑になっていました。特に、Storageインターフェースの設計は、スレッドセーフティ(Lock/Unlockメソッド)やエラーハンドリングなど、実装者に多くの責任を負わせるものでした。
  2. セキュリティとプライバシー: Cookieの永続化は、セキュリティとプライバシーに深く関わる問題です。外部ストレージの実装によっては、Cookieが安全でない方法で保存されたり、意図しない情報漏洩のリスクが生じる可能性がありました。パッケージ自体がストレージの実装の詳細に踏み込むことは、その責任範囲を広げ、潜在的な脆弱性を生む可能性がありました。
  3. Goの設計哲学との整合性: Goの標準ライブラリは、シンプルさと明瞭さを重視する傾向があります。Cookieの永続化のような特定のユースケースに特化した機能は、コアパッケージの責務から外し、より汎用的なインメモリの機能を提供し、永続化が必要な場合はユーザーがその上にレイヤーを構築する方が、Goの設計哲学に合致するという考えがありました。
  4. 実装の困難さ: 外部ストレージの概念は、cookiejarの内部ロジックを複雑にし、テストやメンテナンスを困難にしていました。

これらの議論の結果、exp/cookiejarパッケージは、純粋なインメモリのCookie Jarとして再設計されることになりました。これにより、APIが簡素化され、パッケージの責務が明確になり、より堅牢で使いやすいものになることが期待されました。永続化が必要な場合は、http.CookieJarインターフェースを実装する別のパッケージや、cookiejarパッケージのインスタンスをシリアライズ/デシリアライズするなどの方法で、アプリケーションレベルで対応することになります。

前提知識の解説

このコミットを理解するためには、以下の概念について基本的な知識が必要です。

  1. HTTP Cookie:

    • HTTP Cookieは、ウェブサーバーがユーザーのウェブブラウザに送信し、ブラウザが保存する小さなデータのことです。ブラウザは、同じサーバーへの後続のリクエストでこのCookieをサーバーに送り返します。
    • 主にセッション管理(ログイン状態の維持)、パーソナライゼーション(ユーザー設定の記憶)、トラッキング(ユーザー行動の追跡)などに使用されます。
    • RFC 6265でその仕様が定義されています。
  2. 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に準拠した実装を提供することを目的としていました。
  3. RFC 6265 (HTTP State Management Mechanism):

    • HTTP Cookieの動作を定義するIETFの標準仕様です。
    • Cookieの属性(Expires, Max-Age, Domain, Path, Secure, HttpOnly, SameSiteなど)や、ブラウザがCookieをどのように処理すべきか(例: ドメインマッチング、パスマッチング、有効期限)について詳細に規定しています。
    • exp/cookiejarパッケージは、このRFCに厳密に準拠することを目指していました。
  4. Public Suffix List (PSL):

    • Public Suffix Listは、ドメイン名の「パブリックサフィックス」(例: .com, .co.jp, .github.ioなど)を列挙したリストです。
    • これは、セキュリティとプライバシーのために非常に重要です。例えば、example.comのウェブサイトがattacker.co.ukのCookieを設定することを防ぐために使用されます。もしco.ukがパブリックサフィックスとしてリストされていなければ、attacker.co.ukexample.co.ukのCookieを設定できてしまい、セキュリティ上の問題が発生します。
    • cookiejarパッケージは、このPSLを使用して、Cookieが設定されるドメインの妥当性を検証します。
  5. exp パッケージ (Go言語):

    • Go言語の標準ライブラリには、exp(experimental)というプレフィックスを持つパッケージが存在することがあります。
    • これらのパッケージは、まだ安定版としてリリースされていない、実験的な機能やAPIを含んでいます。
    • APIが変更される可能性があり、後方互換性が保証されないことがあります。
    • このexp/cookiejarもその一つであり、このコミットのような大規模なAPI変更が行われる可能性があることを示唆しています。
  6. 外部ストレージの概念:

    • 一般的なソフトウェア設計において、データ永続化のメカニズムをコアロジックから分離するパターンです。
    • このコミット以前のexp/cookiejarでは、Storageインターフェースを定義し、Cookieの保存・取得・削除といった永続化操作をこのインターフェースに委譲していました。これにより、ユーザーはファイル、データベース、メモリなど、任意のバックエンドでCookieを永続化できる柔軟性を持つことができました。
    • しかし、前述の「変更の背景」で述べたように、この柔軟性がAPIの複雑性やセキュリティ上の懸念を引き起こし、結果として削除されることになりました。

技術的詳細

このコミットの技術的な詳細は、主にexp/cookiejarパッケージから外部ストレージの概念を完全に排除することに集約されます。これにより、パッケージの責務が「RFC 6265に準拠したインメモリのCookie Jar」に限定され、APIが大幅に簡素化されました。

具体的な変更点は以下の通りです。

  1. src/pkg/exp/cookiejar/storage.go の削除:

    • このファイルは、Storageインターフェースとその関連型(Entry, Action)およびヘルパー関数(ValidStorageKey)を定義していました。
    • 外部ストレージの概念が不要になったため、このファイル全体が削除されました。これは、Cookie Jarの永続化メカニズムがパッケージの外部に完全に委ねられることを意味します。
  2. src/pkg/exp/cookiejar/storage_test.go の削除:

    • storage.goの削除に伴い、そのテストファイルも不要になったため削除されました。
  3. 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を参照してJarstorageフィールドを初期化するロジックが削除されました。
      • // 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に特化させることです。

  1. storage.gostorage_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を使用してJarstorageフィールドを初期化するロジックが削除されました。これは、Jarがもはや外部ストレージを持たないため当然の変更です。

これらの変更は、exp/cookiejarパッケージの設計思想を根本的に変更するものです。永続化の複雑さを取り除き、パッケージの責務を明確にすることで、よりシンプルで堅牢なインメモリCookie Jarを提供することを目指しています。

関連リンク

参考にした情報源リンク

  • 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コードレビューシステムに関する一般的な知識