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

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

このコミットは、Go言語の標準ライブラリである net/http パッケージ内のテストファイル fs_test.go に対する変更です。具体的には、TestServeFileFromCWD というテスト関数において、HTTPリクエストボディのクローズ処理を追加することで、Windows環境でのテストの失敗を修正しています。

コミット

commit 5d50dbc55ae5fa7240d94068ffc466b2327ebae3
Author: Alex Brainman <alex.brainman@gmail.com>
Date:   Mon Sep 24 12:48:19 2012 +1000

    net/http: use r.Body.Close to close connection during TestServeFileFromCWD
    
    Fixes #3917.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/6553061

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

https://github.com/golang/go/commit/5d50dbc55ae5fa7240d94068ffc466b2327ebae3

元コミット内容

net/http: use r.Body.Close to close connection during TestServeFileFromCWD

このコミットは、TestServeFileFromCWD テスト中に接続を閉じるために r.Body.Close を使用することを目的としています。これにより、Issue #3917 が修正されます。

変更の背景

この変更の背景には、net/http パッケージの ServeFile 関数をテストする TestServeFileFromCWD テストが、特定の環境、特にWindowsで失敗するという問題がありました。元のコードでは、Windows環境の場合に一時的にテストをスキップする処理が含まれており、その理由として「TODO(brainman): find out why this test is broken; see http://golang.org/issue/3917」とコメントされていました。

この問題は、HTTPリクエストのボディが適切にクローズされないことによって、基盤となるネットワーク接続が解放されず、リソースリークや接続のハングアップが発生していた可能性が高いです。特にテスト環境では、短期間に多数の接続が確立・切断されるため、このようなリソースリークが顕在化しやすくなります。http.Request.Bodyio.ReadCloser インターフェースを実装しており、その Close メソッドを呼び出すことで、関連するリソース(この場合はHTTP接続)を確実に解放する必要があります。この修正は、この重要なクローズ処理をテストコードに追加することで、テストの信頼性を向上させ、Windows環境での問題を解決することを目的としています。

前提知識の解説

Go言語の net/http パッケージ

Go言語の net/http パッケージは、HTTPクライアントとサーバーの実装を提供します。ウェブアプリケーションの構築において中心的な役割を果たし、HTTPリクエストの処理、レスポンスの生成、ファイルの提供など、多岐にわたる機能を提供します。

  • http.HandlerFunc: HTTPリクエストを処理するための関数を http.Handler インターフェースに適合させるための型です。
  • http.ResponseWriter: HTTPレスポンスを構築するために使用されるインターフェースです。
  • http.Request: クライアントからのHTTPリクエストを表す構造体です。リクエストメソッド、URL、ヘッダー、ボディなどの情報を含みます。
  • http.Request.Body: http.Request 構造体の一部で、クライアントから送信されたリクエストボディのデータにアクセスするための io.ReadCloser インターフェースです。これは、リクエストボディを読み取るための Read メソッドと、関連するリソースを解放するための Close メソッドを提供します。
  • http.ServeFile: 指定されたパスのファイルをHTTPレスポンスとして提供する関数です。ファイルの種類に応じて適切な Content-Type ヘッダーを設定し、ファイルのコンテンツをレスポンスボディとして送信します。

io.ReadCloser インターフェース

Go言語の io パッケージで定義されている io.ReadCloser インターフェースは、io.Readerio.Closer の両方のインターフェースを組み合わせたものです。

  • io.Reader: データを読み取るための Read(p []byte) (n int, err error) メソッドを定義します。
  • io.Closer: リソースをクリーンアップ(解放)するための Close() error メソッドを定義します。

http.Request.Bodyio.ReadCloser 型であるため、リクエストボディの読み取りが完了した後、またはエラーが発生した場合でも、必ず Close() メソッドを呼び出して、基盤となるネットワーク接続や関連するリソースを適切に解放する必要があります。これを怠ると、リソースリークが発生し、サーバーのパフォーマンス低下や接続の枯渇につながる可能性があります。

httptest パッケージ

net/http/httptest パッケージは、HTTPハンドラーやサーバーのテストを容易にするためのユーティリティを提供します。

  • httptest.NewServer: 指定された http.Handler を使用して、テスト用のHTTPサーバーを起動します。これにより、実際のネットワーク接続を介してハンドラーの動作をテストできます。テストサーバーは、テストが終了すると自動的にクローズされますが、そのサーバーへのリクエストボディは別途クローズする必要があります。

技術的詳細

