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

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

このコミットは、Go言語の標準ライブラリである net/http パッケージにおける Client 構造体の CheckRedirect メソッドに関するドキュメントの不正確さを修正し、同時にテストコードをより厳密にするものです。具体的には、CheckRedirect がエラーを返した場合の url.Error によるエラーラッピングについて明記し、タイプミスを修正しています。

コミット

  • コミットハッシュ: 93b7d1bf1eefbc3ff33ec935b3c15601820bd4f3
  • 作者: Brad Fitzpatrick bradfitz@golang.org
  • 日付: 2012年6月24日 (日) 10:41:12 -0700

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

https://github.com/golang/go/commit/93b7d1bf1eefbc3ff33ec935b3c15601820bd4f3

元コミット内容

net/http: fix doc inaccuracy and typo; tighten test

Note url.Error wrapping, and s/issue/issuing/.

Fixes #3724

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/6294093

変更の背景

この変更は、GoのIssue #3724 に対応するものです。元の net/http パッケージの Client.CheckRedirect メソッドのドキュメントには、CheckRedirect がエラーを返した場合に、そのエラーがどのように呼び出し元に伝播されるかについての記述が不正確でした。具体的には、返されたエラーが url.Error 型でラップされるという重要な情報が欠けていました。また、ドキュメント内にタイプミス (issueissuing であるべき箇所) が存在しました。

この不正確なドキュメントは、開発者が CheckRedirect メソッドの挙動を誤解し、エラーハンドリングを適切に行えない可能性がありました。そのため、ドキュメントを修正し、その挙動を検証するためのテストコードをより厳密にすることで、ライブラリの正確性と堅牢性を向上させる必要がありました。

前提知識の解説

Go言語の net/http パッケージ

net/http パッケージは、Go言語でHTTPクライアントおよびサーバーを実装するための標準ライブラリです。WebアプリケーションやAPIクライアントを構築する上で中心的な役割を果たします。

  • http.Client 構造体: HTTPリクエストを送信するためのクライアントを表します。この構造体を通じて、GET、POSTなどのHTTPメソッドを実行し、リダイレクト処理やクッキー管理など、HTTPクライアントとしての様々な挙動を制御できます。
  • http.Client.CheckRedirect メソッド: http.Client のフィールドの一つで、リダイレクトを処理するためのカスタム関数を設定できます。この関数は、リダイレクトが発生するたびに呼び出され、リダイレクトを許可するか、特定のエラーを返すかなどを決定できます。もしこの関数がエラーを返した場合、http.Client はそれ以上のリダイレクトを停止し、そのエラーを呼び出し元に返します。
  • url.Error: net/url パッケージで定義されているエラー型です。URLのパースや処理中に発生したエラーをラップするために使用されます。net/http パッケージでは、HTTPリクエストの処理中にURLに関連する問題が発生した場合に、この url.Error で元のエラーをラップして返すことがあります。これにより、エラーが発生した操作(例: "Get"、"Post")とURL、そして元のエラー情報を一貫した形式で提供できます。

Go言語のエラーハンドリングとエラーラッピング

Go言語では、エラーは error インターフェースを実装する値として扱われます。エラーハンドリングは通常、関数の戻り値として (result, error) のペアを返すことで行われます。

エラーラッピングとは、あるエラーを別のエラーの内部に含めることで、エラーのコンテキストや原因をより詳細に伝える手法です。Go 1.13以降では fmt.Errorf%w 動詞を使ってエラーをラップする標準的な方法が導入されましたが、このコミットが作成された2012年時点では、カスタムエラー型(今回の url.Error のように)が内部に別のエラーを保持する形でラッピングが行われていました。これにより、エラーチェーンを辿って元のエラーやその発生コンテキストを特定することが可能になります。

技術的詳細

このコミットの技術的な変更点は以下の2つです。

  1. ドキュメントの修正 (src/pkg/net/http/client.go): http.Client 構造体の CheckRedirect フィールドのコメントが修正されました。

    • タイプミス: instead of issue the Request req. から instead of issuing the Request req. へと issueissuing に修正されました。これは単なるスペルミス修正です。
    • 不正確な記述の修正: returns that error の後に (wrapped in a url.Error) という記述が追加されました。これにより、CheckRedirect がエラーを返した場合、そのエラーが直接返されるのではなく、url.Error 型でラップされて返されるという重要な挙動が明示されました。これは、開発者が CheckRedirect から返されるエラーを適切に型アサートしたり、エラーチェーンを処理したりするために不可欠な情報です。
  2. テストコードの厳密化 (src/pkg/net/http/client_test.go): TestRedirects 関数内のテストアサーションが変更されました。

    • 変更前は、エラーメッセージの文字列比較によってエラーの検証を行っていました。これはエラーメッセージの変更に脆弱であり、エラーの型や内部構造を検証していませんでした。
    • 変更後は、返されたエラーが *url.Error 型であり、かつその内部に期待するエラー (checkErr) が含まれていることを urlError.Err != checkErr で確認するように変更されました。これは、ドキュメントの修正内容(url.Error でラップされること)と整合性を保ち、より堅牢で正確なエラー検証を行うための改善です。これにより、エラーメッセージの文字列に依存することなく、エラーの構造と内容を直接検証できるようになりました。

