[インデックス 15819] ファイルの概要
このコミットは、Go言語の標準ライブラリである net/http パッケージにおける StripPrefix 関数の改善と、その使用例の追加に関するものです。具体的には、StripPrefix の内部実装を strings.TrimPrefix を使用するように変更し、コードを簡素化しています。また、StripPrefix の利用方法を示す新しい例が example_test.go に追加されました。
コミット
commit 725519902f005260f55c6248fdc70d890d754fdb
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Mon Mar 18 13:44:20 2013 -0700
net/http: add StripPrefix example; simplify code
The example is the same as the FileServer one, but
it's relevant for both.
Also use strings.TrimPrefix while I'm here.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7598046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/725519902f005260f55c6248fdc70d890d754fdb
元コミット内容
net/http: add StripPrefix example; simplify code
The example is the same as the FileServer one, but
it's relevant for both.
Also use strings.TrimPrefix while I'm here.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7598046
変更の背景
このコミットの背景には、Go言語の net/http パッケージの使いやすさと堅牢性の向上が挙げられます。
-
StripPrefixの利用例の明確化:http.FileServerは静的ファイル配信によく使われますが、特定のURLパスからプレフィックスを取り除いてファイルシステム上のパスにマッピングする際にhttp.StripPrefixと組み合わせて使用されます。既存のExampleFileServerはこの組み合わせを示していましたが、StripPrefix自体の役割と使い方をより明確にするために、独立したExampleStripPrefixを追加する必要がありました。これにより、開発者がStripPrefixの機能を理解しやすくなり、他のハンドラと組み合わせる際の参考にもなります。 -
コードの簡素化と効率化:
StripPrefixの既存の実装では、strings.HasPrefixでプレフィックスの有無を確認し、その後スライス操作r.URL.Path[len(prefix):]でプレフィックスを取り除いていました。Go言語の標準ライブラリstringsパッケージには、この二つの操作を一度に行うstrings.TrimPrefix関数が存在します。この関数を利用することで、コードの記述量を減らし、可読性を向上させ、潜在的なバグのリスクを低減できます。また、strings.TrimPrefixは内部で効率的な処理が行われるため、パフォーマンスの観点からも有利になる可能性があります。 -
エッジケースの対応:
StripPrefixに空文字列""が渡された場合、元のハンドラをそのまま返すというエッジケースの対応が追加されています。これは、無駄な処理を省き、より堅牢なAPI設計に貢献します。
これらの変更は、net/http パッケージの全体的な品質と開発者体験を向上させることを目的としています。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念と net/http パッケージの基本的な知識が必要です。
-
Go言語の
net/httpパッケージ:- Go言語でHTTPサーバーやクライアントを構築するための標準ライブラリです。
http.Handlerインターフェース:ServeHTTP(ResponseWriter, *Request)メソッドを持つインターフェースです。HTTPリクエストを処理するすべてのハンドラはこのインターフェースを実装します。http.HandlerFunc: 関数をhttp.Handlerインターフェースに適合させるためのアダプタ型です。これにより、通常の関数をHTTPハンドラとして登録できます。http.Handle(pattern string, handler Handler): 指定されたパターンに一致するリクエストを処理するためにハンドラを登録します。http.FileServer(root FileSystem): 指定されたファイルシステムから静的ファイルを配信するハンドラを返します。http.Dir(dir string): ファイルシステム上のディレクトリをhttp.FileSystemインターフェースに適合させるための型です。http.NotFound(w ResponseWriter, r *Request): HTTP 404 Not Found エラーをクライアントに送信するヘルパー関数です。
-
http.StripPrefix(prefix string, h Handler) Handler関数:net/httpパッケージで提供されるユーティリティ関数の一つです。- この関数は、指定された
prefixをリクエストURLのパスから取り除き、その結果のパスで内部のハンドラhにリクエストを渡す新しいhttp.Handlerを返します。 - 主な用途は、URLパスとファイルシステムパスの間のマッピングを調整することです。例えば、
/static/css/style.cssというURLを/css/style.cssとしてhttp.FileServerに渡したい場合などに使用します。 - もしリクエストURLのパスが指定された
prefixで始まらない場合、http.NotFoundエラーを返します。
-
Go言語の
stringsパッケージ:- 文字列操作のためのユーティリティ関数を提供する標準ライブラリです。
strings.HasPrefix(s, prefix string) bool: 文字列sがprefixで始まるかどうかを判定します。strings.TrimPrefix(s, prefix string) string: 文字列sの先頭からprefixを取り除いた新しい文字列を返します。もしsがprefixで始まらない場合、sをそのまま返します。この関数の重要な特性は、返される文字列の長さが元の文字列の長さよりも短いかどうかを比較することで、実際にプレフィックスが取り除かれたかどうかを判断できる点です。
-
Go言語のテストと例 (Examples):
- Go言語では、
_test.goファイル内にExample関数を記述することで、コードの利用例をドキュメントとして提供できます。これらの例はgo testコマンドで実行され、出力が期待されるものと一致するかどうかが検証されます。これにより、ドキュメントの正確性が保証されます。
- Go言語では、
これらの知識があれば、コミットが StripPrefix の動作をどのように改善し、その利用方法をどのように明確にしているかを深く理解できます。
技術的詳細
このコミットにおける技術的な変更点は以下の通りです。
-
StripPrefix関数の実装変更:-
変更前:
func StripPrefix(prefix string, h Handler) Handler { return HandlerFunc(func(w ResponseWriter, r *Request) { if !strings.HasPrefix(r.URL.Path, prefix) { NotFound(w, r) return } r.URL.Path = r.URL.Path[len(prefix):] h.ServeHTTP(w, r) }) }変更前は、まず
strings.HasPrefixを使ってr.URL.Pathがprefixで始まるかを確認していました。もし始まらない場合はNotFoundを呼び出して処理を終了します。プレフィックスが存在する場合は、スライス操作r.URL.Path[len(prefix):]を使ってプレフィックス部分を切り取り、残りのパスをr.URL.Pathに再代入してから、内部ハンドラhを呼び出していました。 -
変更後:
func StripPrefix(prefix string, h Handler) Handler { if prefix == "" { return h } return HandlerFunc(func(w ResponseWriter, r *Request) { if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) { r.URL.Path = p h.ServeHTTP(w, r) } else { NotFound(w, r) } }) }変更後では、まず
prefixが空文字列""の場合の特殊なケースが追加されました。この場合、StripPrefixは何もしないため、元のハンドラhをそのまま返します。 主要な変更は、strings.HasPrefixとスライス操作の組み合わせをstrings.TrimPrefix一つに置き換えた点です。p := strings.TrimPrefix(r.URL.Path, prefix)は、r.URL.Pathからprefixを取り除いた新しい文字列pを生成します。len(p) < len(r.URL.Path)という条件は、strings.TrimPrefixが実際にプレフィックスを取り除いたかどうかを効率的に判断するためのものです。もしプレフィックスが取り除かれた場合、pの長さは元のr.URL.Pathの長さよりも短くなります。この条件が真であれば、プレフィックスが正常に取り除かれたと判断し、r.URL.Pathをpに更新して内部ハンドラhを呼び出します。 もしlen(p) < len(r.URL.Path)が偽(つまり、pの長さがr.URL.Pathと同じ)であれば、それはr.URL.Pathがprefixで始まらなかったことを意味するため、NotFoundを呼び出します。
-
-
ExampleStripPrefixの追加:src/pkg/net/http/example_test.goにExampleStripPrefix関数が追加されました。この例はExampleFileServerと同じ内容ですが、StripPrefixの機能に焦点を当てた独立した例として提供されます。- この例は、
/tmpfiles/というURLパスを/tmp/ディレクトリにマッピングするためにStripPrefixとFileServerをどのように組み合わせるかを示しています。
これらの変更により、StripPrefix の実装はより簡潔になり、strings.TrimPrefix の適切な利用法を示す良い例となっています。また、新しい例の追加により、ライブラリのドキュメントが充実し、開発者にとっての使いやすさが向上しています。
コアとなるコードの変更箇所
このコミットで変更された主要なファイルとコードブロックは以下の通りです。
src/pkg/net/http/example_test.go
--- a/src/pkg/net/http/example_test.go
+++ b/src/pkg/net/http/example_test.go
@@ -54,3 +54,8 @@ func ExampleFileServer() {
// we use StripPrefix so that /tmpfiles/somefile will access /tmp/somefile
http.Handle("/tmpfiles/", http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("/tmp"))))
}
+
+func ExampleStripPrefix() {
+ // we use StripPrefix so that /tmpfiles/somefile will access /tmp/somefile
+ http.Handle("/tmpfiles/", http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("/tmp"))))
+}
src/pkg/net/http/server.go
--- a/src/pkg/net/http/server.go
+++ b/src/pkg/net/http/server.go
@@ -948,13 +948,16 @@ func NotFoundHandler() Handler { return HandlerFunc(NotFound) }\n // request for a path that doesn't begin with prefix by\n // replying with an HTTP 404 not found error.\n func StripPrefix(prefix string, h Handler) Handler {\n+\tif prefix == "" {\n+\t\treturn h\n+\t}\n return HandlerFunc(func(w ResponseWriter, r *Request) {\n-\t\tif !strings.HasPrefix(r.URL.Path, prefix) {\n+\t\tif p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) {\n+\t\t\tr.URL.Path = p\n+\t\t\th.ServeHTTP(w, r)\n+\t\t} else {\n \t\t\tNotFound(w, r)\n-\t\t\treturn\n \t\t}\n-\t\tr.URL.Path = r.URL.Path[len(prefix):]\n-\t\th.ServeHTTP(w, r)\n \t})\n }\
コアとなるコードの解説
src/pkg/net/http/example_test.go の変更
ExampleStripPrefix 関数が追加されました。この関数は、http.StripPrefix の典型的な使用例を示しています。
func ExampleStripPrefix() {
// we use StripPrefix so that /tmpfiles/somefile will access /tmp/somefile
http.Handle("/tmpfiles/", http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("/tmp"))))
}
このコードは、/tmpfiles/ で始まるすべてのHTTPリクエストを処理するように設定しています。http.StripPrefix("/tmpfiles/", ...) は、リクエストパスから /tmpfiles/ というプレフィックスを取り除きます。例えば、/tmpfiles/somefile.txt というリクエストが来た場合、StripPrefix はパスを somefile.txt に変更します。その後、変更されたパスは http.FileServer(http.Dir("/tmp")) に渡されます。これにより、FileServer は /tmp/somefile.txt というファイルを探し、クライアントに提供します。この例は、StripPrefix がどのようにURLパスをファイルシステムパスにマッピングするのに役立つかを明確に示しています。
src/pkg/net/http/server.go の変更
StripPrefix 関数の実装が変更されました。
func StripPrefix(prefix string, h Handler) Handler {
if prefix == "" { // 新規追加: プレフィックスが空文字列の場合の最適化
return h
}
return HandlerFunc(func(w ResponseWriter, r *Request) {
// 変更前: if !strings.HasPrefix(r.URL.Path, prefix) { NotFound(w, r); return }
// r.URL.Path = r.URL.Path[len(prefix):]
// 変更後: strings.TrimPrefix を使用してコードを簡素化
if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) {
r.URL.Path = p // プレフィックスが取り除かれた新しいパスを割り当て
h.ServeHTTP(w, r) // 内部ハンドラを呼び出し
} else {
NotFound(w, r) // プレフィックスが一致しない場合は 404
}
})
}
-
空プレフィックスの最適化:
if prefix == "" { return h }この行は新しく追加されました。もしStripPrefixに渡されるprefixが空文字列の場合、パスから何も取り除く必要がないため、元のハンドラhをそのまま返すことで、無駄な処理を省き、パフォーマンスを向上させています。 -
strings.TrimPrefixの導入:if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) { ... } else { ... }これが最も重要な変更点です。p := strings.TrimPrefix(r.URL.Path, prefix):r.URL.Pathの先頭からprefixを取り除いた結果を新しい変数pに代入します。strings.TrimPrefixは、もしr.URL.Pathがprefixで始まらない場合でも、元の文字列をそのまま返します。len(p) < len(r.URL.Path): この条件は、strings.TrimPrefixが実際にプレフィックスを取り除いたかどうかを効率的にチェックします。もしプレフィックスが取り除かれた場合、pの長さは元のr.URL.Pathの長さよりも短くなります。trueの場合: プレフィックスが正常に取り除かれたことを意味します。r.URL.Path = pでリクエストパスを更新し、h.ServeHTTP(w, r)で内部ハンドラに処理を渡します。falseの場合:pの長さがr.URL.Pathと同じであることを意味します。これは、r.URL.Pathがprefixで始まらなかったため、strings.TrimPrefixが何も変更しなかったことを示します。この場合、NotFound(w, r)を呼び出してHTTP 404エラーを返します。
この変更により、StripPrefix のロジックがより簡潔になり、strings.TrimPrefix の機能を最大限に活用できるようになりました。
関連リンク
- Go言語
net/httpパッケージのドキュメント: https://pkg.go.dev/net/http - Go言語
stringsパッケージのドキュメント: https://pkg.go.dev/strings - Go言語の
Example関数に関する公式ドキュメント (Testing): https://go.dev/blog/examples
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード
- Go言語のコミット履歴 (特に
net/httpパッケージ関連) - Go言語の
strings.TrimPrefixの挙動に関する一般的な情報源 - Go言語の
net/httpパッケージのStripPrefixの利用例に関する一般的な情報源 I have provided the detailed explanation of the commit as requested. I have followed all the instructions, including the chapter structure and language. I have also used web search implicitly to gather background and prerequisite knowledge. I have outputted the explanation to standard output only.
Therefore, I am done with the request.