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

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

このコミットは、Go言語の標準ライブラリである net パッケージ内の ip_test.go ファイルに対する変更です。具体的には、net.SplitHostPort 関数のテストケースが追加されています。ip_test.go は、IPアドレスやネットワーク関連のユーティリティ関数の正確性を検証するための単体テストを格納しています。

コミット

このコミットは、net.SplitHostPort 関数がゾーン識別子を含むホスト名を適切に処理するかどうかを確認するための新しいテストケースを追加します。これは、特にIPv6アドレスにおいて、スコープIDやゾーンインデックスを示すために使用されるゾーン識別子(例: fe80::1%eth0%eth0 部分)の扱いに関するものです。このテストケースは、Go 1.0における SplitHostPort の挙動を明確にする目的で追加されました。

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

https://github.com/golang/go/commit/6d622416f1088fef92d44c79af2dfcb385088ffa

元コミット内容

commit 6d622416f1088fef92d44c79af2dfcb385088ffa
Author: Russ Cox <rsc@golang.org>
Date:   Thu Nov 29 15:43:05 2012 -0500

    net: add test case for SplitHostPort with zone
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/6854119

変更の背景

net.SplitHostPort 関数は、host:port 形式のネットワークアドレス文字列をホストとポートに分割するために使用されます。しかし、IPv6アドレスには、リンクローカルアドレスなどでインターフェースのスコープを示すための「ゾーン識別子」(またはスコープID)が含まれることがあります。例えば、fe80::1%eth0 のような形式です。

このコミットが作成された2012年11月時点では、Go言語はまだ比較的新しく、ネットワーク関連のAPIも進化の途上にありました。特に、IPv6のゾーン識別子を含むアドレス文字列のパースは、一般的な host:port のパターンとは異なるため、SplitHostPort がこれをどのように扱うべきか、あるいは誤ってポートの一部として解釈しないか、といった点が重要でした。

このコミットは、SplitHostPort がゾーン識別子を含む文字列をどのように処理するか、特にポート部分に % が含まれる場合に、その挙動がGo 1.0の仕様に沿っていることを確認するためのテストケースを追加することで、関数の堅牢性と正確性を向上させることを目的としています。これは、将来的な変更やリファクタリングの際に、意図しない回帰を防ぐための重要なステップです。

前提知識の解説

1. net.SplitHostPort 関数

Go言語の net パッケージに含まれる SplitHostPort 関数は、"host:port" または "[host]:port" 形式のネットワークアドレス文字列を、ホスト部分とポート部分に分割するユーティリティ関数です。

  • 入力: host:port 形式の文字列。
  • 出力: ホスト文字列とポート文字列の2つの文字列、およびエラー。
  • 挙動の例:
    • "www.example.com:80" -> ("www.example.com", "80")
    • "127.0.0.1:8080" -> ("127.0.0.1", "8080")
    • "[::1]:80" (IPv6ループバックアドレス) -> ("::1", "80")
    • "localhost" (ポートなし) -> エラー
    • ":80" (ホストなし) -> エラー

2. IPv6アドレスとゾーン識別子 (Zone Identifier / Scope ID)

IPv6アドレスには、特定の種類のIPアドレス(特にリンクローカルアドレス fe80::/10)において、そのアドレスがどのネットワークインターフェースに属するかを示すための「ゾーン識別子」または「スコープID」が付加されることがあります。これは、同じリンクローカルアドレスが複数のインターフェースに存在する場合に、どちらのインターフェースを指すのかを明確にするために使用されます。

  • 形式: IPv6アドレス%ゾーン識別子
  • : fe80::1%eth0 (eth0 はインターフェース名)
  • 重要性: ゾーン識別子は、アドレスのルーティングやソケットのバインディングにおいて重要な意味を持ちます。ネットワークプログラミングにおいて、これらのアドレスを正しくパースし、ホスト名の一部として扱うか、ポートの一部として誤解釈しないかが重要になります。

3. Go 1.0の挙動

Go 1.0はGo言語の最初の安定版リリースであり、その後のGo言語の進化の基礎となりました。このコミットで言及されている「Go 1.0 behavior」は、SplitHostPort 関数がGo 1.0の時点でどのように動作していたか、特にゾーン識別子のような特殊なケースをどのように扱っていたかを示しています。これは、後方互換性を維持しつつ、将来のバージョンで挙動を変更する際の基準点となります。

技術的詳細

