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

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

このコミットは、Go言語の標準ライブラリnetパッケージ内のTestDialTimeoutテストがWindows環境のビルドサーバーで失敗する問題を修正するものです。具体的には、テストが意図しない成功を収めてしまう原因となっていたポート番号の変更と、ダイヤルが失敗したことを明示的に確認するチェックの追加が行われました。

コミット

commit c804efb5de2f73a7ce12b4b09a2947b164c3aa43
Author: Russ Cox <rsc@golang.org>
Date:   Wed Mar 7 00:41:24 2012 -0500

    net: fix TestDialTimeout on windows builder
    
    I don't know what's out there, but something
    is answering to 127.0.71.111:80 on our builder,
    so use a different port.
    
    Also insert a check that the dial fails, which
    would have diagnosed this problem.
    
    Fixes #3016.
    
    R=golang-dev, mikioh.mikioh, r
    CC=golang-dev
    https://golang.org/cl/5754062

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

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

元コミット内容

net: fix TestDialTimeout on windows builder

このコミットは、Windowsビルドサーバー上でTestDialTimeoutテストが失敗する問題を修正します。 問題の原因は、ビルドサーバー上で127.0.71.111:80に対してダイヤルを試みた際に、何らかのサービスが応答してしまい、テストが意図せず成功してしまうことでした。 このため、テストで使用するポートを別のものに変更し、さらにダイヤルが失敗したことを明示的に確認するチェックを追加しました。これにより、将来同様の問題が発生した場合に診断が容易になります。 この修正は、Issue #3016に対応するものです。

変更の背景

TestDialTimeoutは、指定されたタイムアウト時間内に接続が確立できない場合に、DialTimeout関数がエラーを返すことを検証するためのテストです。このテストでは、通常、存在しないIPアドレスやポートに対して接続を試みることで、意図的に接続失敗を発生させます。

しかし、Go言語のWindowsビルドサーバーにおいて、127.0.71.111:80というアドレスとポートの組み合わせに対してDialTimeoutを呼び出した際に、予期せず接続が成功してしまうという問題が発生しました。コミットメッセージによると、「何らかのサービスが応答している」とのことです。これは、ビルド環境のネットワーク設定、仮想マシンの構成、ファイアウォール、あるいはデータセンターのインフラストラクチャが、特定のローカルIPアドレスとポート(特にHTTPの標準ポートである80番)への接続を傍受し、応答を返してしまう状況を示唆しています。

この予期せぬ接続成功は、テストの目的(タイムアウトによる接続失敗の検証)を妨げ、テストが誤ってパスしてしまう原因となっていました。そのため、テストの信頼性を確保し、DialTimeout関数の正しい動作を検証するために、この修正が必要となりました。また、将来的に同様の問題が発生した場合に、その原因を特定しやすくするための診断メカニズムの追加も行われました。

前提知識の解説

  • DialTimeout関数: Go言語のnetパッケージに存在する関数で、指定されたネットワークアドレス(例: tcp, 127.0.0.1:8080)に対して接続を試み、指定されたタイムアウト時間内に接続が確立できない場合にエラーを返します。ネットワーク接続のタイムアウト処理をテストする際などに利用されます。
  • 127.0.0.1 (localhost): ループバックアドレスとして知られ、自身のコンピュータを指します。127.0.0.1から127.255.255.255までの範囲はループバックアドレスとして予約されており、通常は外部ネットワークには到達しません。テスト目的で、存在しないサービスへの接続をシミュレートするためによく使用されます。
  • ポート80: HTTPプロトコルの標準ポートです。Webサーバーが通常このポートでリクエストを待ち受けます。
  • ビルドサーバー (Builder): ソフトウェア開発において、ソースコードをコンパイルし、実行可能なバイナリやパッケージを生成するための専用サーバーです。継続的インテグレーション (CI) システムの一部として機能し、コードの変更がコミットされるたびに自動的にビルドとテストを実行します。ビルドサーバーの環境は、開発者のローカル環境とは異なる場合があり、それが今回のような問題を引き起こすことがあります。
  • TCP/IP: インターネットを含む多くのネットワークで利用される通信プロトコル群です。TCP (Transmission Control Protocol) は信頼性の高いデータ転送を提供し、IP (Internet Protocol) はデータのルーティングを担当します。DialTimeoutはTCP接続の確立を試みます。
  • IIS (Internet Information Services): Microsoftが提供するWebサーバーソフトウェアです。Windows環境でWebサイトをホストする際によく利用されます。コミットメッセージで「IIS web server」に接続された可能性が示唆されているのは、ポート80番がHTTPの標準ポートであり、Windows環境でIISが稼働している場合にそのポートをリッパしている可能性があるためです。

