[インデックス 14590] ファイルの概要
このコミットは、Go言語の標準ライブラリである net/http
パッケージにおける ErrBodyReadAfterClose
エラーのテキストと使用箇所を修正するものです。具体的には、エラーメッセージをより汎用的にし、リクエストボディだけでなくレスポンスボディにも適用可能であることを明確にするとともに、これまで汎用的なエラーメッセージが使われていた箇所でこの特定のエラーを使用するように変更しています。
コミット
commit 4f3dd833e397cc16b3170b486a9239f369e03620
Author: Russ Cox <rsc@golang.org>
Date: Mon Dec 10 01:42:10 2012 -0500
net/http: fix text for ErrBodyReadAfterClose
Can happen in both request and response.
Also use it in one place that wasn't.
Fixes #3997.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/6903057
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4f3dd833e397cc16b3170b486a9239f369e03620
元コミット内容
net/http: fix text for ErrBodyReadAfterClose
Can happen in both request and response.
Also use it in one place that wasn't.
Fixes #3997.
変更の背景
このコミットの背景には、HTTPリクエストまたはレスポンスのボディが既に閉じられた後に読み取り操作が行われた際に発生するエラーのハンドリングとメッセージングの改善があります。
元々、net/http
パッケージには ErrBodyReadAfterClose
というエラーが存在しましたが、そのエラーメッセージのテキストが「リクエストボディ」に特化しており、また、エラーが発生しうる全ての箇所で適切に使用されていませんでした。
具体的には、以下の点が問題として認識されていました。
- エラーメッセージの汎用性不足:
ErrBodyReadAfterClose
のエラーメッセージが「closed request Body」と記述されており、これがリクエストボディに限定されるかのような印象を与えていました。しかし、同様の状況はレスポンスボディでも発生しうるため、より汎用的な表現が求められていました。 - エラーの一貫性の欠如:
expectContinueReader
のような特定のコンテキストでは、ボディが閉じられた後の読み取りに対してerrors.New("http: Read after Close on request Body")
のような汎用的なエラーが生成されており、既存のErrBodyReadAfterClose
が再利用されていませんでした。これにより、エラーハンドリングのロジックが複雑になったり、エラーメッセージの一貫性が損なわれたりする可能性がありました。 - 発生シナリオの網羅性不足:
ErrBodyReadAfterClose
のコメントには、エラーが発生する典型的なシナリオとして「HTTPハンドラがWriteHeader
またはWrite
を呼び出した後」が挙げられていましたが、クライアントがリクエストボディを閉じた場合など、他の重要なシナリオが明示されていませんでした。
これらの問題に対処し、net/http
パッケージのエラーハンドリングをより堅牢で分かりやすくするために、このコミットが作成されました。特に、Fixes #3997
とあることから、このコミットは特定のバグ報告や改善提案に対応するものであることが示唆されます。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびHTTPプロトコルに関する基本的な知識が必要です。
-
Go言語のエラーハンドリング:
- Goでは、エラーは
error
インターフェースを実装する値として返されます。 errors.New()
は、新しいエラー値を生成するための標準的な関数です。- カスタムエラー型や、既存のエラー変数を再利用することで、エラーの比較やハンドリングをより効率的に行うことができます。このコミットでは、既存の
ErrBodyReadAfterClose
変数を再利用することで、エラーの一貫性を高めています。
- Goでは、エラーは
-
HTTPプロトコルにおけるボディの読み書き:
- HTTPリクエストとレスポンスは、ヘッダーとボディで構成されます。
- ボディは、リクエストの場合はクライアントからサーバーへ、レスポンスの場合はサーバーからクライアントへデータを転送するために使用されます。
- Goの
net/http
パッケージでは、リクエストボディはhttp.Request.Body
(型はio.ReadCloser
)、レスポンスボディはhttp.Response.Body
(型はio.ReadCloser
) として扱われます。 io.ReadCloser
インターフェースは、Read
メソッドとClose
メソッドを持ちます。Read
はボディからデータを読み取り、Close
はボディを閉じます。- HTTP/1.1では、リクエストやレスポンスのボディは一度読み取りを開始すると、通常は最後まで読み取るか、明示的に閉じる必要があります。一度閉じられたボディに対して再度読み取り操作を行うことは、プロトコル上不正な操作と見なされます。
-
Expect: 100-continue
ヘッダー:- HTTP/1.1の
Expect: 100-continue
ヘッダーは、クライアントが大きなリクエストボディを送信する前に、サーバーがリクエストを受け入れる準備ができているかを確認するために使用されます。 - クライアントはまずヘッダーのみを送信し、サーバーが
100 Continue
レスポンスを返した場合にのみ、ボディの送信を開始します。これにより、サーバーがリクエストを拒否する場合に、不要なボディのアップロードを防ぐことができます。 net/http
パッケージのexpectContinueReader
は、このExpect: 100-continue
ヘッダーの処理に関連する内部的なリーダーです。
- HTTP/1.1の
-
net/http
パッケージの構造:net/http
パッケージは、HTTPクライアントとサーバーの実装を提供します。server.go
はHTTPサーバーのコアロジックを含み、リクエストの処理やレスポンスの生成を担当します。transfer.go
は、HTTPメッセージの転送エンコーディング(チャンク転送など)やボディの読み書きに関する低レベルのロジックを含みます。ErrBodyReadAfterClose
の定義はこのファイルにあります。
これらの知識があることで、コミットがなぜ行われたのか、そしてその変更が net/http
パッケージの全体的な動作にどのように影響するかをより深く理解できます。
技術的詳細
このコミットは、Go言語の net/http
パッケージにおける ErrBodyReadAfterClose
エラーの定義と使用方法を改善しています。
ErrBodyReadAfterClose
の定義の変更 (src/pkg/net/http/transfer.go
)
src/pkg/net/http/transfer.go
ファイルでは、ErrBodyReadAfterClose
エラー変数の定義とそのコメントが変更されています。
変更前:
// ErrBodyReadAfterClose is returned when reading a Request Body after
// the body has been closed. This typically happens when the body is
// read after an HTTP Handler calls WriteHeader or Write on its
// ResponseWriter.
var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed request Body")
変更後:
// ErrBodyReadAfterClose is returned when reading a Request or Response
// Body after the body has been closed. This typically happens when the body is
// read after an HTTP Handler calls WriteHeader or Write on its ResponseWriter,
// or when the client closes the request body.
var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed Body")
この変更の技術的ポイントは以下の通りです。
-
コメントの修正:
Request Body
からRequest or Response Body
へと変更され、このエラーがリクエストボディだけでなくレスポンスボディにも適用されることを明確にしています。これは、HTTP通信においてボディの読み取りが閉じられた後に発生する問題が、リクエストとレスポンスの両方で起こりうるという現実を反映しています。- エラーが発生する典型的なシナリオとして、「HTTPハンドラが
WriteHeader
またはWrite
を呼び出した後」に加えて、「or when the client closes the request body.
(またはクライアントがリクエストボディを閉じた場合)」という記述が追加されました。これにより、エラーの発生条件がより網羅的に説明されています。クライアントがリクエストボディの送信を途中で中断したり、接続を閉じたりした場合にもこのエラーが発生しうることを示唆しています。
-
エラーメッセージ文字列の修正:
errors.New("http: invalid Read on closed request Body")
からerrors.New("http: invalid Read on closed Body")
へと変更されています。- エラーメッセージから「request」という単語が削除され、より汎用的な「closed Body」という表現になりました。これにより、エラーメッセージ自体がリクエストとレスポンスの両方のボディに適用可能となり、コメントの変更と一貫性が保たれています。
expectContinueReader
での ErrBodyReadAfterClose
の使用 (src/pkg/net/http/server.go
)
src/pkg/net/http/server.go
ファイルでは、expectContinueReader
の Read
メソッド内で、ボディが既に閉じられている場合の戻り値が変更されています。
変更前:
func (ecr *expectContinueReader) Read(p []byte) (n int, err error) {
if ecr.closed {
return 0, errors.New("http: Read after Close on request Body")
}
// ...
}
変更後:
func (ecr *expectContinueReader) Read(p []byte) (n int, err error) {
if ecr.closed {
return 0, ErrBodyReadAfterClose
}
// ...
}
この変更の技術的ポイントは以下の通りです。
- エラーの一貫性:
- これまで
expectContinueReader
がボディが閉じられた後に読み取りが行われた際に返していた汎用的なerrors.New("http: Read after Close on request Body")
が、既存のErrBodyReadAfterClose
変数に置き換えられました。 - これにより、
net/http
パッケージ全体で「ボディが閉じられた後の読み取り」という特定のエラー条件に対して、一貫したエラー値が返されるようになります。これは、エラーハンドリングを行う側が、エラーの型や値に基づいてより正確な処理を記述できるようになるため、コードの堅牢性と保守性を向上させます。 expectContinueReader
はHTTPリクエストボディの処理に関連するため、この変更はリクエストボディのコンテキストにおけるエラーハンドリングの改善に直接貢献します。
- これまで
これらの変更は、net/http
パッケージのエラー報告の精度と一貫性を高め、開発者がHTTPボディの読み取りに関する問題をより効果的に診断し、対処できるようにすることを目的としています。
コアとなるコードの変更箇所
このコミットによるコードの変更は、以下の2つのファイルに集中しています。
-
src/pkg/net/http/server.go
:expectContinueReader
型のRead
メソッド内でのエラー返却ロジックが変更されました。- 具体的には、
ecr.closed
がtrue
の場合に返されるエラーが、errors.New("http: Read after Close on request Body")
からErrBodyReadAfterClose
に変更されました。
-
src/pkg/net/http/transfer.go
:ErrBodyReadAfterClose
変数の定義とそのコメントが変更されました。- コメントは、このエラーがリクエストボディとレスポンスボディの両方に適用されること、およびエラーが発生する新たなシナリオ(クライアントがリクエストボディを閉じた場合)を説明するように更新されました。
- エラーメッセージの文字列自体も、「request」という単語が削除され、より汎用的な「http: invalid Read on closed Body」に変更されました。
コアとなるコードの解説
src/pkg/net/http/server.go
の変更
func (ecr *expectContinueReader) Read(p []byte) (n int, err error) {
if ecr.closed {
- return 0, errors.New("http: Read after Close on request Body")
+ return 0, ErrBodyReadAfterClose
}
if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked() {
ecr.resp.wroteContinue = true
この変更は、expectContinueReader
という内部的なリーダーの Read
メソッドにあります。expectContinueReader
は、HTTP/1.1の Expect: 100-continue
ヘッダーを処理する際に使用される可能性のあるリクエストボディのリーダーです。
ecr.closed
は、このリーダーが既に閉じられているかどうかを示すフラグです。- 変更前は、リーダーが閉じられた後に
Read
が呼び出された場合、errors.New("http: Read after Close on request Body")
という新しいエラーがその場で生成されて返されていました。 - 変更後は、代わりに
ErrBodyReadAfterClose
という、net/http/transfer.go
で定義されている既存のエラー変数が返されるようになりました。
この変更の目的は、エラーの一貫性を確保することです。同じ意味を持つエラー条件に対して、常に同じエラー値(ErrBodyReadAfterClose
)を返すことで、エラーハンドリングを行う側が == ErrBodyReadAfterClose
のようにエラー値を直接比較して、特定の状況を正確に検出できるようになります。これにより、エラー処理のロジックが簡素化され、堅牢性が向上します。
src/pkg/net/http/transfer.go
の変更
type body struct {
res *response // response writer for server requests, else nil
}
-// ErrBodyReadAfterClose is returned when reading a Request Body after
-// the body has been closed. This typically happens when the body is
+// ErrBodyReadAfterClose is returned when reading a Request or Response
+// Body after the body has been closed. This typically happens when the body is
// read after an HTTP Handler calls WriteHeader or Write on its
-// ResponseWriter.
-var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed request Body")
+// ResponseWriter, or when the client closes the request body.
+var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed Body")
func (b *body) Read(p []byte) (n int, err error) {
if b.closed {
この変更は、ErrBodyReadAfterClose
エラー変数の定義自体とその関連コメントにあります。
-
コメントの変更:
- 変更前は「Request Body」に限定されていた説明が、「Request or Response Body」に拡張されました。これは、このエラーがリクエストボディだけでなく、HTTPレスポンスボディが閉じられた後に読み取られた場合にも適用されることを明確にしています。
- エラーが発生する典型的なシナリオとして、「HTTPハンドラが
WriteHeader
またはWrite
を呼び出した後」に加えて、「またはクライアントがリクエストボディを閉じた場合」という新たなシナリオが追加されました。これにより、エラーの発生条件がより包括的に説明され、開発者が様々な状況でこのエラーを予期できるようになります。
-
エラーメッセージ文字列の変更:
errors.New("http: invalid Read on closed request Body")
からerrors.New("http: invalid Read on closed Body")
へと変更されました。- エラーメッセージから「request」という単語が削除され、より汎用的な「closed Body」という表現になりました。これにより、エラーメッセージ自体がリクエストとレスポンスの両方のボディに適用可能となり、コメントの変更と完全に一致します。
これらの変更は、ErrBodyReadAfterClose
の意味と適用範囲を明確にし、net/http
パッケージ全体でエラーメッセージとエラーハンドリングの一貫性を向上させることを目的としています。これにより、デバッグが容易になり、より堅牢なHTTPアプリケーションの構築に貢献します。
関連リンク
- Go言語の
net/http
パッケージのドキュメント: https://pkg.go.dev/net/http - Go言語のエラーハンドリングに関する公式ドキュメントやブログ記事(一般的な情報源として)
参考にした情報源リンク
- コミット情報:
./commit_data/14590.txt
の内容 - GitHub上のコミットページ: https://github.com/golang/go/commit/4f3dd833e397cc16b3170b486a9239f369e03620
- Go言語の公式ドキュメント (net/httpパッケージ、errorsパッケージなど)
- HTTP/1.1 RFC (特に
Expect: 100-continue
ヘッダーに関するセクション) - 一般的なGo言語のエラーハンドリングのプラクティスに関する知識