[インデックス 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
の挙動が不明確であるため、テストが適切に機能しない可能性が指摘されていました。
このコミットでは、以下の変更が加えられました。
darwin
とwindows
の統合:case "darwin":
の条件に", "windows"
を追加し、darwin
とwindows
の両方で同じテストロジックが実行されるようにしました。- 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でも「死んだアドレス」への接続試行がタイムアウトテストに適しているという判断がなされています。 - 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のネットワーク機能のクロスプラットフォームな互換性と堅牢性が向上しました。
関連リンク
- Go言語
net
パッケージのドキュメント: https://pkg.go.dev/net - Go言語
runtime
パッケージのドキュメント: https://pkg.go.dev/runtime - Go言語のコードレビューシステム (Gerrit): https://go-review.googlesource.com/ (コミットメッセージにある
https://golang.org/cl/5616066
はGerritの変更リストへのリンクです)
参考にした情報源リンク
- TCP
listen
backlog andSOMAXCONN
: https://stackoverflow.com/questions/1008001/what-is-the-meaning-of-somaxconn-in-listen - Windows
listen
backlog: https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-listen - Go
DialTimeout
example: https://gobyexample.com/timeouts - Go
runtime.GOOS
usage: https://gobyexample.com/command-line-arguments (間接的にruntime.GOOS
の概念が示されています) - Go issue tracker (for context on
net
package issues): https://github.com/golang/go/issues