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

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

このコミットは、cmd/godoc ツールにおいて、末尾にスラッシュが付いたファイルパスへのリクエストを、スラッシュなしの正規のファイルパスへリダイレクトする機能を追加するものです。これにより、godoc が提供するドキュメントやソースコードの表示において、URLの正規化と一貫性が向上します。具体的には、Fixes #4543 と記載されており、関連するバグ報告に対応しています。

コミット

  • コミットハッシュ: 2874cc4eedf1e152f8327c1d93fb5d6a6a973a15
  • 作者: Andrew Gerrand adg@golang.org
  • 日付: Fri Dec 21 07:03:00 2012 +1100
  • コミットメッセージ:
    cmd/godoc: redirect for file with trailing /
    
    Fixes #4543.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/6971050
    
  • 変更ファイル: src/cmd/godoc/godoc.go (1ファイル)
  • 変更行数: 15行の追加

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

https://github.com/golang/go/commit/2874cc4eedf1e152f8327c1d93fb5d6a6a973a15

元コミット内容

cmd/godoc: redirect for file with trailing /

Fixes #4543.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6971050

変更の背景

この変更の背景には、godoc が提供するWebインターフェースにおけるURLの正規化の問題があります。通常、Webサーバーでは、ディレクトリを示すURLには末尾にスラッシュを付けることが一般的ですが、ファイルを示すURLには末尾にスラッシュを付けません。しかし、ユーザーが誤ってファイルパスの末尾にスラッシュを付けてアクセスした場合、godoc はそのリクエストを正しく処理できないか、または期待しない動作をする可能性がありました。

Fixes #4543 とあるように、このコミットは特定のバグ報告(Issue 4543)に対応しています。このバグは、godoc がファイルを提供するときに、末尾のスラッシュを適切に扱わないことによって発生していたと考えられます。例えば、/path/to/file.go/ のようにアクセスされた場合に、/path/to/file.go へとリダイレクトすることで、一貫したURL表現と正しいコンテンツの提供を保証することが目的です。

Go言語の標準ライブラリである net/http パッケージの http.ServeMux は、末尾のスラッシュの有無によってリダイレクトを行うデフォルトの挙動を持っています。これは、ディレクトリとファイルの区別を明確にし、一貫したルーティングを保証するためです。しかし、このコミットが修正しようとしているのは、おそらく http.ServeMux のデフォルトの挙動だけではカバーしきれない、特定のファイルパスに対する末尾スラッシュの処理に関する godoc 固有の問題であったと推測されます。

前提知識の解説

  • godoc: Go言語の公式ドキュメンテーションツールです。Goのソースコードからドキュメントを生成し、Webサーバーとして提供する機能も持っています。開発者は godoc をローカルで実行することで、Goの標準ライブラリや自身のプロジェクトのドキュメントをブラウザで閲覧できます。
  • HTTPリダイレクト (301 Moved Permanently): Webサーバーがクライアントに対して、リクエストされたリソースが恒久的に別のURLに移動したことを伝えるHTTPステータスコードです。ブラウザはこれを受け取ると、新しいURLに自動的にリクエストを再送信します。SEOの観点からも、URLの正規化に広く利用されます。
  • URLパスと末尾のスラッシュ: URLのパス部分は、Webサーバー上のリソースの場所を示します。
    • example.com/dir/: 通常、dir という名前のディレクトリを示します。
    • example.com/file.html: 通常、file.html という名前のファイルを示します。 Webサーバーによっては、example.com/dir へのアクセスを example.com/dir/ へリダイレクトしたり、その逆を行ったりすることがあります。このコミットでは、ファイルパスに誤って付与された末尾のスラッシュを削除するリダイレクトを扱っています。
  • pathpkg.Clean: Go言語の path パッケージ(通常は pathpkg としてインポートされる)の Clean 関数は、パスを簡潔な形式に変換します。例えば、a/b/../ca/c に、a//ba/b に、/a/b//a/b になります。この関数は、パスの正規化に非常に役立ちます。
  • strings.HasSuffix: Go言語の strings パッケージの関数で、ある文字列が特定の接尾辞で終わるかどうかを判定します。

技術的詳細

このコミットは、godoc のWebサーバー機能において、ファイルパスの末尾に不要なスラッシュが付いている場合に、それを削除して正規のパスにリダイレクトする新しい関数 redirectFile を導入しています。

既存の serveFile 関数は、ファイルの種類を判別し、テキストファイルであれば serveTextFile を呼び出すロジックを持っています。この変更では、serveTextFile を呼び出す前に redirectFile を呼び出すように修正されています。

redirectFile 関数の内部では、以下の処理が行われます。

  1. r.URL.Path (リクエストされたURLパス) を pathpkg.Clean で正規化し、c に格納します。pathpkg.Clean は、末尾のスラッシュを削除する効果もあります。
  2. for strings.HasSuffix("/", c) ループを使って、c の末尾にスラッシュが残っている限り、そのスラッシュを削除します。pathpkg.Clean は通常、末尾のスラッシュを削除しますが、念のためこのループで確実に削除しています。
  3. 正規化されたパス c が元のリクエストパス r.URL.Path と異なる場合、つまり末尾のスラッシュが削除された場合、http.Redirect を使用して http.StatusMovedPermanently (301) ステータスコードで正規のパス c へリダイレクトします。
  4. リダイレクトが行われた場合は true を、そうでない場合は false を返します。

