[インデックス 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.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パスを比較して、最適なハンドラを選択します。この選択ロジックにおいて、末尾のスラッシュの有無は重要な要素です。
-
プレフィックスマッチング:
http.Handle("/prefix/", handler)
のように末尾にスラッシュがあるパターンを登録すると、そのパターンは/prefix/
で始まるすべてのパス(例:/prefix/foo
,/prefix/bar/baz
)にマッチします。これは、ファイルシステムにおけるディレクトリのような振る舞いを模倣しています。 -
完全一致マッチング:
http.Handle("/exact", handler)
のように末尾にスラッシュがないパターンを登録すると、そのパターンは/exact
というパスにのみ完全に一致する場合にマッチします。 -
リダイレクト挙動: もし
/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
というパスに正確に一致するリクエストに対してのみ呼び出されます。これにより、ルーティングの挙動がより予測可能で厳密になります。
この修正は、FlagServer
と ArgServer
が特定のコマンドやリソースに対応しており、その配下のパスを処理する意図がない場合に特に適切です。例えば、/flags
はコマンドライン引数を表示するエンドポイントであり、/flags/foo
のようなサブパスは意味を持たない、といったケースが考えられます。
関連リンク
- Go言語の
net/http
パッケージのドキュメント: https://pkg.go.dev/net/http - GoのHTTPルーティングに関する議論(Stack Overflowなど):
参考にした情報源リンク
- 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
というパスに正確に一致するリクエストに対してのみ呼び出されます。これにより、ルーティングの挙動がより予測可能で厳密になります。
この修正は、FlagServer
と ArgServer
が特定のコマンドやリソースに対応しており、その配下のパスを処理する意図がない場合に特に適切です。例えば、/flags
はコマンドライン引数を表示するエンドポイントであり、/flags/foo
のようなサブパスは意味を持たない、といったケースが考えられます。
関連リンク
- Go言語の
net/http
パッケージのドキュメント: https://pkg.go.dev/net/http - GoのHTTPルーティングに関する議論(Stack Overflowなど):
参考にした情報源リンク
- Go言語の公式ドキュメント (
net/http
パッケージ) - Stack Overflowの関連する質問と回答
- Go言語のHTTPサーバーに関するブログ記事やチュートリアル
- コミットメッセージと差分情報
- Go言語の初期のコミット履歴 (GitHub)
- Go言語の
net/http
パッケージのソースコード (特にServeMux
の実装) - Rob Pike氏のGo言語に関する講演や記事 (一般的なGoの設計思想を理解するため)