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

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

このコミットは、Go言語の標準ライブラリである net/http パッケージにおいて、ClientPOST リクエストを送信する際に、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.ClientPOST リクエストを送信する際に、クライアントに設定された CookieJar から適切なクッキーをリクエストヘッダーに追加しないというものでした。

HTTPプロトコルにおいて、クッキーはセッション管理やユーザー認証など、様々な目的で利用されます。クライアントがサーバーに対してリクエストを送信する際、以前のレスポンスでサーバーから受け取ったクッキーを、後続のリクエストに含めて送信するのが一般的な動作です。net/http パッケージの Client は、このクッキーの管理を CookieJar インターフェースを実装したオブジェクトに委ねています。しかし、特定の条件下(特に POST リクエストの場合)で、ClientCookieJar からクッキーを取得し、それをリクエストに付与する処理が欠落していました。

このバグにより、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 インターフェース: クッキーの保存と取得を管理するためのインターフェースです。SetCookiesCookies の2つのメソッドを持ちます。Client はこのインターフェースを実装したオブジェクトを Jar フィールドに設定することで、自動的にクッキーを管理できます。Go言語の標準ライブラリには、このインターフェースのデフォルト実装として net/http/cookiejar パッケージが提供されています。

HTTP POST リクエスト

POST メソッドは、指定されたリソースにデータを送信するために使用されます。通常、Webフォームの送信や、APIへのデータ送信などに利用されます。POST リクエストのデータは、リクエストボディに含まれます。

クッキー (Cookies)

HTTPクッキーは、WebサーバーがユーザーのWebブラウザに送信する小さなデータ片です。ブラウザはこれらのクッキーを保存し、同じサーバーへの後続のリクエストごとにそれらを送り返します。これにより、サーバーはユーザーの状態を記憶したり、ユーザーを識別したりすることができます。クッキーは、セッション管理、パーソナライゼーション、トラッキングなどに広く利用されます。

技術的詳細

このコミットが修正する問題は、http.ClientPost メソッドが、Client に設定された CookieJar からクッキーを取得し、それを送信する http.Request に追加する処理を欠いていた点にあります。

通常の Get リクエストなどでは、ClientJar.Cookies(req.URL) を呼び出して関連するクッキーを取得し、req.AddCookie(cookie) を使ってリクエストヘッダーにクッキーを追加します。しかし、Post メソッドの内部実装では、このステップが省略されていました。

このコミットでは、client.goClient.Post メソッド内に、Client.Jar が設定されている場合に Jar.Cookies(req.URL) を呼び出し、取得したクッキーを req.AddCookie(cookie) を使ってリクエストに追加するロジックが追加されています。これにより、POST リクエストでも CookieJar に保存されたクッキーが正しく送信されるようになります。

また、このコミットの主要な部分として、client_test.go に新しいテストケース TestClientSendsCookieFromJar が追加されています。このテストケースは、ClientGet, Head, Post, PostForm, Do といった様々なリクエストメソッドで CookieJar からクッキーを正しく送信するかどうかを検証します。これにより、修正が意図通りに機能することを確認し、将来的な回帰を防ぐための安全網が提供されます。

コミットメッセージにある「A subsequent CL will refactor the fix as proposed issue 3511」という記述は、このコミットでの修正が暫定的なものであり、より洗練されたリファクタリングが後続の変更リスト(Change List, CL)で行われる予定であることを示唆しています。これは、Go言語の開発プロセスにおいて、まず問題を修正し、その後にコードの品質や設計を改善するためのリファクタリングを行うというアプローチが取られることがあることを示しています。

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

このコミットによるコードの変更は以下の2つのファイルにわたります。

  1. 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())
      
  2. src/pkg/net/http/client_test.go: ClientCookieJar からクッキーを正しく送信するかどうかを検証する新しいテストケース 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)
		}
	}

このコードブロックは、以下の処理を行います。

  1. if c.Jar != nil: ClientJar フィールド(CookieJar インターフェースを実装したオブジェクト)が nil でないか、つまりクッキー管理が有効になっているかを確認します。
  2. for _, cookie := range c.Jar.Cookies(req.URL): c.Jar.Cookies(req.URL) を呼び出し、現在のリクエストURLに関連するすべてのクッキーを取得します。CookieJarCookies メソッドは、指定されたURLに対して送信すべきクッキーのリストを返します。
  3. req.AddCookie(cookie): 取得した各クッキーを、送信される http.Request オブジェクトのヘッダーに追加します。これにより、クッキーがリクエストと共にサーバーに送信されるようになります。

この変更により、POST リクエストを送信する際にも、ClientCookieJar に保存されている適切なクッキーを自動的に含めるようになり、他の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

参考にした情報源リンク