[インデックス 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.goRoundTripperインターフェースのコメントが変更されました。Client.Postメソッドのコメントが追加されました。
-
src/pkg/net/http/transport_test.goTestTransportClosesRequestBodyという新しいテスト関数が追加されました。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.goRoundTripperインターフェースのコメントが変更されました。Client.Postメソッドのコメントが追加されました。
-
src/pkg/net/http/transport_test.goTestTransportClosesRequestBodyという新しいテスト関数が追加されました。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ブランチのものです。コミット当時のコードとは若干異なる可能性がありますが、基本的な構造は同じです。)