serveFile 関数では、isTextFile(abspath)true の場合に、まず redirectFile(w, r) を呼び出します。もし redirectFiletrue を返した場合(つまりリダイレクトが行われた場合)、それ以上処理を進めずに return することで、二重の処理を防ぎます。これにより、ユーザーは常に正規化されたURLでコンテンツにアクセスできるようになります。

この修正は、godoc の堅牢性とユーザーエクスペリエンスを向上させ、URLの正規化に関する一般的なWebの慣習に沿ったものとなります。

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

--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -658,6 +658,18 @@ func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) {
 	return
 }
 
+func redirectFile(w http.ResponseWriter, r *http.Request) (redirected bool) {
+	c := pathpkg.Clean(r.URL.Path)
+	for strings.HasSuffix("/", c) {
+		c = c[:len(c)-1]
+	}
+	if r.URL.Path != c {
+		http.Redirect(w, r, c, http.StatusMovedPermanently)
+		redirected = true
+	}
+	return
+}
+
 func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, title string) {
 	src, err := ReadFile(fs, abspath)
 	if err != nil {
@@ -749,6 +761,9 @@ func serveFile(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if isTextFile(abspath) {
+		if redirectFile(w, r) {
+			return
+		}
 		serveTextFile(w, r, abspath, relpath, "Text file")
 		return
 	}

コアとなるコードの解説

redirectFile 関数の追加

func redirectFile(w http.ResponseWriter, r *http.Request) (redirected bool) {
	c := pathpkg.Clean(r.URL.Path)
	for strings.HasSuffix("/", c) {
		c = c[:len(c)-1]
	}
	if r.URL.Path != c {
		http.Redirect(w, r, c, http.StatusMovedPermanently)
		redirected = true
	}
	return
}

この新しい関数 redirectFile は、リクエストされたURLパス r.URL.Path を受け取り、そのパスがファイルを示すものでありながら末尾に不要なスラッシュが付いている場合に、正規のパスへリダイレクトする役割を担います。

  1. c := pathpkg.Clean(r.URL.Path): pathpkg.Clean は、パスを正規化します。例えば、/a/b/../c/ のようなパスは /a/c に、/a//b/a/b になります。また、末尾のスラッシュも削除される傾向があります。この行で、まずパスを一般的なクリーンな形式に変換します。

  2. for strings.HasSuffix("/", c) { c = c[:len(c)-1] }: pathpkg.Clean が末尾のスラッシュを完全に削除しないケース(例えば、ルートパス / の場合など)や、より確実にするために、このループが追加されています。strings.HasSuffix("/", c) は、文字列 c/ で終わるかどうかをチェックします。もし終わっていれば、c = c[:len(c)-1] によって末尾の / を削除します。これにより、パスの末尾からすべてのスラッシュが取り除かれます。

  3. if r.URL.Path != c { ... }: 元のリクエストパス r.URL.Path と、スラッシュが削除され正規化されたパス c を比較します。もし両者が異なる場合、それはリダイレクトが必要であることを意味します。

  4. http.Redirect(w, r, c, http.StatusMovedPermanently): http.Redirect 関数は、HTTPリダイレクトを実行します。

    • w: http.ResponseWriter で、HTTPレスポンスを書き込むためのインターフェースです。
    • r: *http.Request で、受信したHTTPリクエストの情報を含みます。
    • c: リダイレクト先の新しいURLパスです。
    • http.StatusMovedPermanently: HTTPステータスコード301を表します。これは、リソースが恒久的に新しいURLに移動したことをクライアントに伝えます。ブラウザや検索エンジンは、この情報に基づいてキャッシュを更新し、今後のリクエストを新しいURLに送るようになります。
  5. redirected = true: リダイレクトが行われたことを示すために、戻り値 redirectedtrue に設定します。

serveFile 関数での呼び出し

 func serveFile(w http.ResponseWriter, r *http.Request) {
 	// ... (既存のコード) ...

 	if isTextFile(abspath) {
+		if redirectFile(w, r) {
+			return
+		}
 		serveTextFile(w, r, abspath, relpath, "Text file")
 		return
 	}
 	// ... (既存のコード) ...
 }

serveFile 関数は、ファイルを提供するための主要なハンドラです。この変更では、テキストファイル(isTextFile(abspath)true の場合)を処理する前に、新しく追加された redirectFile 関数を呼び出すように修正されています。

  1. if redirectFile(w, r) { return }: redirectFile を呼び出し、その戻り値を確認します。もし redirectFiletrue を返した場合(つまり、リダイレクトが実行された場合)、それ以上このリクエストに対する処理は不要なので、serveFile 関数を即座に return します。これにより、リダイレクト後に serveTextFile が誤って呼び出されることを防ぎ、効率的かつ正しい動作を保証します。

この変更により、godoc はファイルパスの末尾スラッシュ問題を適切に処理し、ユーザーに正規化されたURLを提供できるようになりました。

関連リンク

  • Go CL (Code Review) リンク: https://golang.org/cl/6971050
  • 関連するGitHub Issue: Fixes #4543 (ただし、直接的なIssueページはWeb検索では見つかりませんでした。GoのIssueトラッカーは時期によってURL構造が異なる場合があります。)

参考にした情報源リンク