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

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

このコミットは、Go言語の標準ライブラリnetパッケージにおけるアドレス解決関数ResolveIPAddrResolveUDPAddrResolveTCPAddrの挙動を改善し、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.gosrc/pkg/net/tcpsock.gosrc/pkg/net/udpsock.go内の各Resolve*Addr関数に、net引数が空文字列""である場合の特別な処理を追加したことです。

具体的には、以下の変更が行われました。

  1. ResolveIPAddr (src/pkg/net/iprawsock.go): net引数が空文字列""の場合、内部的にnet = "ip"として扱われるようになりました。これにより、空文字列が指定された場合でも、IPアドレス解決が正しく行われるようになります。また、関数のコメントも修正され、IPv6アドレスの書式に関する記述が削除され、より汎用的な説明になっています。

  2. ResolveTCPAddr (src/pkg/net/tcpsock.go): net引数が空文字列""の場合、内部的にnet = "tcp"として扱われるようになりました。これにより、空文字列が指定された場合でも、TCPアドレス解決が正しく行われるようになります。

  3. ResolveUDPAddr (src/pkg/net/udpsock.go): net引数が空文字列""の場合、内部的にnet = "udp"として扱われるようになりました。これにより、空文字列が指定された場合でも、UDPアドレス解決が正しく行われるようになります。

これらの変更は、Go 1.0の非公式な挙動を明示的にコードに落とし込むことで、将来のGoのバージョンアップにおいてもこの挙動が保証されるようにするためのものです。

また、これらの変更を検証するために、各Resolve*Addr関数に対応するテストファイル(src/pkg/net/ipraw_test.gosrc/pkg/net/tcp_test.gosrc/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に関する情報は見つかりませんでした)