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

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

このコミットは、cmd/godoc ツールにおいて、HTTP HEAD リクエスト時に発生する http.ErrBodyNotAllowed エラーに関する警告ログが出力されないように修正するものです。これにより、godocHEAD リクエストを処理する際のログがよりクリーンになり、本来エラーではない状況での不要な警告が抑制されます。

コミット

commit d9d8d4c62c658f7c4015c73fcb2a1b809dbc0182
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Tue May 14 21:21:11 2013 -0700

    cmd/godoc: don't warn about HEAD requests
    
    Fixes #5451
    
    R=gri
    CC=dsymonds, gobot, golang-dev
    https://golang.org/cl/9388043

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

https://github.com/golang/go/commit/d9d8d4c62c658f7c4015c73fcb2a1b809dbc0182

元コミット内容

cmd/godoc: HEAD リクエストに関する警告を出力しない。 Issue #5451 を修正。

変更の背景

godoc はGo言語のドキュメントを生成・表示するためのツールであり、HTTPサーバーとしても機能します。Webサーバーとして動作する際、クライアントからのHTTPリクエストを処理します。HTTP HEAD リクエストは、GET リクエストと同様にリソースのヘッダー情報のみを要求し、レスポンスボディは含みません。

しかし、godoc の内部では、GET リクエストと HEAD リクエストの両方に対して、HTMLテンプレートの実行(Execute メソッドの呼び出し)を試みていました。HEAD リクエストの場合、レスポンスボディは送信されないため、テンプレートの実行結果を http.ResponseWriter に書き込もうとすると、Goの net/http パッケージは http.ErrBodyNotAllowed というエラーを返します。

このエラーは HEAD リクエストの性質上予期されるものであり、本来は問題ではありません。しかし、godoc はこのエラーを通常のログとして出力していたため、不要な警告がログに大量に記録され、ログの可読性を損ねていました。このコミットは、この http.ErrBodyNotAllowed エラーが HEAD リクエスト時に発生した場合にログ出力を行わないようにすることで、この問題を解決します。コミットメッセージにある Fixes #5451 は、この問題がGoのIssueトラッカーで報告されていたことを示唆しています。

前提知識の解説

HTTP HEAD リクエスト

HTTP HEAD メソッドは、GET メソッドと全く同じヘクエストヘッダーを受け取りますが、レスポンスボディは返しません。サーバーは GET リクエストに対するレスポンスと同じヘッダーを返しますが、ボディは空です。これは、リソース全体をダウンロードせずに、そのリソースに関するメタデータ(例えば、最終更新日時やコンテンツタイプなど)を取得したい場合に便利です。

Go言語における http.ErrBodyNotAllowed

Go言語の net/http パッケージにおいて、http.ErrBodyNotAllowed は、レスポンスボディが許可されていないHTTPメソッド(例: HEAD)やステータスコード(例: 204 No Content)に対して、http.ResponseWriter にボディを書き込もうとした際に発生するエラーです。これは、HTTPプロトコルの仕様に準拠するためのGoの挙動であり、HEAD リクエストにおいては正常な動作の一部と見なされます。

cmd/godoc

godoc はGo言語の公式ドキュメントツールです。Goのソースコードからコメントや宣言を解析し、HTML形式でドキュメントを生成・表示することができます。ローカルでHTTPサーバーとして起動し、ブラウザからアクセスしてドキュメントを閲覧することが可能です。

技術的詳細

godoc の内部では、servePage 関数と serveSearchDesc 関数がそれぞれHTMLテンプレートとXMLテンプレートの実行を担当しています。これらの関数は、html/template パッケージや xml/template パッケージの Execute メソッドを使用して、テンプレートを http.ResponseWriter に書き出します。

// servePage 関数の一部 (変更前)
if err := godocHTML.Execute(w, page); err != nil {
    log.Printf("godocHTML.Execute: %s", err)
}

