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

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

このコミットは、Go言語の標準ライブラリ net/http パッケージにおける http.NewRequest 関数のドキュメントを更新するものです。具体的には、NewRequest に渡される io.Reader 型のボディが、もし io.Closer インターフェースも実装している場合に、その io.Closer として扱われ、http.Client のメソッド(Do, Post, PostForm)や http.Transport.RoundTrip によって適切にクローズされるという重要な挙動を明記しています。これは、リソースリークを防ぎ、HTTPリクエストのボディ処理をより堅牢にするための、利用者に向けた注意喚起と仕様の明確化を目的としています。

コミット

net/http: document NewRequest treating Reader as ReadCloser

R=golang-dev, dsymonds, rogpeppe
CC=golang-dev
https://golang.org/cl/11432044

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

https://github.com/golang/go/commit/4aa6d353063aff777a9bdd3d3cedea5ca3fed2a4

元コミット内容

commit 4aa6d353063aff777a9bdd3d3cedea5ca3fed2a4
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Thu Jul 18 10:10:10 2013 +1000

    net/http: document NewRequest treating Reader as ReadCloser
    
    R=golang-dev, dsymonds, rogpeppe
    CC=golang-dev
    https://golang.org/cl/11432044
---
 src/pkg/net/http/request.go | 4 ++++\n 1 file changed, 4 insertions(+)\n

変更の背景

Go言語の net/http パッケージは、HTTPクライアントとサーバーを構築するための基本的な機能を提供します。http.NewRequest 関数は、HTTPリクエストを作成する際に使用され、リクエストボディとして io.Reader インターフェースを受け取ります。しかし、HTTPリクエストのボディは、通常、送信後にクローズされるべきリソース(例: ファイル、ネットワーク接続)であることが多く、io.Readerio.Closer インターフェースも実装している場合、そのリソースを適切に解放する必要があります。

このコミット以前は、NewRequest に渡された io.Readerio.Closer でもある場合に、http.Client がそのボディを自動的にクローズするという挙動が明示的にドキュメント化されていませんでした。この不明瞭さは、開発者がリソースリークを引き起こす可能性のあるコードを誤って記述するリスクをはらんでいました。例えば、os.Open で開いたファイルをリクエストボディとして渡した場合、明示的に Close() を呼び出さなければファイルハンドルが解放されず、リソースリークにつながります。

このコミットの目的は、この重要な挙動を公式ドキュメントに明記することで、開発者が net/http パッケージをより安全かつ効率的に利用できるようにすることです。これにより、リソース管理に関する混乱を解消し、より堅牢なアプリケーションの構築を促進します。

前提知識の解説

このコミットの理解には、以下のGo言語の基本的な概念と net/http パッケージの知識が必要です。

  • io.Reader インターフェース: Go言語における基本的なインターフェースの一つで、データを読み出す操作を抽象化します。Read(p []byte) (n int, err error) メソッドを持ち、バイトスライス p にデータを読み込み、読み込んだバイト数 n とエラー err を返します。ファイル、ネットワーク接続、メモリ上のバッファなど、様々なデータソースからの読み出しを統一的に扱えます。

  • io.Closer インターフェース: これもGo言語の基本的なインターフェースで、リソースをクローズする操作を抽象化します。Close() error メソッドを持ち、リソースの解放やクリーンアップを行います。ファイルハンドル、ネットワークソケット、データベース接続など、使用後に明示的に解放する必要があるリソースに適用されます。

  • io.ReadCloser インターフェース: io.Readerio.Closer の両方のメソッドを持つインターフェースです。つまり、読み出しとクローズの両方が可能なリソースを表します。

  • net/http パッケージ: Go言語の標準ライブラリで、HTTPクライアントとサーバーの機能を提供します。HTTPリクエストの送信、HTTPレスポンスの受信、HTTPサーバーの構築など、Webアプリケーション開発の基盤となります。

  • http.Request 構造体: HTTPリクエストを表す構造体です。メソッド(GET, POSTなど)、URL、ヘッダー、ボディなどの情報を含みます。Body フィールドは io.ReadCloser 型であり、リクエストボディのデータを読み出すためのインターフェースを提供します。

  • http.NewRequest 関数: 新しい http.Request オブジェクトを作成するための関数です。NewRequest(method, urlStr string, body io.Reader) (*Request, error) のシグネチャを持ち、HTTPメソッド、URL文字列、そしてオプションでリクエストボディ(io.Reader 型)を受け取ります。

  • http.Client 構造体と Do, Post, PostForm メソッド: http.Client はHTTPリクエストを送信するためのクライアントです。

    • Do(req *Request) (*Response, error): 任意の http.Request を送信します。
    • Post(url, contentType string, body io.Reader) (*Response, error): POSTリクエストを送信します。
    • PostForm(url string, data url.Values) (*Response, error): フォームデータをエンコードしてPOSTリクエストを送信します。 これらのメソッドは、リクエストボディが io.Closer を実装している場合、リクエスト送信後にそのボディを自動的にクローズする責任を負います。