このコミットの技術的な核心は、HTTPリクエストのボディを適切にクローズすることの重要性にあります。HTTP/1.1では、Keep-Alive接続がデフォルトで有効になっており、クライアントとサーバー間で複数のリクエスト/レスポンスを同じTCP接続上でやり取りできます。しかし、リクエストボディが完全に読み取られない、または明示的にクローズされない場合、サーバーはクライアントがまだデータを送信していると判断し、接続を閉じずに待機し続けることがあります。これにより、サーバー側のリソース(ファイルディスクリプタ、メモリなど)が解放されず、特にテスト環境のように短期間に多数の接続が生成されるシナリオでは、リソース枯渇やテストのハングアップといった問題を引き起こす可能性があります。

TestServeFileFromCWD テストでは、httptest.NewServer を使用してテスト用のHTTPサーバーを起動し、そのサーバーに対してHTTPリクエストを送信しています。サーバー側のハンドラーは http.ServeFile を呼び出してファイルを返します。この際、クライアント側でレスポンスボディを読み取った後、リクエストボディ(r.Body)が適切にクローズされないと、サーバー側の接続が完全に解放されない状態になることがあります。

Windows環境では、ファイルシステムやネットワークスタックの挙動が他のOS(Linux/Unix系)と異なる場合があり、このようなリソースリークがより顕著に現れるか、あるいは特定のタイミングでデッドロックのような状態を引き起こす可能性がありました。コミット前のコードでは、この問題を回避するためにWindowsでのテストをスキップしていましたが、これは根本的な解決策ではありませんでした。

今回の修正では、http.Get(ts.URL) で取得したレスポンス r のボディ r.Body を読み取った後、明示的に r.Body.Close() を呼び出すことで、この問題を解決しています。これにより、HTTPリクエストに関連するリソースが確実に解放され、テストが安定して実行されるようになります。これは、Go言語における io.ReadCloser インターフェースを使用する際の一般的なベストプラクティスであり、リソース管理の重要性を示しています。

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

変更は src/pkg/net/http/fs_test.go ファイルの TestServeFileFromCWD 関数内で行われています。

--- a/src/pkg/net/http/fs_test.go
+++ b/src/pkg/net/http/fs_test.go
@@ -340,11 +340,6 @@ func TestServeFileMimeType(t *testing.T) {
 }
 
 func TestServeFileFromCWD(t *testing.T) {
-	if runtime.GOOS == "windows" {
-		// TODO(brainman): find out why this test is broken
-		t.Logf("Temporarily skipping test on Windows; see http://golang.org/issue/3917")
-		return
-	}
 	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
 		ServeFile(w, r, "fs_test.go")
 	}))
@@ -353,6 +348,7 @@ func TestServeFileFromCWD(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
+	r.Body.Close()
 	if r.StatusCode != 200 {
 		t.Fatalf("expected 200 OK, got %s", r.Status)
 	}

具体的には、以下の2点が変更されています。

  1. Windows環境でのテストスキップ処理の削除:

    -	if runtime.GOOS == "windows" {
    -		// TODO(brainman): find out why this test is broken
    -		t.Logf("Temporarily skipping test on Windows; see http://golang.org/issue/3917")
    -		return
    -	}
    

    このコードブロックが削除され、Windows環境でもテストが実行されるようになりました。

  2. r.Body.Close() の追加:

    +	r.Body.Close()
    

    http.Get で取得したレスポンス r のボディをクローズする r.Body.Close() が、ステータスコードのチェックの直前に追加されました。

コアとなるコードの解説

TestServeFileFromCWD 関数は、現在の作業ディレクトリからファイルをHTTP経由で提供する http.ServeFile 関数の動作をテストします。

変更前のコードでは、Windows環境でこのテストが失敗することが知られており、一時的にスキップされていました。これは、http.Get で取得したレスポンスのボディ (r.Body) が適切にクローズされないために、基盤となるネットワーク接続が解放されず、リソースリークが発生していたことが原因と考えられます。特にWindowsでは、OSのネットワークスタックの挙動により、この問題が顕在化しやすかった可能性があります。

追加された r.Body.Close() は、http.Request.Bodyio.ReadCloser インターフェースを実装しているため、その Close メソッドを呼び出すことで、リクエストボディに関連するリソース(この場合はHTTP接続)を明示的に解放します。これにより、テストが完了した際に、使用されたネットワークリソースが確実にクリーンアップされ、テストの信頼性が向上しました。

この修正により、Windows環境でのテストスキップが不要となり、すべてのプラットフォームで TestServeFileFromCWD が正しく実行されるようになりました。これは、Go言語のネットワークプログラミングにおいて、リソース管理、特に io.ReadCloserClose メソッドの呼び出しが非常に重要であることを示しています。

関連リンク

参考にした情報源リンク