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

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

このコミットで変更されたファイルは src/lib/http/triv.go です。このファイルは、Go言語の初期の標準ライブラリの一部である net/http パッケージの、おそらくはテストまたは非常にシンプルなHTTPサーバーの例として機能していたと考えられます。ファイル名から推測すると、「triv」は「trivial(些細な、簡単な)」を意味し、HTTPハンドラの基本的な使用方法を示すためのものだった可能性が高いです。具体的には、/counter/go//flags//args//go/hello/chan といったパスに対するHTTPハンドラが定義されています。

コミット

  • コミットハッシュ: 6950491b4f0475f488a3a47b8d7771131cd2e8e7
  • 作者: Rob Pike r@golang.org
  • コミット日時: 2009年2月17日 火曜日 19:59:23 -0800
  • コミットメッセージ: drop trailing slashes - missed comment from last review

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

https://github.com/golang/go/commit/6950491b4f0475f488a3a47b8d7771131cd2e8e7

元コミット内容

drop trailing slashes - missed comment from last review

TBR=rsc
OCL=25135
CL=25135

変更の背景

このコミットの背景には、Go言語の初期のHTTPルーティングにおけるパスマッチングの挙動に関する調整があったと考えられます。コミットメッセージにある「missed comment from last review(前回のレビューで見落とされたコメント)」という記述から、以前のコードレビューで指摘されたにもかかわらず修正されていなかった点が、今回改めて修正されたことが示唆されます。

具体的には、http.Handle 関数に登録するパスの末尾のスラッシュ(/)の有無が、ルーティングの挙動に影響を与える可能性があり、その一貫性や意図しないマッチングを防ぐための修正であると推測されます。Goの net/http パッケージにおけるパスのマッチングルールは、特にディレクトリのようなパス(例: /foo/)とファイルのようなパス(例: /foo)を区別する際に重要になります。この変更は、特定のパスに対するハンドラ登録において、末尾のスラッシュを削除することで、より厳密な、あるいは意図した通りのパスマッチングを実現しようとしたものと考えられます。

前提知識の解説

1. HTTPルーティングとパスマッチング

HTTPサーバーは、クライアントからのリクエストURLのパスに基づいて、適切な処理を行うハンドラを呼び出します。この「パスに基づいてハンドラを決定する」プロセスをルーティングと呼びます。ルーティングにおいて、URLパスの末尾のスラッシュの有無は重要な意味を持つことがあります。

  • 末尾スラッシュの有無:
    • /foo のようなパスは、通常、特定の「リソース」や「ファイル」を指すことが多いです。
    • /foo/ のようなパスは、通常、特定の「ディレクトリ」や「コレクション」を指すことが多いです。Webサーバーによっては、/foo/ へのリクエストに対して、自動的に /foo/index.html などのデフォルトファイルを返すことがあります。

Goの net/http パッケージでは、http.Handlehttp.HandleFunc を用いてパスとハンドラを関連付けます。この際、登録するパスのパターンと、実際のリクエストパスのマッチングルールが重要になります。特に、GoのHTTPルーターは、登録されたパスが末尾にスラッシュを持つ場合と持たない場合で、異なるマッチング挙動を示すことがあります。

2. Go言語の net/http パッケージ

Goの標準ライブラリである net/http パッケージは、HTTPクライアントおよびサーバーを実装するための強力な機能を提供します。

  • http.Handle(pattern string, handler Handler): 指定された pattern に対応するHTTPリクエストを処理するために、handler を登録します。handlerhttp.Handler インターフェースを満たす必要があります。
  • http.HandleFunc(pattern string, handler func(ResponseWriter, *Request)): http.Handle の糖衣構文(シンタックスシュガー)で、関数をハンドラとして登録する際に便利です。内部的には、与えられた関数を http.HandlerFunc 型にキャストして http.Handle を呼び出します。
  • パスのマッチングルール: Goの net/http パッケージのデフォルトのServeMux(マルチプレクサ)は、以下のようなパスマッチングルールを持っています。
    • 完全一致: /foo/foo にのみマッチします。
    • 末尾スラッシュ付きのパス: /foo/ のように末尾にスラッシュがあるパスは、/foo/bar/foo/baz/qux のように、そのプレフィックスに続くすべてのパスにマッチします。また、/foo へのリクエストがあった場合、自動的に /foo/ へリダイレクトされることがあります(これは、ディレクトリへのアクセスと見なされるため)。
    • 末尾スラッシュなしのパス: /foo のように末尾にスラッシュがないパスは、完全一致でのみマッチします。ただし、より長いプレフィックスマッチング(例: /foo/bar/foo/ にマッチする)が存在しない場合に限ります。