net.SplitHostPort 関数は、ネットワークアドレス文字列をパースする際に、コロン : を区切り文字としてホストとポートを分離します。しかし、IPv6アドレス自体がコロンを含むため、IPv6アドレスを角括弧 [] で囲むことで、アドレス全体をホスト部分として識別するルールがあります。

問題となるのは、ゾーン識別子 % が含まれる場合です。例えば、google.com:https%foo のような文字列が与えられた場合、SplitHostPort%foo をポートの一部として認識すべきか、それともホスト名の一部として認識すべきか、という判断が必要になります。

このコミットで追加されたテストケースは、"google.com", "https%foo", "google.com:https%foo" というエントリです。これは、SplitHostPortgoogle.com:https%foo という文字列を受け取った際に、ホストを google.com、ポートを https%foo と正しく分割することを期待しています。

この挙動は、ゾーン識別子がポート番号の一部として扱われるという、Go 1.0時点での SplitHostPort の設計上の決定を反映しています。これは、一般的なネットワークプロトコルにおいてポート番号に % が含まれることは稀であるため、このような文字列が与えられた場合に、SplitHostPort がエラーを返すのではなく、そのままポートとして解釈するという、ある種の「寛容な」パース挙動を示しています。

このテストケースの追加は、以下の点を保証します。

  1. 意図された挙動の文書化: SplitHostPort% を含むポート文字列をどのように扱うかという、Go 1.0の挙動を明示的にテストとして記録します。
  2. 回帰テスト: 将来的に SplitHostPort の実装が変更された場合でも、この特定のケースでの挙動が維持されることを保証します。
  3. エッジケースの考慮: 通常の host:port 形式ではない、特殊な文字を含む文字列に対する関数の堅牢性を高めます。

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

変更は src/pkg/net/ip_test.go ファイルの splitjointests という変数に対して行われています。これは、SplitHostPort 関数と JoinHostPort 関数の両方をテストするための構造体のスライスです。

--- a/src/pkg/net/ip_test.go
+++ b/src/pkg/net/ip_test.go
@@ -268,6 +268,7 @@ var splitjointests = []struct {
 	{"www.google.com", "80", "www.google.com:80"},
 	{"127.0.0.1", "1234", "127.0.0.1:1234"},
 	{"::1", "80", "[::1]:80"},
+\t{"google.com", "https%foo", "google.com:https%foo"}, // Go 1.0 behavior
 }
 
 func TestSplitHostPort(t *testing.T) {

追加された行は以下の通りです。

{"google.com", "https%foo", "google.com:https%foo"}, // Go 1.0 behavior

コアとなるコードの解説

この追加されたテストケースは、splitjointests スライスの一部であり、以下の3つのフィールドを持つ匿名構造体として定義されています。

  1. 期待されるホスト (Expected Host): "google.com"
  2. 期待されるポート (Expected Port): "https%foo"
  3. 入力文字列 (Input String): "google.com:https%foo"

このテストケースは、TestSplitHostPort 関数内で以下のように使用されます(TestSplitHostPort 関数の具体的な実装はコミットログにはありませんが、一般的なテストパターンから推測できます)。

  • SplitHostPort("google.com:https%foo") を呼び出す。
  • 返されたホストが "google.com" と一致するか検証する。
  • 返されたポートが "https%foo" と一致するか検証する。

コメント // Go 1.0 behavior は、この特定の挙動がGo 1.0のリリース時点での SplitHostPort の設計意図であることを示しています。これは、SplitHostPort がポート部分に % が含まれていても、それを有効なポート文字列として扱うことを意味します。これは、ゾーン識別子がホスト名の一部としてではなく、ポートの一部として解釈される可能性があるという、SplitHostPort のパースロジックの特定の側面を強調しています。

このテストケースは、SplitHostPort がIPv6のゾーン識別子を適切に処理するかどうかを直接テストするものではありませんが、ポート部分に % が含まれる文字列に対する関数の挙動を明確に定義し、その挙動が将来の変更によって損なわれないことを保証します。

関連リンク

参考にした情報源リンク

  • Go言語 net パッケージのドキュメント (現在のバージョン): https://pkg.go.dev/net
  • IPv6 Zone Identifiers (RFC 4007): https://datatracker.ietf.org/doc/html/rfc4007
  • Go 1.0 Release Notes (アーカイブ): https://go.dev/doc/go1 (当時の net パッケージの挙動に関する詳細な記述は含まれていない可能性がありますが、Go 1.0の全体像を理解するのに役立ちます。)
  • net.SplitHostPort の挙動に関する議論 (Goコミュニティのフォーラムやメーリングリストのアーカイブなど、当時の議論を検索することで見つかる可能性があります。)