[インデックス 16817] ファイルの概要
このコミットは、Go言語の標準ライブラリ net/http
パッケージにおける Client.Post
メソッドの挙動、特にリクエストボディが io.Reader
かつ io.Closer
の両方を実装している場合の扱いについて、ドキュメントの明確化とテストの追加を行ったものです。これにより、net/http
クライアントがリクエストボディを適切にクローズする責任があることが明示され、その動作が保証されるようになりました。
コミット
commit b2b15d1230f3b4f669b5dd171c31e1b081bffede
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Fri Jul 19 12:02:54 2013 +1000
net/http: document and test Client.Post treating Reader body as ReadCloser
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/11542044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b2b15d1230f3b4f669b5dd171c31e1b081bffede
元コミット内容
net/http: document and test Client.Post treating Reader body as ReadCloser
このコミットは、net/http
パッケージの Client.Post
関数が、io.Reader
として渡されたリクエストボディが io.Closer
インターフェースも実装している場合に、そのボディを ReadCloser
として扱い、適切にクローズすることについて、ドキュメントを整備し、テストを追加するものです。
変更の背景
Goの net/http
パッケージでは、HTTPリクエストのボディとして io.Reader
インターフェースを実装する任意の型を受け入れることができます。しかし、ネットワーク操作においては、リソースのリークを防ぐために、読み取りが完了したストリームは適切にクローズされる必要があります。特に、リクエストボディがファイルやネットワーク接続など、クローズが必要なリソースを表す場合、そのクローズの責任がどこにあるのかが不明確であると、リソースリークや予期せぬ動作につながる可能性があります。
このコミット以前は、Client.Post
に渡された io.Reader
が io.Closer
も実装している場合に、net/http
パッケージがその Close
メソッドを呼び出すかどうかの挙動が明示的にドキュメント化されていませんでした。また、RoundTripper
インターフェースのドキュメントも、リクエストボディの「消費」については言及していましたが、「クローズ」については明確ではありませんでした。
この曖昧さを解消し、開発者が安心して net/http
クライアントを使用できるようにするため、以下の点が課題となっていました。
- ドキュメントの不足:
Client.Post
がio.Closer
を実装するボディをどのように扱うかについての公式な説明がなかった。 - 責任の不明確さ:
RoundTripper
がリクエストボディをクローズする責任を負うべきかどうかが不明確だった。 - テストの不足: この重要な挙動を保証するための自動テストが存在しなかった。
このコミットは、これらの課題に対処し、net/http
パッケージの堅牢性と使いやすさを向上させることを目的としています。
前提知識の解説
このコミットを理解するためには、以下のGo言語の基本的なインターフェースと net/http
パッケージの概念を理解しておく必要があります。
-
io.Reader
インターフェース:Read(p []byte) (n int, err error)
メソッドを持つインターフェースです。データを読み出すための一般的な抽象化を提供します。ファイル、ネットワーク接続、メモリ上のバイト列など、様々なデータソースからデータを読み出す際に使用されます。 -
io.Closer
インターフェース:Close() error
メソッドを持つインターフェースです。リソース(ファイルハンドル、ネットワークソケットなど)を解放するための一般的な抽象化を提供します。io.Reader
やio.Writer
と組み合わせて使用されることが多く、リソースのリークを防ぐために重要です。 -
io.ReadCloser
インターフェース:io.Reader
とio.Closer
の両方を埋め込んだインターフェースです。読み取りとクローズの両方の機能を持つリソースを表します。 -
net/http
パッケージ: Go言語の標準ライブラリで、HTTPクライアントとサーバーの実装を提供します。WebアプリケーションやAPIクライアントを構築する上で不可欠なパッケージです。 -
net/http.Client
: HTTPリクエストを送信するためのクライアント構造体です。Get
,Post
,Do
などのメソッドを提供し、HTTP通信を行います。 -
net/http.Request
: HTTPリクエストを表す構造体です。Body
フィールドはio.ReadCloser
型であり、リクエストボディのデータを保持します。 -
net/http.RoundTripper
インターフェース:RoundTrip(*Request) (*Response, error)
メソッドを持つインターフェースです。HTTPリクエストを送信し、HTTPレスポンスを受信する単一のHTTPトランザクションを表します。net/http.Client
は内部的にRoundTripper
を使用してリクエストを処理します。カスタムのRoundTripper
を実装することで、リクエストの変更、認証、プロキシ設定などの高度な処理を行うことができます。
技術的詳細
このコミットの技術的な核心は、net/http
パッケージがリクエストボディの io.Closer
インターフェースをどのように尊重し、いつ Close()
メソッドを呼び出すかという点にあります。
HTTPリクエストのボディは、net/http.Client.Post
メソッドに io.Reader
として渡されます。しかし、この io.Reader
が同時に io.Closer
インターフェースも実装している場合(つまり io.ReadCloser
である場合)、net/http
パッケージは、ボディのデータがサーバーに正常に書き込まれた後に、その Close()
メソッドを呼び出す責任を負います。これは、ファイルやネットワークストリームなど、基になるリソースを適切に解放するために不可欠です。
この挙動は、net/http.Client
が内部で使用する net/http.RoundTripper
インターフェースの実装によって処理されます。RoundTripper
の役割は、リクエストを送信し、レスポンスを受け取るだけでなく、リクエストボディの消費とクローズも含まれるべきであると、このコミットによって明確にされました。
具体的には、以下の変更が行われました。
-
RoundTripper
インターフェースのドキュメント更新:RoundTripper
のRoundTrip
メソッドのドキュメントに、「リクエストのBodyを消費し、クローズする」という記述が追加されました。これにより、RoundTripper
の実装者がリクエストボディのクローズ責任を負うことが明確になりました。 -
Client.Post
メソッドのドキュメント更新:Client.Post
メソッドのドキュメントに、「提供されたボディがio.Closer
でもある場合、ボディがサーバーに正常に書き込まれた後にクローズされる」という記述が追加されました。これは、Client.Post
を使用する開発者に対して、この自動クローズ挙動を明示的に通知するものです。 -
テストケースの追加:
TestTransportClosesRequestBody
という新しいテスト関数がsrc/pkg/net/http/transport_test.go
に追加されました。このテストは、カスタムのcountCloseReader
型(io.Reader
とio.Closer
を実装し、Close()
が呼ばれるとカウンタをインクリメントする)を使用して、Client.Post
が実際にボディのClose()
メソッドを呼び出すことを検証します。これにより、将来の変更によってこの重要な挙動が損なわれることを防ぎます。
これらの変更により、net/http
パッケージは、リクエストボディのライフサイクル管理においてより予測可能で堅牢な挙動を提供するようになりました。開発者は、io.Closer
を実装するリクエストボディを Client.Post
に渡す際に、明示的に Close()
を呼び出す必要がなくなり、リソースリークのリスクが低減されます。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は以下の2つのファイルです。
-
src/pkg/net/http/client.go
RoundTripper
インターフェースのコメントが変更されました。Client.Post
メソッドのコメントが追加されました。
-
src/pkg/net/http/transport_test.go
TestTransportClosesRequestBody
という新しいテスト関数が追加されました。countCloseReader
というヘルパー型が追加されました。
src/pkg/net/http/client.go
の変更点
--- a/src/pkg/net/http/client.go
+++ b/src/pkg/net/http/client.go
@@ -74,8 +74,8 @@ type RoundTripper interface {
// authentication, or cookies.
//
// RoundTrip should not modify the request, except for
- // consuming the Body. The request's URL and Header fields
- // are guaranteed to be initialized.
+ // consuming and closing the Body. The request's URL and
+ // Header fields are guaranteed to be initialized.
RoundTrip(*Request) (*Response, error)
}
@@ -346,6 +346,9 @@ func Post(url string, bodyType string, body io.Reader) (resp *Response, err erro
// Post issues a POST to the specified URL.
//
// Caller should close resp.Body when done reading from it.
+//
+// If the provided body is also an io.Closer, it is closed after the
+// body is successfully written to the server.
func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
req, err := NewRequest("POST", url, body)
if err != nil {
src/pkg/net/http/transport_test.go
の変更点
--- a/src/pkg/net/http/transport_test.go
+++ b/src/pkg/net/http/transport_test.go
@@ -15,6 +15,7 @@ import (
"io"
"io/ioutil"
"net"
+ "net/http"
. "net/http"
"net/http/httptest"
"net/url"
@@ -1610,6 +1611,42 @@ func TestIdleConnChannelLeak(t *testing.T) {
}
}
+// Verify the status quo: that the Client.Post function coerces its
+// body into a ReadCloser if it's a Closer, and that the Transport
+// then closes it.
+func TestTransportClosesRequestBody(t *testing.T) {
+ defer afterTest(t)
+ ts := httptest.NewServer(http.HandlerFunc(func(w ResponseWriter, r *Request) {
+ io.Copy(ioutil.Discard, r.Body)
+ }))
+ defer ts.Close()
+
+ tr := &Transport{}
+ defer tr.CloseIdleConnections()
+ cl := &Client{Transport: tr}
+
+ closes := 0
+
+ res, err := cl.Post(ts.URL, "text/plain", countCloseReader{&closes, strings.NewReader("hello")})
+ if err != nil {
+ t.Fatal(err)
+ }
+ res.Body.Close()
+ if closes != 1 {
+ t.Errorf("closes = %d; want 1", closes)
+ }
+}
+
+type countCloseReader struct {
+ n *int
+ io.Reader
+}
+
+func (cr countCloseReader) Close() error {
+ (*cr.n)++
+ return nil
+}
+
// rgz is a gzip quine that uncompresses to itself.
var rgz = []byte{
0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,
コアとなるコードの解説
src/pkg/net/http/client.go
の変更点
-
RoundTripper
インターフェースのコメント修正:RoundTripper
インターフェースのRoundTrip
メソッドのドキュメントが更新されました。 変更前:// consuming the Body.
変更後:// consuming and closing the Body.
この変更は、RoundTripper
の実装がリクエストボディを単に「消費する」だけでなく、「クローズする」責任も負うことを明確にしています。これは、リソースリークを防ぐ上で非常に重要な指針となります。net/http
パッケージのデフォルトのTransport
実装は、この責任を適切に果たします。 -
Client.Post
メソッドのコメント追加:Client.Post
メソッドのドキュメントに以下の新しい行が追加されました。// If the provided body is also an io.Closer, it is closed after the // body is successfully written to the server.
このコメントは、
Client.Post
に渡されるio.Reader
がio.Closer
インターフェースも実装している場合、net/http
パッケージがそのClose()
メソッドを自動的に呼び出すことを明示しています。これは、開発者がリクエストボディとしてos.File
やbytes.Buffer
のようなio.Closer
を渡した場合に、明示的にdefer file.Close()
などと書く必要がないことを示唆しています(ただし、エラーハンドリングによっては必要になる場合もあります)。この自動クローズは、ボディのデータがサーバーに正常に書き込まれた後に実行されます。
src/pkg/net/http/transport_test.go
の変更点
-
countCloseReader
型の追加: このコミットでは、テストのためにcountCloseReader
という新しい型が定義されました。type countCloseReader struct { n *int io.Reader } func (cr countCloseReader) Close() error { (*cr.n)++ return nil }
この型は、既存の
io.Reader
をラップし、io.Closer
インターフェースも実装しています。Close()
メソッドが呼び出されるたびに、ポインタn
が指す整数カウンタをインクリメントします。これにより、テスト内でClose()
メソッドが何回呼び出されたかを追跡できます。 -
TestTransportClosesRequestBody
テスト関数の追加: この新しいテスト関数は、Client.Post
がio.Closer
を実装するリクエストボディを適切にクローズすることを確認します。httptest.NewServer
を使用して、テスト用のHTTPサーバーを起動します。このサーバーは、受信したリクエストボディを単に読み捨てます。net/http.Transport
とnet/http.Client
のインスタンスを作成します。closes
という整数変数を定義し、そのアドレスをcountCloseReader
に渡します。cl.Post
を呼び出し、リクエストボディとしてcountCloseReader
のインスタンスを渡します。res.Body.Close()
を呼び出してレスポンスボディをクローズします(これはリクエストボディのクローズとは別です)。- 最後に、
closes
変数の値が1
であることをアサートします。これは、countCloseReader
のClose()
メソッドが正確に1回呼び出されたことを意味し、net/http
パッケージがリクエストボディを適切にクローズしたことを証明します。
これらの変更は、net/http
パッケージの内部的な挙動を明確にし、その堅牢性を高めるための重要なステップです。特に、リソース管理の側面において、開発者がより安全にHTTPクライアントを使用できるようになります。
関連リンク
- Go CL (Code Review) リンク: https://golang.org/cl/11542044 このコミットの元のコードレビューページです。詳細な議論や変更履歴を確認できます。
参考にした情報源リンク
-
Go言語公式ドキュメント -
io
パッケージ: https://pkg.go.dev/ioio.Reader
,io.Closer
,io.ReadCloser
インターフェースに関する公式ドキュメントです。 -
Go言語公式ドキュメント -
net/http
パッケージ: https://pkg.go.dev/net/httpnet/http.Client
,net/http.Request
,net/http.RoundTripper
など、HTTPクライアントとサーバーに関する公式ドキュメントです。 -
Go言語公式ドキュメント -
net/http/httptest
パッケージ: https://pkg.go.dev/net/http/httptest HTTPテストサーバーの作成に使用されるhttptest.NewServer
に関する公式ドキュメントです。 -
Go言語のソースコード: この解説は、Go言語の公式リポジトリにある実際のソースコード(
src/pkg/net/http/client.go
およびsrc/pkg/net/http/transport_test.go
)を直接参照して作成されました。client.go
: https://github.com/golang/go/blob/master/src/net/http/client.gotransport_test.go
: https://github.com/golang/go/blob/master/src/net/http/transport_test.go (注: リンクは現在のmasterブランチのものです。コミット当時のコードとは若干異なる可能性がありますが、基本的な構造は同じです。)# [インデックス 16817] ファイルの概要
このコミットは、Go言語の標準ライブラリ net/http
パッケージにおける Client.Post
メソッドの挙動、特にリクエストボディが io.Reader
かつ io.Closer
の両方を実装している場合の扱いについて、ドキュメントの明確化とテストの追加を行ったものです。これにより、net/http
クライアントがリクエストボディを適切にクローズする責任があることが明示され、その動作が保証されるようになりました。
コミット
commit b2b15d1230f3b4f669b5dd171c31e1b081bffede
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Fri Jul 19 12:02:54 2013 +1000
net/http: document and test Client.Post treating Reader body as ReadCloser
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/11542044
GitHub上でのコミットページへのリンク
https://github.com/golang.com/go/commit/b2b15d1230f3b4f669b5dd171c31e1b081bffede
元コミット内容
net/http: document and test Client.Post treating Reader body as ReadCloser
このコミットは、net/http
パッケージの Client.Post
関数が、io.Reader
として渡されたリクエストボディが io.Closer
インターフェースも実装している場合に、そのボディを ReadCloser
として扱い、適切にクローズすることについて、ドキュメントを整備し、テストを追加するものです。
変更の背景
Goの net/http
パッケージでは、HTTPリクエストのボディとして io.Reader
インターフェースを実装する任意の型を受け入れることができます。しかし、ネットワーク操作においては、リソースのリークを防ぐために、読み取りが完了したストリームは適切にクローズされる必要があります。特に、リクエストボディがファイルやネットワーク接続など、クローズが必要なリソースを表す場合、そのクローズの責任がどこにあるのかが不明確であると、リソースリークや予期せぬ動作につながる可能性があります。
このコミット以前は、Client.Post
に渡された io.Reader
が io.Closer
も実装している場合に、net/http
パッケージがその Close()
メソッドを呼び出すかどうかの挙動が明示的にドキュメント化されていませんでした。また、RoundTripper
インターフェースのドキュメントも、リクエストボディの「消費」については言及していましたが、「クローズ」については明確ではありませんでした。
この曖昧さを解消し、開発者が安心して net/http
クライアントを使用できるようにするため、以下の点が課題となっていました。
- ドキュメントの不足:
Client.Post
がio.Closer
を実装するボディをどのように扱うかについての公式な説明がなかった。 - 責任の不明確さ:
RoundTripper
がリクエストボディをクローズする責任を負うべきかどうかが不明確だった。 - テストの不足: この重要な挙動を保証するための自動テストが存在しなかった。
このコミットは、これらの課題に対処し、net/http
パッケージの堅牢性と使いやすさを向上させることを目的としています。
前提知識の解説
このコミットを理解するためには、以下のGo言語の基本的なインターフェースと net/http
パッケージの概念を理解しておく必要があります。
-
io.Reader
インターフェース:Read(p []byte) (n int, err error)
メソッドを持つインターフェースです。データを読み出すための一般的な抽象化を提供します。ファイル、ネットワーク接続、メモリ上のバイト列など、様々なデータソースからデータを読み出す際に使用されます。 -
io.Closer
インターフェース:Close() error
メソッドを持つインターフェースです。リソース(ファイルハンドル、ネットワークソケットなど)を解放するための一般的な抽象化を提供します。io.Reader
やio.Writer
と組み合わせて使用されることが多く、リソースのリークを防ぐために重要です。 -
io.ReadCloser
インターフェース:io.Reader
とio.Closer
の両方を埋め込んだインターフェースです。読み取りとクローズの両方の機能を持つリソースを表します。 -
net/http
パッケージ: Go言語の標準ライブラリで、HTTPクライアントとサーバーの実装を提供します。WebアプリケーションやAPIクライアントを構築する上で不可欠なパッケージです。 -
net/http.Client
: HTTPリクエストを送信するためのクライアント構造体です。Get
,Post
,Do
などのメソッドを提供し、HTTP通信を行います。 -
net/http.Request
: HTTPリクエストを表す構造体です。Body
フィールドはio.ReadCloser
型であり、リクエストボディのデータを保持します。 -
net/http.RoundTripper
インターフェース:RoundTrip(*Request) (*Response, error)
メソッドを持つインターフェースです。HTTPリクエストを送信し、HTTPレスポンスを受信する単一のHTTPトランザクションを表します。net/http.Client
は内部的にRoundTripper
を使用してリクエストを処理します。カスタムのRoundTripper
を実装することで、リクエストの変更、認証、プロキシ設定などの高度な処理を行うことができます。
技術的詳細
このコミットの技術的な核心は、net/http
パッケージがリクエストボディの io.Closer
インターフェースをどのように尊重し、いつ Close()
メソッドを呼び出すかという点にあります。
HTTPリクエストのボディは、net/http.Client.Post
メソッドに io.Reader
として渡されます。しかし、この io.Reader
が同時に io.Closer
インターフェースも実装している場合(つまり io.ReadCloser
である場合)、net/http
パッケージは、ボディのデータがサーバーに正常に書き込まれた後に、その Close()
メソッドを呼び出す責任を負います。これは、ファイルやネットワークストリームなど、基になるリソースを適切に解放するために不可欠です。
この挙動は、net/http.Client
が内部で使用する net/http.RoundTripper
インターフェースの実装によって処理されます。RoundTripper
の役割は、リクエストを送信し、レスポンスを受け取るだけでなく、リクエストボディの消費とクローズも含まれるべきであると、このコミットによって明確にされました。
具体的には、以下の変更が行われました。
-
RoundTripper
インターフェースのドキュメント更新:RoundTripper
のRoundTrip
メソッドのドキュメントに、「リクエストのBodyを消費し、クローズする」という記述が追加されました。これにより、RoundTripper
の実装者がリクエストボディのクローズ責任を負うことが明確になりました。 -
Client.Post
メソッドのドキュメント更新:Client.Post
メソッドのドキュメントに、「提供されたボディがio.Closer
でもある場合、ボディがサーバーに正常に書き込まれた後にクローズされる」という記述が追加されました。これは、Client.Post
を使用する開発者に対して、この自動クローズ挙動を明示的に通知するものです。 -
テストケースの追加:
TestTransportClosesRequestBody
という新しいテスト関数がsrc/pkg/net/http/transport_test.go
に追加されました。このテストは、カスタムのcountCloseReader
型(io.Reader
とio.Closer
を実装し、Close()
が呼ばれるとカウンタをインクリメントする)を使用して、Client.Post
が実際にボディのClose()
メソッドを呼び出すことを検証します。これにより、将来の変更によってこの重要な挙動が損なわれることを防ぎます。
これらの変更により、net/http
パッケージは、リクエストボディのライフサイクル管理においてより予測可能で堅牢な挙動を提供するようになりました。開発者は、io.Closer
を実装するリクエストボディを Client.Post
に渡す際に、明示的に Close()
を呼び出す必要がなくなり、リソースリークのリスクが低減されます。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は以下の2つのファイルです。
-
src/pkg/net/http/client.go
RoundTripper
インターフェースのコメントが変更されました。Client.Post
メソッドのコメントが追加されました。
-
src/pkg/net/http/transport_test.go
TestTransportClosesRequestBody
という新しいテスト関数が追加されました。countCloseReader
というヘルパー型が追加されました。
src/pkg/net/http/client.go
の変更点
--- a/src/pkg/net/http/client.go
+++ b/src/pkg/net/http/client.go
@@ -74,8 +74,8 @@ type RoundTripper interface {
// authentication, or cookies.
//
// RoundTrip should not modify the request, except for
- // consuming the Body. The request's URL and Header fields
- // are guaranteed to be initialized.
+ // consuming and closing the Body. The request's URL and
+ // Header fields are guaranteed to be initialized.
RoundTrip(*Request) (*Response, error)
}
@@ -346,6 +346,9 @@ func Post(url string, bodyType string, body io.Reader) (resp *Response, err erro
// Post issues a POST to the specified URL.
//
// Caller should close resp.Body when done reading from it.
+//
+// If the provided body is also an io.Closer, it is closed after the
+// body is successfully written to the server.
func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
req, err := NewRequest("POST", url, body)
if err != nil {
src/pkg/net/http/transport_test.go
の変更点
--- a/src/pkg/net/http/transport_test.go
+++ b/src/pkg/net/http/transport_test.go
@@ -15,6 +15,7 @@ import (
"io"
"io/ioutil"
"net"
+ "net/http"
. "net/http"
"net/http/httptest"
"net/url"
@@ -1610,6 +1611,42 @@ func TestIdleConnChannelLeak(t *testing.T) {
}
}
+// Verify the status quo: that the Client.Post function coerces its
+// body into a ReadCloser if it's a Closer, and that the Transport
+// then closes it.
+func TestTransportClosesRequestBody(t *testing.T) {
+ defer afterTest(t)
+ ts := httptest.NewServer(http.HandlerFunc(func(w ResponseWriter, r *Request) {
+ io.Copy(ioutil.Discard, r.Body)
+ }))
+ defer ts.Close()
+
+ tr := &Transport{}
+ defer tr.CloseIdleConnections()
+ cl := &Client{Transport: tr}
+
+ closes := 0
+
+ res, err := cl.Post(ts.URL, "text/plain", countCloseReader{&closes, strings.NewReader("hello")})
+ if err != nil {
+ t.Fatal(err)
+ }
+ res.Body.Close()
+ if closes != 1 {
+ t.Errorf("closes = %d; want 1", closes)
+ }
+}
+
+type countCloseReader struct {
+ n *int
+ io.Reader
+}
+
+func (cr countCloseReader) Close() error {
+ (*cr.n)++
+ return nil
+}
+
// rgz is a gzip quine that uncompresses to itself.
var rgz = []byte{
0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,
コアとなるコードの解説
src/pkg/net/http/client.go
の変更点
-
RoundTripper
インターフェースのコメント修正:RoundTripper
インターフェースのRoundTrip
メソッドのドキュメントが更新されました。 変更前:// consuming the Body.
変更後:// consuming and closing the Body.
この変更は、RoundTripper
の実装がリクエストボディを単に「消費する」だけでなく、「クローズする」責任も負うことを明確にしています。これは、リソースリークを防ぐ上で非常に重要な指針となります。net/http
パッケージのデフォルトのTransport
実装は、この責任を適切に果たします。 -
Client.Post
メソッドのコメント追加:Client.Post
メソッドのドキュメントに以下の新しい行が追加されました。// If the provided body is also an io.Closer, it is closed after the // body is successfully written to the server.
このコメントは、
Client.Post
に渡されるio.Reader
がio.Closer
インターフェースも実装している場合、net/http
パッケージがそのClose()
メソッドを自動的に呼び出すことを明示しています。これは、開発者がリクエストボディとしてos.File
やbytes.Buffer
のようなio.Closer
を渡した場合に、明示的にdefer file.Close()
などと書く必要がないことを示唆しています(ただし、エラーハンドリングによっては必要になる場合もあります)。この自動クローズは、ボディのデータがサーバーに正常に書き込まれた後に実行されます。
src/pkg/net/http/transport_test.go
の変更点
-
countCloseReader
型の追加: このコミットでは、テストのためにcountCloseReader
という新しい型が定義されました。type countCloseReader struct { n *int io.Reader } func (cr countCloseReader) Close() error { (*cr.n)++ return nil }
この型は、既存の
io.Reader
をラップし、io.Closer
インターフェースも実装しています。Close()
メソッドが呼び出されるたびに、ポインタn
が指す整数カウンタをインクリメントします。これにより、テスト内でClose()
メソッドが何回呼び出されたかを追跡できます。 -
TestTransportClosesRequestBody
テスト関数の追加: この新しいテスト関数は、Client.Post
がio.Closer
を実装するリクエストボディを適切にクローズすることを確認します。httptest.NewServer
を使用して、テスト用のHTTPサーバーを起動します。このサーバーは、受信したリクエストボディを単に読み捨てます。net/http.Transport
とnet/http.Client
のインスタンスを作成します。closes
という整数変数を定義し、そのアドレスをcountCloseReader
に渡します。cl.Post
を呼び出し、リクエストボディとしてcountCloseReader
のインスタンスを渡します。res.Body.Close()
を呼び出してレスポンスボディをクローズします(これはリクエストボディのクローズとは別です)。- 最後に、
closes
変数の値が1
であることをアサートします。これは、countCloseReader
のClose()
メソッドが正確に1回呼び出されたことを意味し、net/http
パッケージがリクエストボディを適切にクローズしたことを証明します。
これらの変更は、net/http
パッケージの内部的な挙動を明確にし、その堅牢性を高めるための重要なステップです。特に、リソース管理の側面において、開発者がより安全にHTTPクライアントを使用できるようになります。
関連リンク
- Go CL (Code Review) リンク: https://golang.org/cl/11542044 このコミットの元のコードレビューページです。詳細な議論や変更履歴を確認できます。
参考にした情報源リンク
-
Go言語公式ドキュメント -
io
パッケージ: https://pkg.go.dev/ioio.Reader
,io.Closer
,io.ReadCloser
インターフェースに関する公式ドキュメントです。 -
Go言語公式ドキュメント -
net/http
パッケージ: https://pkg.go.dev/net/httpnet/http.Client
,net/http.Request
,net/http.RoundTripper
など、HTTPクライアントとサーバーに関する公式ドキュメントです。 -
Go言語公式ドキュメント -
net/http/httptest
パッケージ: https://pkg.go.dev/net/http/httptest HTTPテストサーバーの作成に使用されるhttptest.NewServer
に関する公式ドキュメントです。 -
Go言語のソースコード: この解説は、Go言語の公式リポジトリにある実際のソースコード(
src/pkg/net/http/client.go
およびsrc/pkg/net/http/transport_test.go
)を直接参照して作成されました。client.go
: https://github.com/golang/go/blob/master/src/net/http/client.gotransport_test.go
: https://github.com/golang/go/blob/master/src/net/http/transport_test.go (注: リンクは現在のmasterブランチのものです。コミット当時のコードとは若干異なる可能性がありますが、基本的な構造は同じです。)