[インデックス 14537] ファイルの概要
このコミットは、Go言語の標準ライブラリnet
パッケージにおけるアドレス解決関数ResolveIPAddr
、ResolveUDPAddr
、ResolveTCPAddr
の挙動を改善し、net
引数に空文字列""
が指定された場合に、それぞれ適切なネットワークタイプ("ip"
、"udp"
、"tcp"
)として解釈されるように変更を加えるものです。これにより、Go 1.0の非公式な挙動が明示的にサポートされ、関連するコメントの修正も行われています。
コミット
commit e3e885bb481731370573e99ac20fd93ed652ae9a
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date: Sat Dec 1 14:49:54 2012 +0900
net: allow "" as a hint wildcard to Resolve{IP,UDP,TCP}Addr
Also fixes comments on ResolveIPAddr.
Fixes #4476.
R=bradfitz, golang-dev
CC=golang-dev
https://golang.org/cl/6854129
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e3e885bb481731370573e99ac20fd93ed652ae9a
元コミット内容
net: allow "" as a hint wildcard to Resolve{IP,UDP,TCP}Addr
Also fixes comments on ResolveIPAddr.
Fixes #4476.
R=bradfitz, golang-dev
CC=golang-dev
https://golang.org/cl/6854129
変更の背景
Go言語のnet
パッケージには、IPアドレスやTCP/UDPアドレスを解決するための関数群(ResolveIPAddr
, ResolveUDPAddr
, ResolveTCPAddr
など)が存在します。これらの関数は通常、第一引数にネットワークタイプ(例: "ip"
, "ip4"
, "ip6"
, "tcp"
, "tcp4"
, "tcp6"
, "udp"
, "udp4"
, "udp6"
)を指定することを想定しています。
しかし、Go 1.0の時代には、これらの関数にネットワークタイプとして空文字列""
を渡した場合に、暗黙的にデフォルトのネットワークタイプ("ip"
、"udp"
、"tcp"
)として扱われるという、ドキュメント化されていない挙動が存在しました。この挙動に依存している既存のコードが存在する可能性があり、将来的な互換性を考慮すると、この非公式な挙動を明示的にサポートし、安定させることが望ましいと判断されました。
このコミットは、この「Go 1.0の挙動」を正式にサポートし、空文字列""
を「ヒントとしてのワイルドカード」として扱うことで、既存のコードが意図せず壊れることを防ぎ、同時にコードの意図を明確にすることを目的としています。また、ResolveIPAddr
関数のコメントが、この新しい挙動に合わせて修正されています。
コミットメッセージに記載されているFixes #4476
は、この変更が特定の課題を解決することを示唆していますが、現在のGoのIssueトラッカーでは該当するIssueが見つかりませんでした。これは、GoのIssueトラッカーが時間とともに変更されたか、あるいは内部的なIssue番号であった可能性が考えられます。同様に、https://golang.org/cl/6854129
のリンクも、現在のGoのChange Listではこのコミットの内容とは異なるものを示しており、古いCLシステムへの参照である可能性があります。
前提知識の解説
Go言語のnet
パッケージ
Go言語の標準ライブラリnet
パッケージは、ネットワークI/O機能を提供します。これには、TCP/UDPソケットの操作、IPアドレスの解決、DNSルックアップなどが含まれます。ネットワークプログラミングを行う上で中心的な役割を果たすパッケージです。
アドレス解決関数
net
パッケージには、ホスト名やサービス名からネットワークアドレスを解決するための関数がいくつか提供されています。
func ResolveIPAddr(net, addr string) (*IPAddr, error)
: 指定されたネットワークタイプ("ip"
,"ip4"
,"ip6"
)とアドレス文字列に基づいて、IPアドレスを解決します。func ResolveTCPAddr(net, addr string) (*TCPAddr, error)
: 指定されたネットワークタイプ("tcp"
,"tcp4"
,"tcp6"
)とアドレス文字列に基づいて、TCPアドレスを解決します。アドレス文字列は通常、"host:port"
の形式です。func ResolveUDPAddr(net, addr string) (*UDPAddr, error)
: 指定されたネットワークタイプ("udp"
,"udp4"
,"udp6"
)とアドレス文字列に基づいて、UDPアドレスを解決します。アドレス文字列は通常、"host:port"
の形式です。
これらの関数の第一引数net
は、使用するネットワークプロトコルファミリーを指定します。例えば、"tcp4"
はIPv4 TCP、"udp6"
はIPv6 UDPを意味します。
ネットワークタイプとしての空文字列""
通常、net
引数には具体的なネットワークタイプを指定しますが、このコミット以前のGo 1.0では、空文字列""
が指定された場合に、Goランタイムが内部的に適切なデフォルトタイプ(IPアドレス解決なら"ip"
、TCPなら"tcp"
、UDPなら"udp"
)を推測して処理していました。この挙動はドキュメントには明記されていませんでしたが、一部のアプリケーションで利用されていた可能性があります。
このコミットは、この「暗黙の挙動」を明示的な「ヒントとしてのワイルドカード」として定義し、コード内でその解釈を明確にすることで、将来的な互換性とコードの可読性を向上させています。
技術的詳細
このコミットの主要な変更点は、src/pkg/net/iprawsock.go
、src/pkg/net/tcpsock.go
、src/pkg/net/udpsock.go
内の各Resolve*Addr
関数に、net
引数が空文字列""
である場合の特別な処理を追加したことです。
具体的には、以下の変更が行われました。
-
ResolveIPAddr
(src/pkg/net/iprawsock.go
):net
引数が空文字列""
の場合、内部的にnet = "ip"
として扱われるようになりました。これにより、空文字列が指定された場合でも、IPアドレス解決が正しく行われるようになります。また、関数のコメントも修正され、IPv6アドレスの書式に関する記述が削除され、より汎用的な説明になっています。 -
ResolveTCPAddr
(src/pkg/net/tcpsock.go
):net
引数が空文字列""
の場合、内部的にnet = "tcp"
として扱われるようになりました。これにより、空文字列が指定された場合でも、TCPアドレス解決が正しく行われるようになります。 -
ResolveUDPAddr
(src/pkg/net/udpsock.go
):net
引数が空文字列""
の場合、内部的にnet = "udp"
として扱われるようになりました。これにより、空文字列が指定された場合でも、UDPアドレス解決が正しく行われるようになります。
これらの変更は、Go 1.0の非公式な挙動を明示的にコードに落とし込むことで、将来のGoのバージョンアップにおいてもこの挙動が保証されるようにするためのものです。
また、これらの変更を検証するために、各Resolve*Addr
関数に対応するテストファイル(src/pkg/net/ipraw_test.go
、src/pkg/net/tcp_test.go
、src/pkg/net/udp_test.go
)に、空文字列""
をnet
引数として使用する新しいテストケースが追加されています。これらのテストケースは、Go 1.0の挙動を模倣し、変更が期待通りに機能することを確認します。
コアとなるコードの変更箇所
src/pkg/net/iprawsock.go
--- a/src/pkg/net/iprawsock.go
+++ b/src/pkg/net/iprawsock.go
@@ -24,9 +24,11 @@ func (a *IPAddr) String() string {
// ResolveIPAddr parses addr as an IP address and resolves domain
// names to numeric addresses on the network net, which must be
-// "ip", "ip4" or "ip6". A literal IPv6 host address must be
-// enclosed in square brackets, as in "[::]".
+// "ip", "ip4" or "ip6".
func ResolveIPAddr(net, addr string) (*IPAddr, error) {
+ if net == "" { // a hint wildcard for Go 1.0 undocumented behavior
+ net = "ip"
+ }
afnet, _, err := parseDialNetwork(net)
if err != nil {
return nil, err
src/pkg/net/tcpsock.go
--- a/src/pkg/net/tcpsock.go
+++ b/src/pkg/net/tcpsock.go
@@ -31,6 +31,8 @@ func (a *TCPAddr) String() string {
func ResolveTCPAddr(net, addr string) (*TCPAddr, error) {
switch net {
case "tcp", "tcp4", "tcp6":
+ case "": // a hint wildcard for Go 1.0 undocumented behavior
+ net = "tcp"
default:
return nil, UnknownNetworkError(net)
}
src/pkg/net/udpsock.go
--- a/src/pkg/net/udpsock.go
+++ b/src/pkg/net/udpsock.go
@@ -35,6 +35,8 @@ func (a *UDPAddr) String() string {
func ResolveUDPAddr(net, addr string) (*UDPAddr, error) {
switch net {
case "udp", "udp4", "udp6":
+ case "": // a hint wildcard for Go 1.0 undocumented behavior
+ net = "udp"
default:
return nil, UnknownNetworkError(net)
}
テストファイルの追加箇所
src/pkg/net/ipraw_test.go
--- a/src/pkg/net/ipraw_test.go
+++ b/src/pkg/net/ipraw_test.go
@@ -29,6 +29,9 @@ var resolveIPAddrTests = []struct {
{"ip6", "::1", &IPAddr{IP: ParseIP("::1")}, nil},
{"ip6:icmp", "::1", &IPAddr{IP: ParseIP("::1")}, nil},
+ {"", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil}, // Go 1.0 behavior
+ {"", "::1", &IPAddr{IP: ParseIP("::1")}, nil}, // Go 1.0 behavior
+
{"l2tp", "127.0.0.1", nil, UnknownNetworkError("l2tp")},
{"l2tp:gre", "127.0.0.1", nil, UnknownNetworkError("l2tp:gre")},
{"tcp", "1.2.3.4:123", nil, UnknownNetworkError("tcp")},
src/pkg/net/tcp_test.go
--- a/src/pkg/net/tcp_test.go
+++ b/src/pkg/net/tcp_test.go
@@ -130,6 +130,9 @@ var resolveTCPAddrTests = []struct {
{"tcp", "[::1]:1", &TCPAddr{IP: ParseIP("::1"), Port: 1}, nil},
{"tcp6", "[::1]:65534", &TCPAddr{IP: ParseIP("::1"), Port: 65534}, nil},
+ {"", "127.0.0.1:0", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior
+ {"", "[::1]:0", &TCPAddr{IP: ParseIP("::1"), Port: 0}, nil}, // Go 1.0 behavior
+
{"http", "127.0.0.1:0", nil, UnknownNetworkError("http")},
}
src/pkg/net/udp_test.go
--- a/src/pkg/net/udp_test.go
+++ b/src/pkg/net/udp_test.go
@@ -22,6 +22,9 @@ var resolveUDPAddrTests = []struct {
{"udp", "[::1]:1", &UDPAddr{IP: ParseIP("::1"), Port: 1}, nil},
{"udp6", "[::1]:65534", &UDPAddr{IP: ParseIP("::1"), Port: 65534}, nil},
+ {"", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior
+ {"", "[::1]:0", &UDPAddr{IP: ParseIP("::1"), Port: 0}, nil}, // Go 1.0 behavior
+
{"sip", "127.0.0.1:0", nil, UnknownNetworkError("sip")},
}
コアとなるコードの解説
各Resolve*Addr
関数に追加された以下のコードブロックが、この変更の核心です。
if net == "" { // a hint wildcard for Go 1.0 undocumented behavior
net = "ip" // or "tcp", "udp" depending on the function
}
このif
文は、net
引数が空文字列""
であるかどうかをチェックします。もし空文字列であれば、コメントにもあるように「Go 1.0のドキュメント化されていない挙動に対するヒントとしてのワイルドカード」として解釈し、内部的にnet
変数を適切なデフォルト値(ResolveIPAddr
では"ip"
、ResolveTCPAddr
では"tcp"
、ResolveUDPAddr
では"udp"
)に上書きします。
これにより、関数は後続の処理でこのデフォルト値を使用し、空文字列が指定された場合でもエラーを発生させることなく、期待されるアドレス解決を実行できるようになります。この変更は、既存のGo 1.0コードとの後方互換性を維持しつつ、空文字列の扱いを明示的にすることで、コードの堅牢性と可読性を向上させています。
また、ResolveIPAddr
のコメント修正は、以前のコメントがIPv6アドレスの書式について具体的に言及していた部分を削除し、より一般的な説明にすることで、この関数の柔軟な利用を妨げないようにしています。
テストケースの追加は、この新しい挙動が正しく実装されていることを確認するためのものです。特に、// Go 1.0 behavior
というコメントは、このテストがGo 1.0時代の非公式な挙動を意図的に検証していることを示しています。
関連リンク
参考にした情報源リンク
- Go言語の
net
パッケージに関する公式ドキュメント(コミット当時のバージョンに準ずる) - Go言語のIssueトラッカー(
Fixes #4476
に関する情報は見つかりませんでした) - Go言語のChange List(
https://golang.org/cl/6854129
に関する情報は見つかりませんでした)