技術的詳細

このコミットの技術的な核心は、TestDialTimeoutテストの堅牢性を高めることにあります。

  1. ポート番号の変更:

    • 元のテストでは、127.0.71.111:80というアドレスとポートの組み合わせを使用していました。ポート80はHTTPの標準ポートであり、多くのシステムでWebサーバー(IISなど)が稼働している可能性があります。
    • Windowsビルドサーバーでこのポート80への接続が予期せず成功してしまったため、テストの意図(接続失敗)が達成されませんでした。
    • 修正では、ポート番号を80から44444に変更しました。44444は一般的に使用されない非特権ポートであり、このポートでサービスが稼働している可能性は非常に低いため、接続が失敗する確率が高まります。これにより、テストが意図した通りにDialTimeoutがエラーを返すことを検証できるようになります。
  2. 明示的な接続失敗チェックの追加:

    • 以前のテストでは、DialTimeoutがエラーを返さない場合に、その原因を特定するのが困難でした。
    • 修正では、DialTimeoutの呼び出し結果としてerr == nil(エラーがない、つまり接続が成功した)の場合に、明示的にエラーを生成するロジックが追加されました。
    • 具体的には、fmt.Errorf("unexpected: connected to %s!", c.RemoteAddr())というエラーメッセージを生成し、接続が成功したことを示すリモートアドレスを含めることで、デバッグ時の情報量を増やしています。
    • さらに、予期せず接続が成功してしまった場合には、確立された接続cc.Close()で即座に閉じ、リソースリークを防いでいます。
    • このチェックの追加により、テストが誤って成功した場合でも、その原因(予期せぬ接続成功)が明確に診断されるようになり、テストの信頼性とデバッグの容易性が向上しました。
  3. Windows環境への適用:

    • 元々darwin(macOS)環境でのみ適用されていた「希望的に死んでいる127/8アドレスに接続する」というロジックが、今回の修正でwindows環境にも適用されるようになりました。これは、macOSと同様にWindowsでも特定のローカルアドレスへの接続が予期せぬ挙動を示す可能性があるためです。

これらの変更により、TestDialTimeoutは、様々な環境下でDialTimeout関数のタイムアウト挙動をより正確かつ確実に検証できるようになりました。

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

--- a/src/pkg/net/dial_test.go
+++ b/src/pkg/net/dial_test.go
@@ -6,6 +6,7 @@ package net
 
  import (
  	"flag"
+	"fmt"
  	"regexp"
  	"runtime"
  	"testing"
@@ -44,13 +45,22 @@ func TestDialTimeout(t *testing.T) {
  			errc <- err
  		}()
  	}
-	case "darwin":
+	case "darwin", "windows":
  		// At least OS X 10.7 seems to accept any number of
  		// connections, ignoring listen's backlog, so resort
  		// to connecting to a hopefully-dead 127/8 address.
  		// Same for windows.