このコミットは、これらのマッチングルールを考慮し、意図しないプレフィックスマッチングやリダイレクトを防ぐために、特定のパスから末尾スラッシュを削除したと考えられます。

技術的詳細

このコミットの技術的詳細は、Goの net/http パッケージにおけるパスのマッチング挙動、特にデフォルトの ServeMux の動作に深く関連しています。

Goの net/http パッケージの ServeMux は、登録されたパターンと受信したリクエストのURLパスを比較して、最適なハンドラを選択します。この選択ロジックにおいて、末尾のスラッシュの有無は重要な要素です。

  1. プレフィックスマッチング: http.Handle("/prefix/", handler) のように末尾にスラッシュがあるパターンを登録すると、そのパターンは /prefix/ で始まるすべてのパス(例: /prefix/foo, /prefix/bar/baz)にマッチします。これは、ファイルシステムにおけるディレクトリのような振る舞いを模倣しています。

  2. 完全一致マッチング: http.Handle("/exact", handler) のように末尾にスラッシュがないパターンを登録すると、そのパターンは /exact というパスにのみ完全に一致する場合にマッチします。

  3. リダイレクト挙動: もし /foo/ というパターンでハンドラが登録されており、クライアントが /foo というパスでリクエストを送信した場合、Goのデフォルトの ServeMux は、通常、クライアントを /foo/ へとリダイレクトします(HTTPステータスコード301 Moved Permanently)。これは、/foo がディレクトリを指す /foo/ の省略形であると解釈されるためです。

このコミットでは、http.Handle("/flags/", ...)http.Handle("/args/", ...) をそれぞれ http.Handle("/flags", ...)http.Handle("/args", ...) に変更しています。この変更の意図は以下の通りです。

  • 厳密なマッチング: 末尾のスラッシュを削除することで、/flags/args というパスに対してのみハンドラが呼び出されるように、マッチングをより厳密にしています。これにより、例えば /flags/some_param のようなパスが意図せず FlagServer にルーティングされることを防ぎます。
  • リダイレクトの回避: /flags/args へのリクエストが、不必要に /flags//args/ へリダイレクトされることを回避します。これは、これらのパスが特定の「リソース」や「コマンド」を指しており、ディレクトリのような振る舞いを期待していない場合に特に重要です。
  • 一貫性の確保: 他のハンドラ登録(例: /counter, /go/hello)が末尾スラッシュなしで登録されているため、これらも末尾スラッシュなしに統一することで、ルーティングの挙動に一貫性を持たせることができます。

この修正は、GoのHTTPサーバーが提供するルーティングのセマンティクスをより明確にし、開発者が意図した通りのパスマッチングを実現するための重要な調整であったと言えます。

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

