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

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

このコミットは、Go言語の標準ライブラリである net パッケージ内の TestDialTimeout テストがWindows環境でも実行されるように修正するものです。具体的には、dial_test.go ファイルにおいて、TestDialTimeout のOSごとの条件分岐に windows を追加し、Windows上でのテストの挙動を調整しています。

コミット

commit 1f133e2b8ecfc2d26ed480eba266f1b86647dc5c
Author: Alex Brainman <alex.brainman@gmail.com>
Date:   Tue Feb 7 12:05:59 2012 +1100

    net: run TestDialTimeout on windows
    
    R=golang-dev, rsc
    CC=bradfitz, golang-dev, mikioh.mikioh
    https://golang.org/cl/5616066

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

https://github.com/golang/go/commit/1f133e2b8ecfc2d26ed480eba266f1b86647dc5c

元コミット内容

net: run TestDialTimeout on windows

R=golang-dev, rsc
CC=bradfitz, golang-dev, mikioh.mikioh
https://golang.org/cl/5616066

変更の背景

この変更の背景には、Go言語のネットワークパッケージにおける DialTimeout 関数のテストカバレッジをWindows環境にも拡大するという目的があります。

TestDialTimeout は、指定されたタイムアウト時間内にネットワーク接続が確立できるか、またはタイムアウトによって接続が失敗するかを検証するためのテストです。以前のバージョンでは、このテストはWindows環境ではスキップされていました。コメントアウトされた部分 (// TODO(bradfitz): this probably doesn't work on // Windows? SOMAXCONN is huge there. I'm not sure how // listen works there.) からわかるように、Windowsにおける listen システムコール(特に SOMAXCONN の挙動)や、多数の接続を同時に処理する際の挙動について懸念があり、テストが適切に機能しない可能性が指摘されていました。

SOMAXCONN は、TCP/IPソケットプログラミングにおいて、listen システムコールが受け入れることができる保留中の接続の最大数を定義するシステムレベルの定数です。この値はOSによって異なり、Windowsでは他のUnix系OSと比較して非常に大きな値が設定されていることが一般的です。これにより、テストが意図する「接続が拒否される」状況を再現するのが難しいという問題がありました。

このコミットは、Windows環境でも TestDialTimeout を実行できるようにすることで、Goのネットワーク機能のクロスプラットフォームな堅牢性を向上させることを目指しています。特に、OS X (Darwin) と同様に、Windowsでも「おそらく死んでいるであろう」IPアドレス (127.0.71.111:80) への接続を試みることで、タイムアウトによる接続失敗のシナリオをテストしています。これは、特定のポートでリッスンしているサーバーが存在しない場合に、DialTimeout が正しくタイムアウトエラーを返すことを確認するための一般的な手法です。

前提知識の解説

1. net パッケージと DialTimeout 関数

Go言語の net パッケージは、ネットワークI/Oプリミティブを提供します。TCP/IP、UDP、IP、Unixドメインソケットなどのネットワークプロトコルを扱うための機能が含まれています。

DialTimeout 関数は、net パッケージで提供される重要な関数の一つです。これは、指定されたネットワークアドレスへの接続を試み、指定されたタイムアウト時間内に接続が確立できない場合にエラーを返す機能を提供します。

func DialTimeout(network, address string, timeout time.Duration) (Conn, error)
  • network: "tcp", "udp", "unix" などのネットワークプロトコルを指定します。
  • address: 接続先のアドレス(例: "localhost:8080", "192.168.1.1:22")を指定します。
  • timeout: 接続試行の最大時間を time.Duration 型で指定します。

この関数は、ネットワーク接続の信頼性を高めるために非常に重要です。特に、応答しないサーバーへの接続試行によってアプリケーションがハングアップするのを防ぐことができます。

2. listen システムコールと SOMAXCONN

listen システムコールは、サーバーアプリケーションが特定のポートでクライアントからの接続要求を待ち受けるために使用されます。このシステムコールには、バックログキューのサイズを指定する引数があります。バックログキューとは、サーバーが accept システムコールで処理する準備ができるまでの間、保留中の接続要求を保持するキューのことです。

SOMAXCONN は、このバックログキューの最大サイズを定義するシステムレベルの定数です。OSごとにそのデフォルト値や挙動が異なります。

  • Unix/Linux系OS: SOMAXCONN は通常、比較的低い値(例: 128)に設定されています。これは、サーバーが同時に処理できる接続数を制限し、過負荷を防ぐのに役立ちます。
  • Windows: Windowsでは、SOMAXCONN の値が非常に大きい(例えば、数万)ことが一般的です。これは、Windowsが設計上、より多くの同時接続をデフォルトで受け入れるように最適化されているためです。

SOMAXCONN の値が大きいと、テストで「接続が拒否される」シナリオを再現するのが難しくなります。なぜなら、テストが意図的に多数の接続を試みても、OSがそれらをバックログキューに受け入れてしまい、すぐに接続拒否エラーが発生しない可能性があるためです。

3. runtime.GOOS

Go言語の runtime パッケージは、Goランタイムとの対話のための機能を提供します。runtime.GOOS は、プログラムがコンパイルされ、実行されているオペレーティングシステムの名前を表す文字列定数です(例: "linux", "windows", "darwin")。

この定数を使用することで、Goプログラムは実行環境のOSに基づいて異なるコードパスを実行することができます。これは、OS固有の挙動やAPIの違いを吸収し、クロスプラットフォームなアプリケーションを開発する上で非常に有用です。

4. ネットワークテストにおける「死んだアドレス」の使用

ネットワークテストにおいて、意図的に存在しないIPアドレスやポート(「死んだアドレス」や「死んだポート」と呼ばれる)に接続を試みることは一般的な手法です。これは、以下のようなシナリオをテストするために使用されます。

  • タイムアウトの検証: 接続先が存在しないため、接続試行は必ずタイムアウトします。これにより、DialTimeout のようなタイムアウト機能が正しく動作するかを確認できます。
  • エラーハンドリングの検証: 接続が失敗した場合に、アプリケーションが適切にエラーを捕捉し、処理できるかを確認します。

このコミットでは、127.0.71.111:80 というIPアドレスとポートの組み合わせを使用しています。127.0.0.0/8 はループバックアドレスの範囲ですが、127.0.71.111 は通常使用されないアドレスであり、ポート80も通常はWebサーバーが使用しますが、このテストでは意図的にリッスンしていないことを前提としています。

技術的詳細

このコミットの技術的な核心は、Goの net パッケージにおける DialTimeout 関数のテストロジックを、Windows環境の特性に合わせて調整した点にあります。

元の TestDialTimeout 関数では、OSごとに異なるテストシナリオが考慮されていました。

  • darwin (macOS): macOS 10.7以降では、listen のバックログ設定を無視して任意の数の接続を受け入れる傾向があるため、意図的に存在しない 127.0.71.111:80 への接続を試みることでタイムアウトを発生させていました。
  • default (その他): Windowsを含むその他のOSでは、テストがスキップされていました。特にWindowsについては、SOMAXCONN の値が非常に大きく、listen の挙動が不明確であるため、テストが適切に機能しない可能性が指摘されていました。

このコミットでは、以下の変更が加えられました。

  1. darwinwindows の統合: case "darwin": の条件に ", "windows" を追加し、darwinwindows の両方で同じテストロジックが実行されるようにしました。
  2. 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. というコメントを追加し、WindowsでもmacOSと同様に listen のバックログが無視される傾向があることを示唆しています。これにより、Windowsでも「死んだアドレス」への接続試行がタイムアウトテストに適しているという判断がなされています。
  3. TODOコメントの簡略化: 以前のWindowsに関する懸念を示す詳細なTODOコメント (// TODO(bradfitz): this probably doesn't work on // Windows? SOMAXCONN is huge there. I'm not sure how // listen works there.) を簡略化し、一般的なTODOコメント (// TODO(bradfitz):) に変更しました。これは、Windowsでのテスト実行が可能になったため、以前の懸念が解消されたことを示しています。

この変更により、Windows環境でも DialTimeout 関数が正しくタイムアウトエラーを返すかどうかが検証されるようになりました。これは、Goのネットワークスタックが異なるOS環境下でも一貫した挙動を示すことを保証する上で重要なステップです。特に、WindowsのネットワークスタックはUnix系OSとは異なる特性を持つことが多いため、このような明示的なテストの追加は、Goアプリケーションのクロスプラットフォーム互換性を高める上で不可欠です。

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

--- a/src/pkg/net/dial_test.go
+++ b/src/pkg/net/dial_test.go
@@ -43,18 +43,17 @@ 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.
 		go func() {
 			_, err := DialTimeout("tcp", "127.0.71.111:80", 200*time.Millisecond)
 			errc <- err
 		}()
 	default:
-		// TODO(bradfitz): this probably doesn't work on
-		// Windows? SOMAXCONN is huge there.  I'm not sure how
-		// listen works there.
+		// TODO(bradfitz):
 		// OpenBSD may have a reject route to 10/8.
 		// FreeBSD likely works, but is untested.
 		t.Logf("skipping test on %q; untested.", runtime.GOOS)

コアとなるコードの解説

このコミットのコアとなる変更は、src/pkg/net/dial_test.go ファイル内の TestDialTimeout 関数における switch runtime.GOOS ステートメントの変更です。

元のコードでは、runtime.GOOS の値が "darwin" の場合に特定のテストロジックが実行され、それ以外のOS(default ケース)ではテストがスキップされていました。

変更後、case "darwin": の行が case "darwin", "windows": に修正されました。これにより、runtime.GOOS"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.
		go func() {
			_, err := DialTimeout("tcp", "127.0.71.111:80", 200*time.Millisecond)
			errc <- err
		}()

このロジックは、DialTimeout 関数を使用して tcp プロトコルで 127.0.71.111:80 というアドレスに接続を試みます。タイムアウトは 200*time.Millisecond (200ミリ秒) に設定されています。127.0.71.111:80 は意図的に存在しない(またはリッスンしていない)アドレスであるため、この接続試行はタイムアウトし、エラーが errc チャネルに送信されることが期待されます。

追加されたコメント // Same for windows. は、Windows環境でもmacOSと同様に、listen のバックログが無視される傾向があるため、この「死んだアドレス」への接続試行がタイムアウトテストに適していることを示しています。

また、default ケースのTODOコメントも簡略化されました。これは、Windowsに関する以前の懸念が解消され、テストが実行可能になったためです。

この変更により、Windows環境でも DialTimeout のタイムアウト挙動がテストされるようになり、Goのネットワーク機能のクロスプラットフォームな互換性と堅牢性が向上しました。

関連リンク

参考にした情報源リンク