[インデックス 17431] ファイルの概要
このコミットは、Go言語の標準ライブラリ net/http
パッケージにおける ServeMux
の自動リダイレクト処理に関するバグ修正です。具体的には、ServeMux.Handler()
メソッドが生成するリダイレクトにおいて、元のリクエストのクエリ文字列が失われてしまう問題を解決します。これにより、クエリ文字列を含むURLへのリクエストが ServeMux
によって自動的にリダイレクトされる際に、そのクエリ文字列が正しく保持されるようになります。
コミット
commit 716a409b9044850d0edf11318ec0eca63de57a93
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Thu Aug 29 13:55:12 2013 -0700
net/http: redirect handlers from mux.Handler() shouldn't clear the query string
R=bradfitz, alberto.garcia.hierro, rsc, adg
CC=golang-dev
https://golang.org/cl/7099045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/716a409b9044850d0edf11318ec0eca63de57a93
元コミット内容
net/http: redirect handlers from mux.Handler() shouldn't clear the query string
R=bradfitz, alberto.garcia.hierro, rsc, adg
CC=golang-dev
https://golang.org/cl/7099045
変更の背景
Goの net/http
パッケージの ServeMux
は、HTTPリクエストのパスを処理する際に、末尾のスラッシュの有無などによって自動的にリダイレクトを行う機能を持っています。例えば、/path
へのリクエストが登録されており、ユーザーが /path/
とリクエストした場合、ServeMux
は自動的に /path
へとリダイレクト(HTTP 301 Moved Permanently)を返します。
しかし、この自動リダイレクト処理において、元のリクエストURLに含まれるクエリ文字列(例: /path?key=value
の ?key=value
の部分)が、リダイレクト先のURLから失われてしまうという問題がありました。これは、リダイレクト先のURLを生成する際に、リクエストのパス部分のみを考慮し、クエリ文字列を適切に引き継いでいなかったためです。
この問題は、特にクエリ文字列に重要な情報が含まれている場合(例: セッションID、トラッキング情報、検索クエリなど)に、アプリケーションの動作に影響を与える可能性がありました。このコミットは、このクエリ文字列の消失を防ぎ、ServeMux
の自動リダイレクトがより期待通りに動作するようにするために行われました。
前提知識の解説
net/http
パッケージ: Go言語でHTTPクライアントおよびサーバーを実装するための標準ライブラリです。Webアプリケーション開発の基盤となります。http.ServeMux
: HTTPリクエストのURLパスに基づいて、適切なハンドラ(http.Handler
インターフェースを実装する型)にリクエストをルーティングするマルチプレクサ(ルーター)です。Handle
メソッドやHandleFunc
メソッドを使って、特定のパスとハンドラを関連付けます。http.Handler
インターフェース:ServeHTTP(ResponseWriter, *Request)
メソッドを持つインターフェースです。このメソッドがHTTPリクエストを処理し、レスポンスを書き込みます。- HTTPリダイレクト (HTTP Redirect): クライアント(ブラウザなど)に対して、リクエストされたリソースが別のURLに移動したことを伝えるHTTPレスポンスです。
StatusMovedPermanently
(301): リクエストされたリソースが恒久的に新しいURLに移動したことを示します。クライアントは将来のリクエストで新しいURLを使用すべきです。
- URLのクエリ文字列 (Query String): URLのパス部分の後に
?
で区切られて続く部分で、キーと値のペア(例:key1=value1&key2=value2
)で構成されます。サーバーに情報を渡すためによく使用されます。http.Request
オブジェクトのURL.RawQuery
フィールドでアクセスできます。 http.Request
オブジェクト: 受信したHTTPリクエストに関するすべての情報(メソッド、URL、ヘッダー、ボディなど)を含む構造体です。url.URL
構造体: URLの各コンポーネント(Scheme, Host, Path, RawQueryなど)を表現する構造体です。
技術的詳細
http.ServeMux
の Handler
メソッドは、受信した *http.Request
オブジェクトに基づいて、そのリクエストを処理する適切な http.Handler
と、そのハンドラが登録されているパターンを返します。このメソッドの内部では、リクエストの URL.Path
が正規化(cleanPath
関数による処理)されます。例えば、/path/
のような末尾にスラッシュがあるパスや、//path
のような余分なスラッシュがあるパスは、正規化されて /path
のような形式になります。
もし正規化されたパスが元のリクエストパスと異なる場合、ServeMux
は自動的に http.StatusMovedPermanently
(301) リダイレクトを生成し、クライアントを正規化されたパスに誘導します。
このコミット以前は、この自動リダイレクトを生成する際に、RedirectHandler
に渡されるURLが cleanPath
によって得られた「パス部分のみ」でした。具体的には、RedirectHandler(p, StatusMovedPermanently)
のように呼び出されていました。ここで p
はクエリ文字列を含まないパスです。このため、元のリクエストが http://example.com/foo/?param=value
であった場合、リダイレクト先は http://example.com/foo/
となり、?param=value
が失われていました。
このコミットでは、この問題を解決するために、リダイレクト先のURLを生成する際に、元のリクエストの URL
オブジェクトをコピーし、そのコピーの Path
フィールドのみを正規化されたパスに更新するように変更されました。そして、この更新された url.URL
オブジェクト全体を String()
メソッドで文字列化して RedirectHandler
に渡すことで、元のクエリ文字列が保持されるようになりました。
これにより、http://example.com/foo/?param=value
のようなリクエストが ServeMux
によって自動リダイレクトされる場合、リダイレクト先は http://example.com/foo?param=value
となり、クエリ文字列が正しく引き継がれます。
この変更は、net/http/server.go
の ServeMux.Handler
メソッド内で行われました。また、この修正の正しさを検証するために、net/http/serve_test.go
に新しいテストケース TestServeMuxHandlerRedirects
が追加されています。このテストは、クエリ文字列を含むURLが自動リダイレクトされた際に、クエリ文字列が保持されていることを確認します。
コアとなるコードの変更箇所
src/pkg/net/http/server.go
の ServeMux.Handler
メソッド内の変更です。
--- a/src/pkg/net/http/server.go
+++ b/src/pkg/net/http/server.go
@@ -1448,7 +1448,9 @@ func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
if r.Method != "CONNECT" {
if p := cleanPath(r.URL.Path); p != r.URL.Path {
_, pattern = mux.handler(r.Host, p)
- return RedirectHandler(p, StatusMovedPermanently), pattern
+ url := *r.URL
+ url.Path = p
+ return RedirectHandler(url.String(), StatusMovedPermanently), pattern
}
}
コアとなるコードの解説
変更されたコードブロックは、ServeMux.Handler
メソッド内で、リクエストのパスが正規化され、元のパスと異なる場合に自動リダイレクトを生成する部分です。
-
変更前:
return RedirectHandler(p, StatusMovedPermanently), pattern
ここでは、
cleanPath
関数によって正規化されたパスp
のみがRedirectHandler
に渡されていました。p
は単なるパス文字列であり、元のリクエストに含まれていたクエリ文字列は含まれていませんでした。このため、リダイレクト先のURLからクエリ文字列が失われていました。 -
変更後:
url := *r.URL url.Path = p return RedirectHandler(url.String(), StatusMovedPermanently), pattern
url := *r.URL
: これは、元のリクエストr
のURL
フィールド(*url.URL
型)をデリファレンスし、その値(url.URL
構造体)を新しい変数url
にコピーしています。これにより、元のURLに含まれるScheme
,Host
,RawQuery
(クエリ文字列) などの情報がすべてurl
に引き継がれます。url.Path = p
: コピーしたurl
オブジェクトのPath
フィールドを、cleanPath
によって正規化されたパスp
で上書きします。これにより、URLのパス部分は正規化されますが、他のコンポーネント(特にRawQuery
)は元のまま保持されます。return RedirectHandler(url.String(), StatusMovedPermanently), pattern
: 更新されたurl
オブジェクトのString()
メソッドを呼び出しています。url.URL
構造体のString()
メソッドは、その構造体が持つすべてのコンポーネント(Scheme, Host, Path, RawQueryなど)を結合して完全なURL文字列を生成します。この完全なURL文字列がRedirectHandler
に渡されることで、クエリ文字列がリダイレクト先に正しく含まれるようになります。
この変更により、ServeMux
の自動リダイレクト機能が、クエリ文字列を適切に保持するようになり、より堅牢なWebアプリケーションの構築に貢献します。
関連リンク
- Go CL 7099045: https://golang.org/cl/7099045
参考にした情報源リンク
- Go言語の
net/http
パッケージのドキュメント - Go言語の
url
パッケージのドキュメント - HTTP/1.1 RFC 2616 (Status Code 301 Moved Permanently)
- Go言語のソースコード (特に
src/pkg/net/http/server.go
とsrc/pkg/net/http/serve_test.go
)