--- a/src/lib/http/triv.go
+++ b/src/lib/http/triv.go
@@ -87,8 +87,8 @@ func main() {
 	flag.Parse();
 	http.Handle("/counter", new(Counter));
 	http.Handle("/go/", http.HandlerFunc(FileServer));
-	http.Handle("/flags/", http.HandlerFunc(FlagServer));
-	http.Handle("/args/", http.HandlerFunc(ArgServer));
+	http.Handle("/flags", http.HandlerFunc(FlagServer));
+	http.Handle("/args", http.HandlerFunc(ArgServer));
 	http.Handle("/go/hello", http.HandlerFunc(HelloServer));
 	http.Handle("/chan", ChanCreate());
 	err := http.ListenAndServe(":12345", nil);

コアとなるコードの解説

変更は main 関数内の http.Handle の呼び出しに集中しています。

元のコードでは、以下の2つのハンドラ登録がありました。

  • http.Handle("/flags/", http.HandlerFunc(FlagServer));
  • http.Handle("/args/", http.HandlerFunc(ArgServer));

これらの行が、それぞれ以下のように変更されました。

  • http.Handle("/flags", http.HandlerFunc(FlagServer));
  • http.Handle("/args", http.HandlerFunc(ArgServer));

この変更の核心は、登録するパスパターンから末尾のスラッシュ(/)を削除した点です。

  • 変更前 (/flags/, /args/): 末尾にスラッシュがあるため、これらのパターンはプレフィックスマッチングとして機能します。つまり、/flags//flags/ だけでなく、/flags/foo/flags/bar/baz のようなパスにもマッチする可能性があります。また、/flags へのリクエストは /flags/ へリダイレクトされる可能性がありました。

  • 変更後 (/flags, /args): 末尾のスラッシュが削除されたことで、これらのパターンは完全一致マッチングとして機能します。FlagServer/flags というパスに正確に一致するリクエストに対してのみ呼び出され、ArgServer/args というパスに正確に一致するリクエストに対してのみ呼び出されます。これにより、ルーティングの挙動がより予測可能で厳密になります。

この修正は、FlagServerArgServer が特定のコマンドやリソースに対応しており、その配下のパスを処理する意図がない場合に特に適切です。例えば、/flags はコマンドライン引数を表示するエンドポイントであり、/flags/foo のようなサブパスは意味を持たない、といったケースが考えられます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (net/http パッケージ)
  • Stack Overflowの関連する質問と回答
  • Go言語のHTTPサーバーに関するブログ記事やチュートリアル
  • コミットメッセージと差分情報
  • Go言語の初期のコミット履歴 (GitHub)
  • Go言語の net/http パッケージのソースコード (特に ServeMux の実装)
  • Rob Pike氏のGo言語に関する講演や記事 (一般的なGoの設計思想を理解するため)
# [インデックス 1696] ファイルの概要

このコミットで変更されたファイルは `src/lib/http/triv.go` です。このファイルは、Go言語の初期の標準ライブラリの一部である `net/http` パッケージの、おそらくはテストまたは非常にシンプルなHTTPサーバーの例として機能していたと考えられます。ファイル名から推測すると、「triv」は「trivial(些細な、簡単な)」を意味し、HTTPハンドラの基本的な使用方法を示すためのものだった可能性が高いです。具体的には、`/counter`、`/go/`、`/flags/`、`/args/`、`/go/hello`、`/chan` といったパスに対するHTTPハンドラが定義されています。

## コミット

- **コミットハッシュ**: `6950491b4f0475f488a3a47b8d7771131cd2e8e7`
- **作者**: Rob Pike <r@golang.org>
- **コミット日時**: 2009年2月17日 火曜日 19:59:23 -0800
- **コミットメッセージ**: `drop trailing slashes - missed comment from last review`

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

[https://github.com/golang/go/commit/6950491b4f0475f488a3a47b8d7771131cd2e8e7](https://github.com/golang/go/commit/6950491b4f0475f488a3a47b8d7771131cd2e8e7)

## 元コミット内容

drop trailing slashes - missed comment from last review

TBR=rsc OCL=25135 CL=25135


## 変更の背景

このコミットの背景には、Go言語の初期のHTTPルーティングにおけるパスマッチングの挙動に関する調整があったと考えられます。コミットメッセージにある「missed comment from last review(前回のレビューで見落とされたコメント)」という記述から、以前のコードレビューで指摘されたにもかかわらず修正されていなかった点が、今回改めて修正されたことが示唆されます。

具体的には、`http.Handle` 関数に登録するパスの末尾のスラッシュ(`/`)の有無が、ルーティングの挙動に影響を与える可能性があり、その一貫性や意図しないマッチングを防ぐための修正であると推測されます。Goの `net/http` パッケージにおけるパスのマッチングルールは、特にディレクトリのようなパス(例: `/foo/`)とファイルのようなパス(例: `/foo`)を区別する際に重要になります。この変更は、特定のパスに対するハンドラ登録において、末尾のスラッシュを削除することで、より厳密な、あるいは意図した通りのパスマッチングを実現しようとしたものと考えられます。

## 前提知識の解説

### 1. HTTPルーティングとパスマッチング

HTTPサーバーは、クライアントからのリクエストURLのパスに基づいて、適切な処理を行うハンドラを呼び出します。この「パスに基づいてハンドラを決定する」プロセスをルーティングと呼びます。ルーティングにおいて、URLパスの末尾のスラッシュの有無は重要な意味を持つことがあります。

-   **末尾スラッシュの有無**:
    -   `/foo` のようなパスは、通常、特定の「リソース」や「ファイル」を指すことが多いです。
    -   `/foo/` のようなパスは、通常、特定の「ディレクトリ」や「コレクション」を指すことが多いです。Webサーバーによっては、`/foo/` へのリクエストに対して、自動的に `/foo/index.html` などのデフォルトファイルを返すことがあります。

Goの `net/http` パッケージでは、`http.Handle` や `http.HandleFunc` を用いてパスとハンドラを関連付けます。この際、登録するパスのパターンと、実際のリクエストパスのマッチングルールが重要になります。特に、GoのHTTPルーターは、登録されたパスが末尾にスラッシュを持つ場合と持たない場合で、異なるマッチング挙動を示すことがあります。

### 2. Go言語の `net/http` パッケージ

Goの標準ライブラリである `net/http` パッケージは、HTTPクライアントおよびサーバーを実装するための強力な機能を提供します。

-   **`http.Handle(pattern string, handler Handler)`**:
    指定された `pattern` に対応するHTTPリクエストを処理するために、`handler` を登録します。`handler` は `http.Handler` インターフェースを満たす必要があります。
-   **`http.HandleFunc(pattern string, handler func(ResponseWriter, *Request))`**:
    `http.Handle` の糖衣構文(シンタックスシュガー)で、関数をハンドラとして登録する際に便利です。内部的には、与えられた関数を `http.HandlerFunc` 型にキャストして `http.Handle` を呼び出します。
-   **パスのマッチングルール**:
    Goの `net/http` パッケージのデフォルトのServeMux(マルチプレクサ)は、以下のようなパスマッチングルールを持っています。
    -   完全一致: `/foo` は `/foo` にのみマッチします。
    -   末尾スラッシュ付きのパス: `/foo/` のように末尾にスラッシュがあるパスは、`/foo/bar` や `/foo/baz/qux` のように、そのプレフィックスに続くすべてのパスにマッチします。また、`/foo` へのリクエストがあった場合、自動的に `/foo/` へリダイレクトされることがあります(これは、ディレクトリへのアクセスと見なされるため)。
    -   末尾スラッシュなしのパス: `/foo` のように末尾にスラッシュがないパスは、完全一致でのみマッチします。ただし、より長いプレフィックスマッチング(例: `/foo/bar` が `/foo/` にマッチする)が存在しない場合に限ります。

このコミットは、これらのマッチングルールを考慮し、意図しないプレフィックスマッチングやリダイレクトを防ぐために、特定のパスから末尾スラッシュを削除したと考えられます。

## 技術的詳細

このコミットの技術的詳細は、Goの `net/http` パッケージにおけるパスのマッチング挙動、特にデフォルトの `ServeMux` の動作に深く関連しています。

Goの `net/http` パッケージの `ServeMux` は、登録されたパターンと受信したリクエストのURLパスを比較して、最適なハンドラを選択します。この選択ロジックにおいて、末尾のスラッシュの有無は重要な要素です。

1.  **プレフィックスマッチング**:
    `http.Handle("/prefix/", handler)` のように末尾にスラッシュがあるパターンを登録すると、そのパターンは `/prefix/` で始まるすべてのパス(例: `/prefix/foo`, `/prefix/bar/baz`)にマッチします。これは、ファイルシステムにおけるディレクトリのような振る舞いを模倣しています。

2.  **完全一致マッチング**:
    `http.Handle("/exact", handler)` のように末尾にスラッシュがないパターンを登録すると、そのパターンは `/exact` というパスにのみ完全に一致する場合にマッチします。

3.  **リダイレクト挙動**:
    もし `/foo/` というパターンでハンドラが登録されており、クライアントが `/foo` というパスでリクエストを送信した場合、Goのデフォルトの `ServeMux` は、通常、クライアントを `/foo/` へとリダイレクトします(HTTPステータスコード301 Moved Permanently)。これは、`/foo` がディレクトリを指す `/foo/` の省略形であると解釈されるためです。

このコミットでは、`http.Handle("/flags/", ...)` と `http.Handle("/args/", ...)` をそれぞれ `http.Handle("/flags", ...)` と `http.Handle("/args", ...)` に変更しています。この変更の意図は以下の通りです。

-   **厳密なマッチング**: 末尾のスラッシュを削除することで、`/flags` と `/args` というパスに対してのみハンドラが呼び出されるように、マッチングをより厳密にしています。これにより、例えば `/flags/some_param` のようなパスが意図せず `FlagServer` にルーティングされることを防ぎます。
-   **リダイレクトの回避**: `/flags` や `/args` へのリクエストが、不必要に `/flags/` や `/args/` へリダイレクトされることを回避します。これは、これらのパスが特定の「リソース」や「コマンド」を指しており、ディレクトリのような振る舞いを期待していない場合に特に重要です。
-   **一貫性の確保**: 他のハンドラ登録(例: `/counter`, `/go/hello`)が末尾スラッシュなしで登録されているため、これらも末尾スラッシュなしに統一することで、ルーティングの挙動に一貫性を持たせることができます。

この修正は、GoのHTTPサーバーが提供するルーティングのセマンティクスをより明確にし、開発者が意図した通りのパスマッチングを実現するための重要な調整であったと言えます。

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

```diff
--- a/src/lib/http/triv.go
+++ b/src/lib/http/triv.go
@@ -87,8 +87,8 @@ func main() {
 	flag.Parse();
 	http.Handle("/counter", new(Counter));
 	http.Handle("/go/", http.HandlerFunc(FileServer));
-	http.Handle("/flags/", http.HandlerFunc(FlagServer));
-	http.Handle("/args/", http.HandlerFunc(ArgServer));
+	http.Handle("/flags", http.HandlerFunc(FlagServer));
+	http.Handle("/args", http.HandlerFunc(ArgServer));
 	http.Handle("/go/hello", http.HandlerFunc(HelloServer));
 	http.Handle("/chan", ChanCreate());
 	err := http.ListenAndServe(":12345", nil);

コアとなるコードの解説

変更は main 関数内の http.Handle の呼び出しに集中しています。

元のコードでは、以下の2つのハンドラ登録がありました。

  • http.Handle("/flags/", http.HandlerFunc(FlagServer));
  • http.Handle("/args/", http.HandlerFunc(ArgServer));

これらの行が、それぞれ以下のように変更されました。

  • http.Handle("/flags", http.HandlerFunc(FlagServer));
  • http.Handle("/args", http.HandlerFunc(ArgServer));

この変更の核心は、登録するパスパターンから末尾のスラッシュ(/)を削除した点です。

  • 変更前 (/flags/, /args/): 末尾にスラッシュがあるため、これらのパターンはプレフィックスマッチングとして機能します。つまり、/flags//flags/ だけでなく、/flags/foo/flags/bar/baz のようなパスにもマッチする可能性があります。また、/flags へのリクエストは /flags/ へリダイレクトされる可能性がありました。

  • 変更後 (/flags, /args): 末尾のスラッシュが削除されたことで、これらのパターンは完全一致マッチングとして機能します。FlagServer/flags というパスに正確に一致するリクエストに対してのみ呼び出され、ArgServer/args というパスに正確に一致するリクエストに対してのみ呼び出されます。これにより、ルーティングの挙動がより予測可能で厳密になります。

この修正は、FlagServerArgServer が特定のコマンドやリソースに対応しており、その配下のパスを処理する意図がない場合に特に適切です。例えば、/flags はコマンドライン引数を表示するエンドポイントであり、/flags/foo のようなサブパスは意味を持たない、といったケースが考えられます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (net/http パッケージ)
  • Stack Overflowの関連する質問と回答
  • Go言語のHTTPサーバーに関するブログ記事やチュートリアル
  • コミットメッセージと差分情報
  • Go言語の初期のコミット履歴 (GitHub)
  • Go言語の net/http パッケージのソースコード (特に ServeMux の実装)
  • Rob Pike氏のGo言語に関する講演や記事 (一般的なGoの設計思想を理解するため)