// serveSearchDesc 関数の一部 (変更前)
if err := searchDescXML.Execute(w, &data); err != nil {
    log.Printf("searchDescXML.Execute: %s", err)
}

HEAD リクエストがこれらの関数に到達した場合、Execute メソッドはレスポンスボディを書き込もうとしますが、HEAD リクエストではボディが許可されていないため、http.ErrBodyNotAllowed エラーが発生します。変更前は、このエラーも他のエラーと同様に log.Printf で出力されていました。

このコミットでは、エラーが http.ErrBodyNotAllowed である場合に限り、ログ出力をスキップするように条件を追加しています。これにより、HEAD リクエスト時に発生する予期されたエラーがログに記録されなくなり、ログがより意味のある情報のみを含むようになります。

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

--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -642,7 +642,9 @@ func servePage(w http.ResponseWriter, page Page) {
 	page.SearchBox = *indexEnabled
 	page.Playground = *showPlayground
 	page.Version = runtime.Version()
-	if err := godocHTML.Execute(w, page); err != nil {
+	if err := godocHTML.Execute(w, page); err != nil && err != http.ErrBodyNotAllowed {
+		// Only log if there's an error that's not about writing on HEAD requests.
+		// See Issues 5451 and 5454.
 		log.Printf("godocHTML.Execute: %s", err)
 	}
 }
@@ -860,7 +862,9 @@ func serveSearchDesc(w http.ResponseWriter, r *http.Request) {
 	data := map[string]interface{}{}
 	data["BaseURL"] = fmt.Sprintf("http://%s", r.Host)
-	if err := searchDescXML.Execute(w, &data); err != nil {
+	if err := searchDescXML.Execute(w, &data); err != nil && err != http.ErrBodyNotAllowed {
+		// Only log if there's an error that's not about writing on HEAD requests.
+		// See Issues 5451 and 5454.
 		log.Printf("searchDescXML.Execute: %s", err)
 	}
 }

コアとなるコードの解説

変更は src/cmd/godoc/godoc.go ファイルの servePage 関数と serveSearchDesc 関数の2箇所で行われています。

servePage 関数

元のコード:

if err := godocHTML.Execute(w, page); err != nil {
    log.Printf("godocHTML.Execute: %s", err)
}

変更後のコード:

if err := godocHTML.Execute(w, page); err != nil && err != http.ErrBodyNotAllowed {
    // Only log if there's an error that's not about writing on HEAD requests.
    // See Issues 5451 and 5454.
    log.Printf("godocHTML.Execute: %s", err)
}

godocHTML.Execute(w, page) の呼び出し結果としてエラーが発生した場合の条件式に、&& err != http.ErrBodyNotAllowed が追加されました。これにより、エラーが nil でなく、かつ http.ErrBodyNotAllowed でない場合にのみ log.Printf が実行されるようになります。コメントには「HEAD リクエストでの書き込みに関するエラーでない場合にのみログを記録する」と明記されており、Issue 5451と5454が参照されています。

serveSearchDesc 関数

同様に、searchDescXML.Execute(w, &data) の呼び出し結果としてエラーが発生した場合の条件式にも、&& err != http.ErrBodyNotAllowed が追加されています。

if err := searchDescXML.Execute(w, &data); err != nil && err != http.ErrBodyNotAllowed {
    // Only log if there's an error that's not about writing on HEAD requests.
    // See Issues 5451 and 5454.
    log.Printf("searchDescXML.Execute: %s", err)
}

この変更により、godocHEAD リクエストを処理する際に発生する http.ErrBodyNotAllowed エラーがログに記録されなくなり、ログがよりクリーンで意味のある情報のみを含むようになります。

関連リンク

  • Go Issue #5451 (コミットメッセージで参照されていますが、直接的な検索結果は見つかりませんでした。古いIssueであるか、番号が変更された可能性があります。)
  • Go Issue #5454 (コミットのコメントで参照されています。)

参考にした情報源リンク