[インデックス 13104] ファイルの概要
このコミットは、Go言語の標準ライブラリである net/http
パッケージにおいて、Client
が POST
リクエストを送信する際に、CookieJar
に保存されているクッキーを適切にリクエストに追加しないというバグを修正するものです。主な変更は、この問題を再現するテストケースの追加と、その問題を修正するための小さなコード変更です。
コミット
- コミットハッシュ:
b4456df6d237e2f8dc66c3f405d2d79836aa797d
- Author: Volker Dobler dr.volker.dobler@gmail.com
- Date: Mon May 21 10:57:15 2012 -0700
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b4456df6d237e2f8dc66c3f405d2d79836aa797d
元コミット内容
net/http: add cookies from jar to POST request.
The main content of this CL is a test case checking the reported
issue 3511 and a tiny fix for it. A subsequent CL will refactor
the fix as proposed issue 3511.
Fixes #3511.
R=golang-dev, steven.hartland, bradfitz
CC=golang-dev
https://golang.org/cl/6013049
変更の背景
このコミットは、Go言語の net/http
パッケージにおける既知のバグ、具体的には Issue 3511 を修正するために作成されました。この問題は、http.Client
が POST
リクエストを送信する際に、クライアントに設定された CookieJar
から適切なクッキーをリクエストヘッダーに追加しないというものでした。
HTTPプロトコルにおいて、クッキーはセッション管理やユーザー認証など、様々な目的で利用されます。クライアントがサーバーに対してリクエストを送信する際、以前のレスポンスでサーバーから受け取ったクッキーを、後続のリクエストに含めて送信するのが一般的な動作です。net/http
パッケージの Client
は、このクッキーの管理を CookieJar
インターフェースを実装したオブジェクトに委ねています。しかし、特定の条件下(特に POST
リクエストの場合)で、Client
が CookieJar
からクッキーを取得し、それをリクエストに付与する処理が欠落していました。
このバグにより、POST
リクエストを必要とするWebアプリケーションやAPIとの連携において、セッション情報が正しく引き継がれないなどの問題が発生する可能性がありました。このコミットは、この重要な機能の欠落を修正し、net/http
クライアントの堅牢性と互換性を向上させることを目的としています。
前提知識の解説
Go言語の net/http
パッケージ
net/http
パッケージは、Go言語におけるHTTPクライアントおよびサーバーの実装を提供します。Webアプリケーションの構築やHTTPベースのサービスとの連携において中心的な役割を果たします。
http.Client
: HTTPリクエストを送信し、HTTPレスポンスを受信するクライアントを表す構造体です。Get
,Post
,Head
,Do
などのメソッドを提供し、様々なHTTPメソッドでのリクエスト送信をサポートします。http.Request
: HTTPリクエストを表す構造体です。URL、メソッド(GET, POSTなど)、ヘッダー、ボディなどの情報を含みます。http.Response
: HTTPレスポンスを表す構造体です。ステータスコード、ヘッダー、ボディなどの情報を含みます。http.Cookie
: HTTPクッキーを表す構造体です。名前、値、ドメイン、パス、有効期限などの情報を含みます。http.CookieJar
インターフェース: クッキーの保存と取得を管理するためのインターフェースです。SetCookies
とCookies
の2つのメソッドを持ちます。Client
はこのインターフェースを実装したオブジェクトをJar
フィールドに設定することで、自動的にクッキーを管理できます。Go言語の標準ライブラリには、このインターフェースのデフォルト実装としてnet/http/cookiejar
パッケージが提供されています。
HTTP POST リクエスト
POST
メソッドは、指定されたリソースにデータを送信するために使用されます。通常、Webフォームの送信や、APIへのデータ送信などに利用されます。POST
リクエストのデータは、リクエストボディに含まれます。
クッキー (Cookies)
HTTPクッキーは、WebサーバーがユーザーのWebブラウザに送信する小さなデータ片です。ブラウザはこれらのクッキーを保存し、同じサーバーへの後続のリクエストごとにそれらを送り返します。これにより、サーバーはユーザーの状態を記憶したり、ユーザーを識別したりすることができます。クッキーは、セッション管理、パーソナライゼーション、トラッキングなどに広く利用されます。
技術的詳細
このコミットが修正する問題は、http.Client
の Post
メソッドが、Client
に設定された CookieJar
からクッキーを取得し、それを送信する http.Request
に追加する処理を欠いていた点にあります。
通常の Get
リクエストなどでは、Client
は Jar.Cookies(req.URL)
を呼び出して関連するクッキーを取得し、req.AddCookie(cookie)
を使ってリクエストヘッダーにクッキーを追加します。しかし、Post
メソッドの内部実装では、このステップが省略されていました。
このコミットでは、client.go
の Client.Post
メソッド内に、Client.Jar
が設定されている場合に Jar.Cookies(req.URL)
を呼び出し、取得したクッキーを req.AddCookie(cookie)
を使ってリクエストに追加するロジックが追加されています。これにより、POST
リクエストでも CookieJar
に保存されたクッキーが正しく送信されるようになります。
また、このコミットの主要な部分として、client_test.go
に新しいテストケース TestClientSendsCookieFromJar
が追加されています。このテストケースは、Client
が Get
, Head
, Post
, PostForm
, Do
といった様々なリクエストメソッドで CookieJar
からクッキーを正しく送信するかどうかを検証します。これにより、修正が意図通りに機能することを確認し、将来的な回帰を防ぐための安全網が提供されます。
コミットメッセージにある「A subsequent CL will refactor the fix as proposed issue 3511」という記述は、このコミットでの修正が暫定的なものであり、より洗練されたリファクタリングが後続の変更リスト(Change List, CL)で行われる予定であることを示唆しています。これは、Go言語の開発プロセスにおいて、まず問題を修正し、その後にコードの品質や設計を改善するためのリファクタリングを行うというアプローチが取られることがあることを示しています。
コアとなるコードの変更箇所
このコミットによるコードの変更は以下の2つのファイルにわたります。
-
src/pkg/net/http/client.go
:Client
構造体のPost
メソッドにクッキーを追加するロジックが追加されました。- 変更行数: 5行追加
- 差分:
--- a/src/pkg/net/http/client.go +++ b/src/pkg/net/http/client.go @@ -278,6 +278,11 @@ func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, return nil, err } req.Header.Set("Content-Type", bodyType) + if c.Jar != nil { + for _, cookie := range c.Jar.Cookies(req.URL) { + req.AddCookie(cookie) + } + } r, err = send(req, c.Transport) if err == nil && c.Jar != nil { c.Jar.SetCookies(req.URL, r.Cookies())
-
src/pkg/net/http/client_test.go
:Client
がCookieJar
からクッキーを正しく送信するかどうかを検証する新しいテストケースTestClientSendsCookieFromJar
が追加されました。- 変更行数: 25行追加
- 差分:
--- a/src/pkg/net/http/client_test.go +++ b/src/pkg/net/http/client_test.go @@ -256,6 +256,31 @@ var echoCookiesRedirectHandler = HandlerFunc(func(w ResponseWriter, r *Request)\ }\ })\ \n+func TestClientSendsCookieFromJar(t *testing.T) {\n+\ttr := &recordingTransport{}\n+\tclient := &Client{Transport: tr}\n+\tclient.Jar = &TestJar{perURL: make(map[string][]*Cookie)}\n+\tus := "http://dummy.faketld/"\n+\tu, _ := url.Parse(us)\n+\tclient.Jar.SetCookies(u, expectedCookies)\n+\n+\tclient.Get(us) // Note: doesn't hit network\n+\tmatchReturnedCookies(t, expectedCookies, tr.req.Cookies())\n+\n+\tclient.Head(us) // Note: doesn't hit network\n+\tmatchReturnedCookies(t, expectedCookies, tr.req.Cookies())\n+\n+\tclient.Post(us, "text/plain", strings.NewReader("body")) // Note: doesn't hit network\n+\tmatchReturnedCookies(t, expectedCookies, tr.req.Cookies())\n+\n+\tclient.PostForm(us, url.Values{}) // Note: doesn't hit network\n+\tmatchReturnedCookies(t, expectedCookies, tr.req.Cookies())\n+\n+\treq, _ := NewRequest("GET", us, nil)\n+\tclient.Do(req) // Note: doesn't hit network\n+\tmatchReturnedCookies(t, expectedCookies, tr.req.Cookies())\n+}\n+\n // Just enough correctness for our redirect tests. Uses the URL.Host as the\n // scope of all cookies.\n type TestJar struct {
コアとなるコードの解説
src/pkg/net/http/client.go
の変更
Client.Post
メソッド内の変更は非常にシンプルですが、重要な機能を追加しています。
if c.Jar != nil {
for _, cookie := range c.Jar.Cookies(req.URL) {
req.AddCookie(cookie)
}
}
このコードブロックは、以下の処理を行います。
if c.Jar != nil
:Client
のJar
フィールド(CookieJar
インターフェースを実装したオブジェクト)がnil
でないか、つまりクッキー管理が有効になっているかを確認します。for _, cookie := range c.Jar.Cookies(req.URL)
:c.Jar.Cookies(req.URL)
を呼び出し、現在のリクエストURLに関連するすべてのクッキーを取得します。CookieJar
のCookies
メソッドは、指定されたURLに対して送信すべきクッキーのリストを返します。req.AddCookie(cookie)
: 取得した各クッキーを、送信されるhttp.Request
オブジェクトのヘッダーに追加します。これにより、クッキーがリクエストと共にサーバーに送信されるようになります。
この変更により、POST
リクエストを送信する際にも、Client
が CookieJar
に保存されている適切なクッキーを自動的に含めるようになり、他のHTTPメソッド(GET
など)と同様の一貫したクッキー処理が実現されます。
src/pkg/net/http/client_test.go
の変更
追加された TestClientSendsCookieFromJar
テスト関数は、この修正の検証に不可欠です。
func TestClientSendsCookieFromJar(t *testing.T) {
tr := &recordingTransport{}
client := &Client{Transport: tr}
client.Jar = &TestJar{perURL: make(map[string][]*Cookie)}
us := "http://dummy.faketld/"
u, _ := url.Parse(us)
client.Jar.SetCookies(u, expectedCookies)
client.Get(us) // Note: doesn't hit network
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
client.Head(us) // Note: doesn't hit network
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
client.Post(us, "text/plain", strings.NewReader("body")) // Note: doesn't hit network
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
client.PostForm(us, url.Values{}) // Note: doesn't hit network
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
req, _ := NewRequest("GET", us, nil)
client.Do(req) // Note: doesn't hit network
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
}
このテストの主要なポイントは以下の通りです。
recordingTransport
: これは、実際のネットワークリクエストを送信する代わりに、送信されるhttp.Request
オブジェクトを記録するカスタムのhttp.RoundTripper
実装です。これにより、テストはネットワークに依存せず、Client
が生成するリクエストの内容を検査できます。TestJar
: これは、http.CookieJar
インターフェースのシンプルなテスト実装です。テストに必要なクッキーを事前に設定し、Client
がそれらを正しく取得できることをシミュレートします。- クッキーの設定:
client.Jar.SetCookies(u, expectedCookies)
を呼び出すことで、テスト用のCookieJar
に期待されるクッキーのセットを事前に設定します。 - 様々なメソッドのテスト:
client.Get
,client.Head
,client.Post
,client.PostForm
,client.Do
といったClient
の主要なリクエスト送信メソッドをそれぞれ呼び出し、それぞれのメソッドがCookieJar
からクッキーを正しく取得し、リクエストに含めているかを検証します。 matchReturnedCookies
: これは、送信されたリクエストに含まれるクッキーが、事前に設定したexpectedCookies
と一致するかどうかを検証するヘルパー関数です。
この包括的なテストケースにより、POST
リクエストだけでなく、他のリクエストメソッドにおいてもクッキーの処理が正しく行われることが保証されます。
関連リンク
- Go Change-Id:
I2222222222222222222222222222222222222222
(コミットメッセージに記載されているhttps://golang.org/cl/6013049
は、Goの古いChange Listシステムのものであり、現在のGitHubのコミットハッシュとは直接対応していません。しかし、このコミットがそのCLに対応するものであることは明らかです。) - Go Issue 3511: https://github.com/golang/go/issues/3511
参考にした情報源リンク
- GitHub golang/go Issue #3511: https://github.com/golang/go/issues/3511
- Go言語
net/http
パッケージ公式ドキュメント (当時のバージョンに基づく): https://pkg.go.dev/net/http (現在のドキュメント) - HTTP クッキー (MDN Web Docs): https://developer.mozilla.org/ja/docs/Web/HTTP/Cookies