+		//
+		// Use a bogus port (44444) instead of 80, because
+		// on our 386 builder, this Dial succeeds, connecting
+		// to an IIS web server somewhere.  The data center
+		// or VM or firewall must be stealing the TCP connection.
  		go func() {
-\t\t\t_, err := DialTimeout("tcp", "127.0.71.111:80", 200*time.Millisecond)
+\t\t\tc, err := DialTimeout("tcp", "127.0.71.111:44444", 200*time.Millisecond)
+\t\t\tif err == nil {
+\t\t\t\terr = fmt.Errorf("unexpected: connected to %s!", c.RemoteAddr())
+\t\t\t\tc.Close()
+\t\t\t}
  		errc <- err
  		}()
  	default:

コアとなるコードの解説

このコミットによるsrc/pkg/net/dial_test.goへの変更は以下の通りです。

  1. fmtパッケージのインポート追加:

    • import ("fmt")が追加されました。これは、新しく追加されるエラーメッセージ生成のためにfmt.Errorf関数を使用するためです。
  2. case "darwin"からcase "darwin", "windows"への変更:

    • TestDialTimeout関数内のswitch runtime.GOOS文において、以前はdarwin(macOS)環境のみに適用されていたテストロジックが、windows環境にも適用されるようになりました。これにより、Windowsビルドサーバーでの問題に対応します。
  3. ポート番号の変更とコメントの追加:

    • DialTimeoutの呼び出しで指定されるアドレスが、"127.0.71.111:80"から"127.0.71.111:44444"に変更されました。ポート80番がWindowsビルドサーバー上で予期せず応答してしまう問題があったため、使用される可能性が低いポート44444番に変更されました。
    • 新しいコメントが追加され、ポート80番がIISウェブサーバーなどに接続されてしまう可能性があり、データセンター、VM、またはファイアウォールがTCP接続を「盗んでいる」可能性があることが説明されています。これは、問題の背景を明確にするための重要な情報です。
  4. 接続成功時のエラーチェックの追加:

    • DialTimeoutの戻り値である接続オブジェクトcとエラーerrを受け取るようになりました。
    • if err == nilという条件が追加されました。これは、DialTimeoutがエラーを返さずに接続が成功してしまった場合に実行されます。
    • この条件が真の場合、fmt.Errorf("unexpected: connected to %s!", c.RemoteAddr())を使って新しいエラーが生成されます。このエラーメッセージには、予期せず接続されたリモートアドレスが含まれており、デバッグに役立ちます。
    • エラー生成後、c.Close()が呼び出され、予期せず確立された接続が閉じられます。これにより、リソースリークを防ぎます。
    • 最終的に、この生成されたエラーがerrcチャネルに送信され、テストフレームワークによって捕捉されます。

これらの変更により、TestDialTimeoutはWindows環境においても、意図した通りに接続がタイムアウトし、エラーを返すことを確実に検証できるようになりました。また、万が一予期せぬ接続が成功した場合でも、その事実が明確に報告され、問題の診断が容易になります。

関連リンク

  • GitHubコミットページ: https://github.com/golang/go/commit/c804efb5de2f73a7ce12b4b09a2947b164c3aa43
  • コミットメッセージに記載されているGo CL (Change List) リンク https://golang.org/cl/5754062 は、現在の公開されているGoのコードレビューシステムでは見つかりませんでした。これは、古い内部的なCL番号であるか、リンクが変更された可能性があります。
  • コミットメッセージに記載されているIssue #3016 は、現在のGoの公開Issueトラッカーや一般的なWeb検索では、このコミットの内容に直接関連するものは特定できませんでした。これは、当時のGoの内部Issueトラッカーの番号である可能性が高いです。

参考にした情報源リンク

  • Go言語のnetパッケージに関する一般的な知識
  • TCP/IPおよびネットワークプロトコルに関する一般的な知識
  • ソフトウェア開発におけるビルドサーバーおよび継続的インテグレーションの概念
  • Web検索によるgolang.org/cl/5754062およびGo issue 3016の調査結果(ただし、直接的な関連は確認できず)