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

[インデックス 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 というエラーが存在しましたが、そのエラーメッセージのテキストが「リクエストボディ」に特化しており、また、エラーが発生しうる全ての箇所で適切に使用されていませんでした。

具体的には、以下の点が問題として認識されていました。

  1. エラーメッセージの汎用性不足: ErrBodyReadAfterClose のエラーメッセージが「closed request Body」と記述されており、これがリクエストボディに限定されるかのような印象を与えていました。しかし、同様の状況はレスポンスボディでも発生しうるため、より汎用的な表現が求められていました。
  2. エラーの一貫性の欠如: expectContinueReader のような特定のコンテキストでは、ボディが閉じられた後の読み取りに対して errors.New("http: Read after Close on request Body") のような汎用的なエラーが生成されており、既存の ErrBodyReadAfterClose が再利用されていませんでした。これにより、エラーハンドリングのロジックが複雑になったり、エラーメッセージの一貫性が損なわれたりする可能性がありました。
  3. 発生シナリオの網羅性不足: ErrBodyReadAfterClose のコメントには、エラーが発生する典型的なシナリオとして「HTTPハンドラが WriteHeader または Write を呼び出した後」が挙げられていましたが、クライアントがリクエストボディを閉じた場合など、他の重要なシナリオが明示されていませんでした。

これらの問題に対処し、net/http パッケージのエラーハンドリングをより堅牢で分かりやすくするために、このコミットが作成されました。特に、Fixes #3997 とあることから、このコミットは特定のバグ報告や改善提案に対応するものであることが示唆されます。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびHTTPプロトコルに関する基本的な知識が必要です。

  1. Go言語のエラーハンドリング:

    • Goでは、エラーは error インターフェースを実装する値として返されます。
    • errors.New() は、新しいエラー値を生成するための標準的な関数です。
    • カスタムエラー型や、既存のエラー変数を再利用することで、エラーの比較やハンドリングをより効率的に行うことができます。このコミットでは、既存の ErrBodyReadAfterClose 変数を再利用することで、エラーの一貫性を高めています。
  2. HTTPプロトコルにおけるボディの読み書き:

    • HTTPリクエストとレスポンスは、ヘッダーとボディで構成されます。
    • ボディは、リクエストの場合はクライアントからサーバーへ、レスポンスの場合はサーバーからクライアントへデータを転送するために使用されます。
    • Goの net/http パッケージでは、リクエストボディは http.Request.Body (型は io.ReadCloser)、レスポンスボディは http.Response.Body (型は io.ReadCloser) として扱われます。
    • io.ReadCloser インターフェースは、Read メソッドと Close メソッドを持ちます。Read はボディからデータを読み取り、Close はボディを閉じます。
    • HTTP/1.1では、リクエストやレスポンスのボディは一度読み取りを開始すると、通常は最後まで読み取るか、明示的に閉じる必要があります。一度閉じられたボディに対して再度読み取り操作を行うことは、プロトコル上不正な操作と見なされます。
  3. Expect: 100-continue ヘッダー:

    • HTTP/1.1の Expect: 100-continue ヘッダーは、クライアントが大きなリクエストボディを送信する前に、サーバーがリクエストを受け入れる準備ができているかを確認するために使用されます。
    • クライアントはまずヘッダーのみを送信し、サーバーが 100 Continue レスポンスを返した場合にのみ、ボディの送信を開始します。これにより、サーバーがリクエストを拒否する場合に、不要なボディのアップロードを防ぐことができます。
    • net/http パッケージの expectContinueReader は、この Expect: 100-continue ヘッダーの処理に関連する内部的なリーダーです。
  4. 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")

この変更の技術的ポイントは以下の通りです。

  1. コメントの修正:

    • Request Body から Request or Response Body へと変更され、このエラーがリクエストボディだけでなくレスポンスボディにも適用されることを明確にしています。これは、HTTP通信においてボディの読み取りが閉じられた後に発生する問題が、リクエストとレスポンスの両方で起こりうるという現実を反映しています。
    • エラーが発生する典型的なシナリオとして、「HTTPハンドラが WriteHeader または Write を呼び出した後」に加えて、「or when the client closes the request body. (またはクライアントがリクエストボディを閉じた場合)」という記述が追加されました。これにより、エラーの発生条件がより網羅的に説明されています。クライアントがリクエストボディの送信を途中で中断したり、接続を閉じたりした場合にもこのエラーが発生しうることを示唆しています。
  2. エラーメッセージ文字列の修正:

    • 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 ファイルでは、expectContinueReaderRead メソッド内で、ボディが既に閉じられている場合の戻り値が変更されています。

変更前:

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
    }
    // ...
}

この変更の技術的ポイントは以下の通りです。

  1. エラーの一貫性:
    • これまで expectContinueReader がボディが閉じられた後に読み取りが行われた際に返していた汎用的な errors.New("http: Read after Close on request Body") が、既存の ErrBodyReadAfterClose 変数に置き換えられました。
    • これにより、net/http パッケージ全体で「ボディが閉じられた後の読み取り」という特定のエラー条件に対して、一貫したエラー値が返されるようになります。これは、エラーハンドリングを行う側が、エラーの型や値に基づいてより正確な処理を記述できるようになるため、コードの堅牢性と保守性を向上させます。
    • expectContinueReader はHTTPリクエストボディの処理に関連するため、この変更はリクエストボディのコンテキストにおけるエラーハンドリングの改善に直接貢献します。

これらの変更は、net/http パッケージのエラー報告の精度と一貫性を高め、開発者がHTTPボディの読み取りに関する問題をより効果的に診断し、対処できるようにすることを目的としています。

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

このコミットによるコードの変更は、以下の2つのファイルに集中しています。

  1. src/pkg/net/http/server.go:

    • expectContinueReader 型の Read メソッド内でのエラー返却ロジックが変更されました。
    • 具体的には、ecr.closedtrue の場合に返されるエラーが、errors.New("http: Read after Close on request Body") から ErrBodyReadAfterClose に変更されました。
  2. 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言語のエラーハンドリングのプラクティスに関する知識