技術的詳細

このコミットが追加するドキュメントは、Goのインターフェースの強力な特性である「ダックタイピング(Duck Typing)」に基づいています。Goでは、型が特定のインターフェースを実装しているかどうかは、その型がインターフェースのすべてのメソッドを持っているかどうかによって決定されます。明示的な宣言は必要ありません。

http.NewRequest 関数は、リクエストボディとして io.Reader 型の引数 body を受け取ります。しかし、内部的には、NewRequest はこの body 引数が io.Closer インターフェースも実装しているかどうかをチェックします。もし実装していれば、Request.Body フィールド(これは io.ReadCloser 型)に、元の body 引数をそのまま設定します。

この挙動の重要性は、http.ClientDo, Post, PostForm メソッド、および http.Transport.RoundTrip メソッドにあります。これらのメソッドは、HTTPリクエストを実際に送信する際に、Request.Body フィールドが nil でなく、かつ io.Closer インターフェースを実装している場合、リクエストの処理が完了した後に自動的に Close() メソッドを呼び出します。

したがって、開発者が os.Filebytes.Buffer のような io.Reader かつ io.Closer であるオブジェクトを NewRequest に渡した場合、http.Client がそのリソースを自動的にクローズしてくれるため、明示的に body.Close() を呼び出す必要がなくなります。これは、リソース管理の簡素化と、リソースリークの防止に貢献します。

このドキュメントの追加は、この暗黙的な(しかし重要な)挙動を明示的にすることで、APIの透明性を高め、開発者がより安全で効率的なコードを書けるようにするためのものです。特に、ファイルやネットワークストリームなど、使用後にクローズが必要なリソースをリクエストボディとして扱う場合に、この情報が不可欠となります。

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

変更は src/pkg/net/http/request.go ファイルの NewRequest 関数のドキュメントコメントにあります。

--- a/src/pkg/net/http/request.go
+++ b/src/pkg/net/http/request.go
@@ -424,6 +424,10 @@ func ParseHTTPVersion(vers string) (major, minor int, ok bool) {
 }
 
 // NewRequest returns a new Request given a method, URL, and optional body.
+//
+// If the provided body is also an io.Closer, the returned
+// Request.Body is set to body and will be closed by the Client
+// methods Do, Post, and PostForm, and Transport.RoundTrip.
 func NewRequest(method, urlStr string, body io.Reader) (*Request, error) {
 	u, err := url.Parse(urlStr)
 	if err != nil {

コアとなるコードの解説

追加された4行のコメントは、NewRequest 関数の既存のドキュメントに追記されたものです。

// If the provided body is also an io.Closer, the returned
// Request.Body is set to body and will be closed by the Client
// methods Do, Post, and PostForm, and Transport.RoundTrip.

このコメントは以下の点を明確にしています。

  1. 条件: NewRequest に渡される body 引数(io.Reader 型)が、同時に io.Closer インターフェースも実装している場合。
  2. 結果: 生成される http.Request オブジェクトの Body フィールドは、渡された body そのものに設定されます。これは、Request.Bodyio.ReadCloser 型であるため、io.Readerio.Closer の両方を満たすオブジェクトであればそのまま代入できるためです。
  3. 自動クローズ: その Request.Body は、http.ClientDo, Post, PostForm メソッド、および http.Transport.RoundTrip によって自動的にクローズされます。これは、これらのメソッドがリクエスト処理の完了後に Request.BodyClose() メソッドを呼び出す責任を負っているためです。

このドキュメントの追加により、開発者は NewRequest に渡す io.Readerio.Closer でもある場合に、手動で Close() を呼び出す必要がないことを明確に理解できるようになります。これにより、コードの記述が簡素化され、リソースリークの可能性が低減されます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (pkg.go.dev)
  • Go言語のソースコード (github.com/golang/go)
  • Go言語のインターフェースに関する一般的な知識
  • HTTPプロトコルにおけるリクエストボディの扱いに関する一般的な知識