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

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

このコミットは、Go言語の標準ライブラリ net/http パッケージに、HTTPクライアントがクッキーを永続化・管理するための CookieJar インターフェースを追加するものです。これにより、HTTPリクエストとレスポンス間でクッキーの状態を保持し、セッション管理などを容易に行えるようになります。

コミット

commit dd694fb14912814fdcdf280ffa2a783b3bd18e63
Author: Volker Dobler <dr.volker.dobler@gmail.com>
Date:   Fri Dec 16 10:48:41 2011 -0500

    net/http: Added interface for a cookie jar.
    
    Types implementing CookieJar may be used in a Client
    to persist cookies.
    
    R=bradfitz, rsc
    CC=golang-dev
    https://golang.org/cl/5399043

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

https://github.com/golang/go/commit/dd694fb14912814fdcdf280ffa2a783b3bd18e63

元コミット内容

net/http: Added interface for a cookie jar.

Types implementing CookieJar may be used in a Client
to persist cookies.

R=bradfitz, rsc
CC=golang-dev
https://golang.org/cl/5399043

変更の背景

HTTPプロトコルはステートレスであり、各リクエストは独立して扱われます。しかし、ウェブアプリケーションではユーザー認証やセッション管理など、複数のリクエスト間で状態を維持する必要があることがほとんどです。この状態管理の主要なメカニズムの一つが「クッキー(Cookie)」です。

Go言語の net/http パッケージは、HTTPクライアントとサーバーを構築するための基本的な機能を提供します。このコミット以前は、http.Client は自動的にクッキーを管理する機能を持っていませんでした。つまり、開発者は手動で Set-Cookie ヘッダーからクッキーを抽出し、次のリクエストの Cookie ヘッダーに含める必要がありました。これは非常に煩雑であり、特にリダイレクトや複雑なセッション管理を伴う場合には、エラーの温床となりがちでした。

このコミットは、http.Client にクッキー管理の責務を委譲するための標準的なインターフェース CookieJar を導入することで、この問題を解決しようとしています。これにより、開発者は CookieJar インターフェースを実装する型(例えば、メモリ内でのクッキー管理や、ディスクへの永続化を行う型)を http.Client に設定するだけで、自動的なクッキーの送受信と保存が可能になります。これは、より堅牢で使いやすいHTTPクライアントの実現に向けた重要な一歩です。

前提知識の解説

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

クッキーは主に以下の目的で使用されます:

  • セッション管理: ユーザーのログイン状態、ショッピングカートの内容など。
  • パーソナライゼーション: ユーザー設定、テーマ、言語設定など。
  • トラッキング: ユーザーの行動追跡、広告ターゲティングなど。

クッキーの送受信はHTTPヘッダーを通じて行われます。

  • サーバーからブラウザへ: Set-Cookie ヘッダー
  • ブラウザからサーバーへ: Cookie ヘッダー

「クッキージャー」という概念は、HTTPクライアントが受信したクッキーを保存し、後続のリクエストで適切なクッキーを送信するために使用するストレージメカニズムを指します。これは、ブラウザがクッキーを管理する方法を抽象化したものです。クッキージャーは、クッキーの有効期限、ドメイン、パスなどの属性を考慮して、どのクッキーを保存し、どのリクエストに含めるべきかを決定します。

RFC 6265 (HTTP State Management Mechanism)

RFC 6265は、HTTPクッキーの動作を定義するIETF(Internet Engineering Task Force)の標準仕様です。これは、以前のRFC 2109やRFC 2965を廃止し、実際のウェブでのクッキーの使用方法をより正確に反映するように作成されました。

RFC 6265は、クッキーの構文、Set-Cookie ヘッダーと Cookie ヘッダーの処理ルール、セキュリティに関する考慮事項などを詳細に規定しています。CookieJar の実装は、このRFCの規定に準拠することで、標準的で予測可能なクッキー管理動作を保証する必要があります。特に、クッキーのドメイン、パス、有効期限、Secure/HttpOnly属性などの処理は、RFC 6265に厳密に従う必要があります。

Go言語の net/http/cookiejar パッケージ(このコミットで導入されたインターフェースを実装する具体的な型を提供するパッケージ)は、RFC 6265に準拠したインメモリの http.CookieJar を提供します。

技術的詳細

このコミットの核心は、net/http パッケージに CookieJar インターフェースを導入し、http.Client がこのインターフェースを通じてクッキーを管理できるようにした点です。

CookieJar インターフェース

CookieJar インターフェースは、以下の2つのメソッドを定義します。

type CookieJar interface {
	// SetCookies handles the receipt of the cookies in a reply for the
	// given URL.  It may or may not choose to save the cookies, depending
	// on the jar's policy and implementation.
	SetCookies(u *url.URL, cookies []*Cookie)

	// Cookies returns the cookies to send in a request for the given URL.
	// It is up to the implementation to honor the standard cookie use
	// restrictions such as in RFC 6265.
	Cookies(u *url.URL) []*Cookie
}
  • SetCookies(u *url.URL, cookies []*Cookie): このメソッドは、特定のURL (u) から受信したクッキーのリスト (cookies) を処理するために呼び出されます。CookieJar の実装は、これらのクッキーを保存するかどうか、どのように保存するかを決定します。例えば、有効期限が切れているクッキーは保存しない、といったロジックがここに実装されます。
  • Cookies(u *url.URL) []*Cookie: このメソッドは、特定のURL (u) へのリクエストを送信する際に、そのリクエストに含めるべきクッキーのリストを返します。CookieJar の実装は、RFC 6265などの標準に従って、適切なドメインとパスに一致するクッキーを選択して返します。