これらの変更により、net/http クライアントのリダイレクト処理におけるエラーの挙動がより明確になり、開発者がより正確なコードを書けるようになりました。

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

diff --git a/src/pkg/net/http/client.go b/src/pkg/net/http/client.go
index fba775fddc..89441424e1 100644
--- a/src/pkg/net/http/client.go
+++ b/src/pkg/net/http/client.go
@@ -36,7 +36,8 @@ type Client struct {
  // following an HTTP redirect. The arguments req and via
  // are the upcoming request and the requests made already,
  // oldest first. If CheckRedirect returns an error, the client
- // returns that error instead of issue the Request req.
+ // returns that error (wrapped in a url.Error) instead of
+ // issuing the Request req.
  //
  // If CheckRedirect is nil, the Client uses its default policy,
  // which is to stop after 10 consecutive requests.
diff --git a/src/pkg/net/http/client_test.go b/src/pkg/net/http/client_test.go
index e2a08204e0..fe4b626a31 100644
--- a/src/pkg/net/http/client_test.go
+++ b/src/pkg/net/http/client_test.go
@@ -231,8 +231,8 @@ func TestRedirects(t *testing.T) {

  checkErr = errors.New("no redirects allowed")
  res, err = c.Get(ts.URL)
- if e, g := "Get /?n=1: no redirects allowed", fmt.Sprintf("%v\", err); e != g {
-  t.Errorf("with redirects forbidden, expected error %q, got %q", e, g)
+ if urlError, ok := err.(*url.Error); !ok || urlError.Err != checkErr {
+  t.Errorf("with redirects forbidden, expected a *url.Error with our 'no redirects allowed' error inside; got %#v (%q)", err, err)
  }
 }

コアとなるコードの解説

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

 // If CheckRedirect returns an error, the client
-// returns that error instead of issue the Request req.
+// returns that error (wrapped in a url.Error) instead of
+// issuing the Request req.

この変更は、Client.CheckRedirect メソッドのドキュメントコメントを修正しています。

  • - // returns that error instead of issue the Request req.
    • 元の記述では、「クライアントはそのエラーを返す」とだけ書かれていました。また、「issue」は「issuing」のタイプミスでした。
  • + // returns that error (wrapped in a url.Error) instead of
  • + // issuing the Request req.
    • 修正後では、「そのエラーを url.Error でラップして返す」という重要な情報が追加されました。これにより、CheckRedirect がカスタムエラーを返した場合、それが直接返されるのではなく、url.Error 型のラッパーの中に含まれて返されることが明確になります。
    • タイプミスも issue から issuing に修正され、文法的に正しくなりました。

このドキュメントの修正は、CheckRedirect のカスタムエラーハンドリングを実装する開発者にとって非常に重要です。これにより、返されるエラーの型を正しく予測し、適切なエラー処理ロジック(例: errors.As や型アサーションによる url.Error のチェック)を記述できるようになります。

src/pkg/net/http/client_test.go の変更

 if urlError, ok := err.(*url.Error); !ok || urlError.Err != checkErr {
  t.Errorf("with redirects forbidden, expected a *url.Error with our 'no redirects allowed' error inside; got %#v (%q)", err, err)
 }

このテストコードの変更は、CheckRedirect がエラーを返した際の挙動をより厳密に検証するためのものです。

  • - if e, g := "Get /?n=1: no redirects allowed", fmt.Sprintf("%v", err); e != g {

  • - t.Errorf("with redirects forbidden, expected error %q, got %q", e, g)

    • 元のテストでは、fmt.Sprintf("%v", err) を使ってエラーを文字列に変換し、その文字列が期待するエラーメッセージと一致するかどうかを比較していました。この方法は、エラーメッセージのテキストが変更されるとテストが失敗するという脆弱性がありました。また、エラーが url.Error でラップされているという事実を検証していませんでした。
  • + if urlError, ok := err.(*url.Error); !ok || urlError.Err != checkErr {

  • + t.Errorf("with redirects forbidden, expected a *url.Error with our 'no redirects allowed' error inside; got %#v (%q)", err, err)

    • 修正後のテストでは、まず err.(*url.Error) を使って、返されたエラーが *url.Error 型に型アサートできるかを確認しています。!ok は型アサートが失敗した場合(つまり、エラーが *url.Error でない場合)に真となります。
    • 次に、urlError.Err != checkErr で、url.Error の内部にラップされているエラー (urlError.Err) が、テストで意図的に設定したエラー (checkErr) と一致するかどうかを検証しています。
    • この変更により、テストはエラーの「メッセージ」ではなく、エラーの「型」と「内部にラップされたエラー」という構造的な側面を検証するようになり、より堅牢で正確なテストになりました。これは、ドキュメントの修正内容と完全に一致する検証方法です。

関連リンク

  • Go Change-Id: https://golang.org/cl/6294093
  • Go Issue: https://github.com/golang/go/issues/3724

参考にした情報源リンク

  • Go言語の公式ドキュメント (net/http パッケージ、net/url パッケージ)
  • Go言語のエラーハンドリングに関する一般的な情報源
  • GitHubのGoリポジトリにおける関連Issue (#3724) とコミット履歴
  • Go言語の url.Error 型に関する解説記事
  • Go言語の CheckRedirect メソッドに関する解説記事