[インデックス 18741] ファイルの概要
このコミットは、Go言語の標準ライブラリ net/http
パッケージ内のテストコード client_test.go
に対する修正です。具体的には、一部のWindows環境で発生していたテストの失敗を解消することを目的としています。クライアントが切断された際のネットワークエラーの挙動がWindowsとUnix系OSで異なるため、エラーメッセージの比較方法をより汎用的なものに変更しています。
コミット
commit a5166a9512215483dae4e2963435cb3803a9f330
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Tue Mar 4 11:55:35 2014 -0800
net/http: fix test failure on some Windows machines
The network connection dies differently from the server's
perspective on (some) Windows when the client goes away. Match
on the common prefix (common to Unix and Windows) instead of
the network error part.
Fixes #7456
LGTM=josharian
R=golang-codereviews, josharian
CC=alex.brainman, golang-codereviews, iant
https://golang.org/cl/70010050
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/a5166a9512215483dae4e2963435cb3803a9f330
元コミット内容
net/http: fix test failure on some Windows machines
このコミットは、一部のWindowsマシンで発生するテストの失敗を修正します。クライアントが切断された際に、サーバー側から見たネットワーク接続の終了の仕方が(一部の)Windowsでは異なります。そのため、ネットワークエラーの特定の部分ではなく、UnixとWindowsに共通するプレフィックスでマッチングするように変更します。
Fixes #7456
LGTM=josharian R=golang-codereviews, josharian CC=alex.brainman, golang-codereviews, iant https://golang.org/cl/70010050
変更の背景
このコミットの背景には、Go言語の net/http
パッケージのテストスイートが、特定のWindows環境で不安定になるという問題がありました。具体的には、TestClientWithIncorrectTLSServerName
というテストが、クライアントが意図的に不正なTLSサーバー名で接続を試み、サーバーが接続を切断するシナリオを検証していました。
問題は、クライアントが接続を終了した際に、サーバー側で発生するネットワークエラーのメッセージが、WindowsとUnix系OS(Linux, macOSなど)で一貫していなかった点にあります。Unix系OSでは特定のエラーメッセージ(例:「bad certificate」)が期待される一方で、Windowsでは異なる、あるいはより一般的なエラーメッセージが返されることがありました。これにより、テストが期待するエラーメッセージと実際のメッセージが一致せず、テストが失敗していました。
開発者は、このプラットフォーム間の挙動の違いを吸収し、テストの信頼性を向上させる必要がありました。テストは、特定のOSに依存しない形で、期待されるエラーの「種類」を検証すべきであり、厳密なエラーメッセージの文字列に依存すべきではありません。
Fixes #7456
と記載されていますが、これはGoプロジェクトの内部的な課題追跡システムにおけるチケット番号である可能性が高く、一般的なGitHubのIssueとして公開されているものではないようです。しかし、https://golang.org/cl/70010050
というGoのコードレビューシステム(Gerritベース)へのリンクが提供されており、これがこの変更に関する公式な議論と承認の記録となります。
前提知識の解説
- Go言語の
net/http
パッケージ: Go言語の標準ライブラリの一部で、HTTPクライアントとサーバーの実装を提供します。WebアプリケーションやAPIの構築に不可欠なパッケージです。 - TLS (Transport Layer Security): インターネット上での通信を暗号化し、認証を行うためのプロトコルです。HTTPS通信の基盤となっています。
- TLSハンドシェイク: クライアントとサーバーがTLS通信を開始する際に、互いの身元を確認し、暗号化パラメータを確立するための一連のプロセスです。このプロセス中にエラーが発生すると、通信は確立されません。
- エラーメッセージのプラットフォーム依存性: オペレーティングシステム(OS)のネットワークスタックの実装や、エラー報告のメカニズムは、OSによって異なる場合があります。これにより、同じ種類のネットワークエラーでも、OSによって異なるエラーメッセージが生成されることがあります。特に、低レベルのネットワークエラーや、接続の切断に関するエラーは、OSの内部的な挙動に強く依存する傾向があります。
- テストの堅牢性: ソフトウェアテストは、コードが期待通りに動作することを確認するために重要です。しかし、テストが特定の環境やOSの挙動に過度に依存していると、異なる環境でテストが失敗する「flaky test」(不安定なテスト)になる可能性があります。堅牢なテストは、本質的なロジックを検証し、環境による差異を吸収するように設計されるべきです。
strings.Contains
: Go言語のstrings
パッケージに含まれる関数で、ある文字列が別の文字列の部分文字列として含まれているかどうかをチェックします。このコミットでは、エラーメッセージが特定の文字列を含んでいるかどうかを検証するために使用されています。
技術的詳細
このコミットの技術的な核心は、テストにおけるエラーメッセージの検証方法の変更です。
元のコードでは、TestClientWithIncorrectTLSServerName
テスト内で、TLSハンドシェイクエラーが発生した際にログに出力されるエラーメッセージが "bad certificate"
という文字列を含んでいることを期待していました。
// 変更前
if !strings.Contains(v, "bad certificate") {
t.Errorf("expected an error log message containing 'bad certificate'; got %q", v)
}
しかし、Windows環境では、クライアントが不正なTLSサーバー名で接続を試み、サーバーが接続を切断した際に、必ずしも "bad certificate"
というメッセージが生成されるわけではありませんでした。Windowsのネットワークスタックは、この種の接続終了を異なる方法で処理し、結果として異なるエラー文字列を生成することがありました。
この問題を解決するため、コミットでは期待するエラーメッセージの文字列を "TLS handshake error"
に変更しました。
// 変更後
if !strings.Contains(v, "TLS handshake error") {
t.Errorf("expected an error log message containing 'TLS handshake error'; got %q", v)
}
この変更の意図は以下の通りです。
- 汎用性の向上:
"TLS handshake error"
という文字列は、TLSハンドシェイク中に発生する一般的なエラーを示すものであり、特定の証明書の問題に限定されません。これにより、Windowsを含む様々なOSで発生しうるTLSハンドシェイク関連のエラーをより広範にカバーできます。 - プラットフォーム非依存性: コミットメッセージにあるように、「UnixとWindowsに共通するプレフィックス」にマッチングさせることで、OS間のエラーメッセージの差異を吸収し、テストのプラットフォーム非依存性を高めています。これは、テストが特定のOSの挙動に過度に依存しないようにするための重要なプラクティスです。
- テストの安定化: この変更により、Windows環境でのテスト失敗が解消され、CI/CDパイプラインにおけるテストの安定性が向上します。不安定なテストは、開発者の生産性を低下させ、実際のバグを見逃す原因となる可能性があります。
この修正は、Go言語のクロスプラットフォーム対応の重要性と、テストの設計においてプラットフォーム固有の挙動を考慮する必要があることを示しています。
コアとなるコードの変更箇所
変更は src/pkg/net/http/client_test.go
ファイルの TestClientWithIncorrectTLSServerName
関数内で行われています。
--- a/src/pkg/net/http/client_test.go
+++ b/src/pkg/net/http/client_test.go
@@ -671,8 +671,8 @@ func TestClientWithIncorrectTLSServerName(t *testing.T) {
}
select {
case v := <-errc:
- if !strings.Contains(v, "bad certificate") {
- t.Errorf("expected an error log message containing 'bad certificate'; got %q", v)
+ if !strings.Contains(v, "TLS handshake error") {
+ t.Errorf("expected an error log message containing 'TLS handshake error'; got %q", v)
}
case <-time.After(5 * time.Second):
t.Errorf("timeout waiting for logged error")
コアとなるコードの解説
このコードスニペットは、TestClientWithIncorrectTLSServerName
テスト関数の一部です。このテストは、クライアントが不正なTLSサーバー名を使用して接続を試みた場合に、サーバーが適切にエラーを処理するかどうかを検証します。
select { ... }
: Goのselect
ステートメントは、複数の通信操作(チャネルからの受信、チャネルへの送信)を待機するために使用されます。ここでは、errc
チャネルからの値の受信、または5秒間のタイムアウトのいずれかを待機しています。case v := <-errc:
:errc
チャネルからエラーメッセージv
を受信した場合の処理です。if !strings.Contains(v, "TLS handshake error") { ... }
: ここが変更された主要なロジックです。- 変更前:
!strings.Contains(v, "bad certificate")
- 受信したエラーメッセージ
v
が"bad certificate"
という文字列を含んでいない場合に、テストを失敗させていました。これは、TLSハンドシェイク中に証明書関連の具体的なエラーが発生することを期待していたためです。
- 受信したエラーメッセージ
- 変更後:
!strings.Contains(v, "TLS handshake error")
- 受信したエラーメッセージ
v
が"TLS handshake error"
という文字列を含んでいない場合に、テストを失敗させます。この変更により、テストはより一般的なTLSハンドシェイクエラーを捕捉するようになり、特定の証明書エラーメッセージに依存しなくなりました。
- 受信したエラーメッセージ
- 変更前:
t.Errorf(...)
: テストが失敗した場合にエラーメッセージを出力し、テストを失敗としてマークします。
この修正は、テストの期待値をより現実的かつプラットフォーム非依存なものに調整することで、テストの信頼性を高めるという、シンプルながらも効果的な変更です。
関連リンク
- Go言語の
net/http
パッケージのドキュメント: https://pkg.go.dev/net/http - Go言語の
strings
パッケージのドキュメント: https://pkg.go.dev/strings - Goのコードレビューシステム (Gerrit): https://go-review.googlesource.com/
参考にした情報源リンク
- コミットメッセージの内容
- Go言語の公式ドキュメント
- Go言語のソースコード (特に
src/pkg/net/http/client_test.go
の変更履歴) - 一般的なTLSおよびネットワークエラーに関する知識
- Goのコードレビューシステムへのリンク: https://golang.org/cl/70010050
- GitHub上のコミットページ: https://github.com/golang/go/commit/a5166a9512215483dae4e2963435cb3803a9f330