このインターフェースの導入により、http.Client はクッキーの具体的な保存・取得ロジックを知る必要がなくなり、CookieJar の実装にその責務を委譲できるようになりました。これにより、クッキー管理のロジックを柔軟に差し替えることが可能になります。例えば、メモリに保存するだけでなく、ファイルシステムやデータベースにクッキーを永続化するカスタム CookieJar を作成することもできます。

http.ClientJar フィールド

http.Client 構造体には、新しく Jar CookieJar フィールドが追加されました。

type Client struct {
	// ... 既存のフィールド ...

	// Jar specifies the cookie jar.
	// If Jar is nil, cookies are not sent in requests and ignored
	// in responses.
	Jar CookieJar
}
  • Jar フィールドに CookieJar インターフェースを実装したオブジェクトを設定することで、そのクライアントは自動的にクッキーを管理するようになります。
  • Jarnil の場合、クライアントはクッキーを送受信せず、クッキー管理は行われません。これは、クッキーが不要な場合や、手動でクッキーを管理したい場合に利用できます。

blackHoleJar

このコミットでは、blackHoleJar という内部的な型も導入されています。

type blackHoleJar struct{}

func (blackHoleJar) SetCookies(u *url.URL, cookies []*Cookie) {}
func (blackHoleJar) Cookies(u *url.URL) []*Cookie             { return nil }

blackHoleJarCookieJar インターフェースを実装していますが、SetCookies メソッドは何もしません(クッキーを保存しない)し、Cookies メソッドは常に nil を返します(クッキーを送信しない)。これは、http.ClientJar フィールドが nil の場合に内部的に使用され、クッキー管理を無効にするためのプレースホルダーとして機能します。これにより、nil チェックを減らし、コードを簡潔に保つことができます。

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

このコミットでは、主に以下の3つのファイルが変更されています。

  1. src/pkg/net/http/Makefile:

    • jar.go がビルド対象のファイルリスト GOFILES に追加されました。これにより、新しく作成される jar.gonet/http パッケージの一部としてコンパイルされるようになります。
  2. src/pkg/net/http/client.go:

    • Client 構造体に Jar CookieJar フィールドが追加されました。
    • doFollowingRedirects 関数内で、Client.Jarnil の場合に blackHoleJar を使用するロジックが追加されました。
    • リクエスト送信前に jar.Cookies(req.URL) を呼び出してクッキーを取得し、リクエストヘッダーに追加する処理が追加されました。
    • レスポンス受信後に jar.SetCookies(req.URL, c) を呼び出して、レスポンスに含まれるクッキーを CookieJar に保存する処理が追加されました。
  3. src/pkg/net/http/jar.go:

    • 新しく作成されたファイルです。
    • CookieJar インターフェースが定義されています。
    • blackHoleJar 型とそのメソッドが定義されています。

コアとなるコードの解説

src/pkg/net/http/client.go の変更点

client.go の変更は、http.ClientCookieJar インターフェースを利用してクッキーを自動的に管理するメカニズムを実装しています。

// Client 構造体への Jar フィールドの追加
type Client struct {
	// ...
	Jar CookieJar
}

Client 構造体に Jar フィールドが追加されたことで、ユーザーは http.Client インスタンスを作成し、その Jar フィールドにカスタムの CookieJar 実装を割り当てるだけで、クッキー管理機能を有効にできます。

// doFollowingRedirects 関数内の変更
jar := c.Jar
if jar == nil {
	jar = blackHoleJar{}
}

doFollowingRedirects 関数は、HTTPリクエストの送信とリダイレクトの処理を担当する内部関数です。この部分では、ClientJar フィールドが設定されているかを確認します。もし Jarnil であれば、blackHoleJar インスタンスが jar 変数に割り当てられます。これにより、後続のクッキー関連の操作で nil ポインタ参照を防ぎつつ、クッキー管理を実質的に無効にします。

// リクエスト送信前のクッキー追加
for _, cookie := range jar.Cookies(req.URL) {
	req.AddCookie(cookie)
}

これは、HTTPリクエストを送信する直前に行われる処理です。jar.Cookies(req.URL) を呼び出すことで、現在のリクエストURL (req.URL) に関連するクッキーが CookieJar から取得されます。取得された各クッキーは、req.AddCookie(cookie) を使ってリクエストの Cookie ヘッダーに追加されます。これにより、サーバーはクライアントからのリクエストに適切なクッキーが含まれていることを認識できます。

// レスポンス受信後のクッキー保存
if c := r.Cookies(); len(c) > 0 {
	jar.SetCookies(req.URL, c)
}

これは、HTTPレスポンスを受信した直後に行われる処理です。r.Cookies() はレスポンスに含まれる Set-Cookie ヘッダーからパースされたクッキーのリストを返します。もしクッキーが存在すれば (len(c) > 0)、jar.SetCookies(req.URL, c) を呼び出して、これらのクッキーを CookieJar に保存します。CookieJar の実装は、これらのクッキーを適切に処理し、将来のリクエストのために保存するかどうかを決定します。

src/pkg/net/http/jar.go の新規追加

このファイルは、CookieJar インターフェースの定義と、そのインターフェースの最小限の実装である blackHoleJar を提供します。これにより、net/http パッケージの外部から CookieJar インターフェースを参照できるようになり、カスタムのクッキー管理ロジックを実装する際の基盤となります。

関連リンク

参考にした情報源リンク