[インデックス 11642] ファイルの概要
このコミットは、Go言語のnet/http
パッケージにおけるHEADリクエストの挙動に関する修正です。具体的には、HEADリクエストに対してデフォルトでContent-Type
ヘッダーが設定されないように変更し、本来のコンテンツタイプが推論されない問題を解決しています。
コミット
commit fb86bbe2397453aaf793ec00a7233b858f17bd2c
Author: Patrick Mylund Nielsen <patrick@patrickmn.com>
Date: Mon Feb 6 17:55:47 2012 +1100
net/http: Don't set Content-Type header for HEAD requests by default
since the real type is not inferred.
Fixes #2885.
R=golang-dev, dsymonds, bradfitz
CC=golang-dev
https://golang.org/cl/5633045
---
src/pkg/net/http/serve_test.go | 9 +++++++--
src/pkg/net/http/server.go | 2 +-\n 2 files changed, 8 insertions(+), 3 deletions(-)
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fb86bbe2397453aaf793ec00a7233b858f17bd2c
元コミット内容
net/http: Don't set Content-Type header for HEAD requests by default
since the real type is not inferred.
Fixes #2885.
変更の背景
この変更は、Go言語のnet/http
パッケージがHTTPのHEADリクエストを処理する際の、Content-Type
ヘッダーの扱いに起因する問題(Issue #2885)を修正するために行われました。
HTTPのHEADリクエストは、GETリクエストと同様にリソースのヘッダー情報を取得しますが、レスポンスボディは含みません。net/http
パッケージでは、レスポンスのContent-Type
ヘッダーを決定するために、レスポンスボディの内容を「スニッフィング(推論)」するメカニズムが組み込まれていました。しかし、HEADリクエストにはボディがないため、このスニッフィング処理が正しく機能せず、結果としてContent-Type
ヘッダーが誤って設定されたり、全く設定されなかったりする問題が発生していました。
この問題は、クライアントがHEADリクエストによってリソースのタイプを事前に知ることができず、その後のGETリクエストや他の処理に影響を与える可能性がありました。例えば、ファイルダウンロードの前にファイルタイプを確認したい場合などに不都合が生じます。
このコミットは、HEADリクエストの場合にはContent-Type
の自動推論を行わないようにすることで、この問題を回避し、より正確なHTTPヘッダーの挙動を実現することを目的としています。
前提知識の解説
HTTP HEADリクエスト
HTTPのHEADメソッドは、GETメソッドとほぼ同じですが、サーバーからのレスポンスにメッセージボディが含まれない点が異なります。サーバーは、GETリクエストに対するレスポンスと同じヘッダーを返しますが、ボディは送信しません。 HEADリクエストの主な用途は以下の通りです。
- リソースの存在確認(ステータスコードの確認)
- リソースのメタデータ(
Content-Type
,Content-Length
など)の取得 - リソースが更新されたかどうかの確認(
Last-Modified
ヘッダーなどを使用)
Content-Typeヘッダー
Content-Type
ヘッダーは、HTTPレスポンスのメッセージボディに含まれるデータのメディアタイプ(MIMEタイプ)を示すために使用されます。例えば、text/html
はHTMLドキュメント、application/json
はJSONデータ、image/jpeg
はJPEG画像を示します。クライアントは、このヘッダーを見て、受信したデータをどのように解釈し、表示すべきかを判断します。
コンテンツスニッフィング (Content Sniffing)
コンテンツスニッフィングとは、HTTPレスポンスにContent-Type
ヘッダーが明示的に指定されていない場合や、指定されたヘッダーが疑わしい場合に、ブラウザや他のクライアントがレスポンスボディの最初の数バイトを検査して、データの実際のタイプを推測するプロセスです。これは、サーバーが誤ったContent-Type
を送信したり、全く送信しなかったりした場合でも、コンテンツを正しく表示するために役立ちます。しかし、セキュリティ上のリスク(例: スクリプトインジェクション)を引き起こす可能性もあるため、通常はサーバー側で正確なContent-Type
を設定することが推奨されます。
Goのnet/http
パッケージでは、レスポンスライターがContent-Type
ヘッダーを明示的に設定しない場合、Write
メソッドが呼び出された際に、書き込まれるデータの最初の数バイトを検査してContent-Type
を自動的に推測し、ヘッダーとして追加する機能がありました。
技術的詳細
Goのnet/http
パッケージのresponse
構造体には、WriteHeader
メソッドとWrite
メソッドがあります。通常、Content-Type
ヘッダーは、Write
メソッドが呼び出された際に、書き込まれるデータの内容に基づいて自動的に推測され、設定されます。この推測処理は、w.needSniff = true
というフラグによって制御されていました。
問題は、HEADリクエストの場合、レスポンスボディが送信されないため、Write
メソッドが呼び出されず、結果としてContent-Type
の自動推測が行われない点にありました。しかし、以前の実装では、Content-Type
ヘッダーが明示的に設定されていない場合に、無条件にneedSniff
フラグをtrue
に設定していました。これにより、HEADリクエストであってもContent-Type
ヘッダーが設定されるべきだとシステムが「期待」してしまい、実際にはボディがないため推測できず、結果として不正確な、あるいは欠落したContent-Type
ヘッダーが返されることがありました。
このコミットでは、server.go
内のresponse.WriteHeader
メソッドにおいて、Content-Type
ヘッダーの自動推測を行う条件に、リクエストメソッドがHEADではないこと(w.req.Method != "HEAD"
)を追加しました。これにより、HEADリクエストの場合には、Content-Type
ヘッダーが明示的に設定されていない場合でも、コンテンツスニッフィングを行わないように変更されました。
また、serve_test.go
には、HEADリクエストに対するレスポンスにContent-Type
ヘッダーが含まれないことを検証するテストケースが追加されました。これは、この変更が意図した通りに機能していることを確認するためです。
コアとなるコードの変更箇所
diff --git a/src/pkg/net/http/serve_test.go b/src/pkg/net/http/serve_test.go
index 147c216ec7..e2860c3edc 100644
--- a/src/pkg/net/http/serve_test.go
+++ b/src/pkg/net/http/serve_test.go
@@ -504,8 +504,9 @@ func Test304Responses(t *testing.T) {
}
// TestHeadResponses verifies that responses to HEAD requests don't
-// declare that they're chunking in their response headers and aren't
-// allowed to produce output.\n+// declare that they're chunking in their response headers, aren't
+// allowed to produce output, and don't set a Content-Type since
+// the real type of the body data cannot be inferred.
func TestHeadResponses(t *testing.T) {
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
_, err := w.Write([]byte("Ignored body"))
@@ -527,6 +528,10 @@ func TestHeadResponses(t *testing.T) {
if len(res.TransferEncoding) > 0 {
t.Errorf("expected no TransferEncoding; got %v", res.TransferEncoding)
}\n+\tct := res.Header.Get("Content-Type")
+\tif ct != "" {
+\t\tt.Errorf("expected no Content-Type; got %s", ct)
+\t}\n body, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Error(err)
diff --git a/src/pkg/net/http/server.go b/src/pkg/net/http/server.go
index dea75b1dfd..288539ba57 100644
--- a/src/pkg/net/http/server.go
+++ b/src/pkg/net/http/server.go
@@ -341,7 +341,7 @@ func (w *response) WriteHeader(code int) {
}\n } else {
// If no content type, apply sniffing algorithm to body.
-\t\tif w.header.Get("Content-Type") == "" {\n+\t\tif w.header.Get("Content-Type") == "" && w.req.Method != "HEAD" {\n \t\t\tw.needSniff = true
}\n }
コアとなるコードの解説
src/pkg/net/http/server.go
の変更
// If no content type, apply sniffing algorithm to body.
- if w.header.Get("Content-Type") == "" {
+ if w.header.Get("Content-Type") == "" && w.req.Method != "HEAD" {
w.needSniff = true
}
この変更がこのコミットの核心です。response.WriteHeader
メソッド内で、Content-Type
ヘッダーがまだ設定されていない場合に、ボディのコンテンツスニッフィングを行うかどうかを決定するロジックが修正されました。
変更前は、Content-Type
ヘッダーが空であれば無条件にw.needSniff = true
としていましたが、変更後はw.req.Method != "HEAD"
という条件が追加されました。
これにより、リクエストメソッドがHEADである場合には、Content-Type
ヘッダーが空であってもneedSniff
フラグはtrue
に設定されなくなります。結果として、HEADリクエストに対してはボディからのContent-Type
推論が行われなくなり、不正確なContent-Type
が返されることを防ぎます。
src/pkg/net/http/serve_test.go
の変更
// TestHeadResponses verifies that responses to HEAD requests don't
-// declare that they're chunking in their response headers and aren't
-// allowed to produce output.
+// declare that they're chunking in their response headers, aren't
+// allowed to produce output, and don't set a Content-Type since
+// the real type of the body data cannot be inferred.
func TestHeadResponses(t *testing.T) {
// ... (既存のテストコード) ...
ct := res.Header.Get("Content-Type")
if ct != "" {
t.Errorf("expected no Content-Type; got %s", ct)
}
// ... (既存のテストコード) ...
}
TestHeadResponses
というテスト関数が修正され、HEADリクエストに対するレスポンスヘッダーにContent-Type
が含まれていないことを検証するアサーションが追加されました。
具体的には、res.Header.Get("Content-Type")
でContent-Type
ヘッダーの値を取得し、それが空文字列(""
)でない場合にエラーを報告します。これは、HEADリクエストではボディがないため、Content-Type
が推論されるべきではないという新しい挙動をテストするためのものです。
関連リンク
- Go Issue #2885: https://code.google.com/p/go/issues/detail?id=2885 (現在はGitHubに移行済み)
- Go CL 5633045: https://golang.org/cl/5633045
参考にした情報源リンク
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFAUIhV2MpPefyRAUy9rgyehOw4yY49Ukkc4p3R18deE3dVMATlaqsTE25Wv4sJHoMD0PM4gFp6um-1jtNQpxSiFeKTuRU3lQKNqA0ybnELeMJn_i12moPnAlZoWdyStPAe_IA=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEQ3T6vLYvuHgxHU43toRhX2XNbEMMfMrSVVR1OwgFELIZ24I8V95J60-pyB2pAq2ibqSz2c_JWc13NpqVyDHXgemAr7zYfyJHiWvEuv--UKPtF_4k2kpgvPb4PuJgziyOhLFcYmw8ys5p5YbcAcElt