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

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

このコミットは、Go言語の標準ライブラリである net/http パッケージに、example_test.go という新しいテストファイルを追加するものです。このファイルには、http.Hijacker インターフェースの使用例と、http.Get 関数の使用例が含まれています。これらの例は、go doc コマンドや Go のドキュメントサイトで表示され、net/http パッケージの特定の機能の利用方法を開発者に示すことを目的としています。

コミット

このコミットは、net/http パッケージに ExampleHijackerExampleGet という2つの使用例を追加しました。これにより、net/http パッケージのドキュメントが強化され、ユーザーがこれらの機能をより簡単に理解し、利用できるようになります。

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

https://github.com/golang/go/commit/3a317183a140c6804ce1729f8f456075faf7252f

元コミット内容

net/http: add some examples

R=golang-dev, dsymonds, adg, rogpeppe, bradfitz
CC=golang-dev
https://golang.org/cl/5673052

変更の背景

Go言語の標準ライブラリは、その堅牢性と使いやすさで知られていますが、特定の高度な機能や一般的な使用パターンについては、コード例を通じてその利用方法を示すことが非常に有効です。net/http パッケージは、GoにおけるWebアプリケーション開発の基盤であり、多くの開発者が利用しています。

このコミットの背景には、net/http パッケージのドキュメントを改善し、特に http.Hijacker のような、より低レベルのネットワーク操作を可能にする機能の利用方法を明確にする意図があります。http.Hijacker は、HTTPプロトコルからTCP接続を「ハイジャック」し、生(raw)のTCP通信を可能にするための強力な機能ですが、その性質上、一般的なHTTPハンドリングとは異なるアプローチを必要とします。そのため、具体的なコード例を提供することで、開発者がこの機能を安全かつ効果的に使用するためのガイドラインを提供することが重要でした。

また、http.Get のような基本的なHTTPクライアント操作についても、簡潔な例を提供することで、新規開発者が net/http パッケージの利用を開始する際の障壁を低減する目的もあります。これらの例は、Goのドキュメンテーションツールによって自動的に抽出され、公式ドキュメントの一部として表示されるため、開発者が go doc コマンドや pkg.go.dev のようなオンラインリソースを通じて簡単にアクセスできるようになります。

前提知識の解説

このコミットの変更内容を理解するためには、以下のGo言語およびネットワークに関する基本的な知識が必要です。

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

net/http パッケージは、Go言語でHTTPクライアントとサーバーを実装するための標準ライブラリです。Webアプリケーションの構築、RESTful APIの作成、HTTPリクエストの送信など、Web関連のあらゆるタスクに利用されます。

  • http.Handlerhttp.HandleFunc:

    • http.Handler は、ServeHTTP(ResponseWriter, *Request) メソッドを持つインターフェースです。HTTPリクエストを処理するためのロジックをカプセル化します。
    • http.HandleFunc は、特定のパスに対するHTTPリクエストを処理するための関数を登録する便利な関数です。内部的には、提供された関数を http.HandlerFunc 型にラップして http.Handle を呼び出します。
  • http.ResponseWriter: HTTPレスポンスをクライアントに書き込むためのインターフェースです。ヘッダーの設定、ステータスコードの送信、ボディの書き込みなどを行います。

  • http.Request: クライアントからのHTTPリクエストを表す構造体です。リクエストメソッド、URL、ヘッダー、ボディなどの情報を含みます。

http.Hijacker インターフェース

http.Hijacker は、http.ResponseWriter インターフェースを実装する型がオプションで実装できるインターフェースです。このインターフェースは、Hijack() (net.Conn, *bufio.ReadWriter, error) メソッドを定義します。

  • 目的: Hijack メソッドが呼び出されると、HTTPサーバーは基盤となるTCP接続の制御を呼び出し元に引き渡します。これにより、開発者はHTTPプロトコルから離れて、生(raw)のTCP通信を直接行うことができます。これは、WebSocketのようなアップグレードプロトコルや、カスタムのバイナリプロトコルを実装する際に非常に有用です。
  • 注意点: Hijack が呼び出された後、http.ResponseWriter は使用できなくなります。接続のクローズやエラーハンドリングは、ハイジャックした側(通常はアプリケーションコード)の責任となります。

bufio.ReadWriter

bufio パッケージは、バッファリングされたI/O操作を提供します。bufio.ReadWriter は、bufio.Readerbufio.Writer を組み合わせた構造体で、読み書き両方のバッファリングされた操作を効率的に行うことができます。http.Hijacker が返す *bufio.ReadWriter は、ハイジャックされたTCP接続上でバッファリングされた読み書きを行うために使用されます。

http.Get 関数

http.Get は、指定されたURLに対してHTTP GETリクエストを送信するための便利な関数です。これは、最も一般的なHTTPクライアント操作の一つであり、Webページの内容を取得したり、APIからデータをフェッチしたりする際に頻繁に利用されます。

GoのテストとExample関数

Go言語では、_test.go で終わるファイルにテストコードを記述します。Example というプレフィックスを持つ関数は、特別な意味を持ちます。

  • Example 関数: Example 関数は、Goのドキュメンテーションツールによって自動的に検出され、生成されるドキュメントにコード例として埋め込まれます。これらの関数は、通常のテスト関数と同様に実行されますが、その出力はドキュメントに表示される期待される出力と比較されます。これにより、ドキュメントのコード例が常に最新で動作することが保証されます。
  • Output: コメント: Example 関数の末尾に // Output: コメントを記述することで、その例が生成する期待される出力を指定できます。テスト実行時に実際の出力とこのコメントの内容が比較され、一致しない場合はテストが失敗します。

技術的詳細

このコミットで追加された example_test.go ファイルは、net/http パッケージの2つの異なる側面を示すための具体的なコード例を提供します。

ExampleHijacker() の詳細

ExampleHijacker 関数は、HTTPサーバーがクライアントとのTCP接続を「ハイジャック」する方法を示しています。

  1. ハンドラの登録: http.HandleFunc("/hijack", ...) を使用して、/hijack パスに対するHTTPリクエストを処理するハンドラ関数を登録します。
  2. Hijacker インターフェースの確認: ハンドラ内で、http.ResponseWriterhttp.Hijacker インターフェースを実装しているかを確認します。これは型アサーション w.(http.Hijacker) を使用して行われます。もし実装していなければ、サーバーはハイジャックをサポートしていないため、エラーを返します。
  3. 接続のハイジャック: hj.Hijack() を呼び出すことで、基盤となるTCP接続 (net.Conn) と、その接続に対するバッファリングされた読み書きを行うための *bufio.ReadWriter を取得します。この時点で、HTTPサーバーはこれ以上この接続を管理しません。
  4. 接続のクローズ: defer conn.Close() を使用して、関数が終了する際にTCP接続が確実にクローズされるようにします。これは、ハイジャックされた接続のライフサイクル管理がアプリケーションの責任となるため、非常に重要です。
  5. 生TCP通信:
    • bufrw.WriteString("Now we're speaking raw TCP. Say hi: ")bufrw.Flush() を使用して、クライアントにメッセージを送信します。
    • bufrw.ReadString('\n') を使用して、クライアントからの改行文字までの入力を読み取ります。これにより、クライアントが何かを送信するのを待ちます。
    • fmt.Fprintf(bufrw, "You said: %q\\nBye.\\n", s)bufrw.Flush() を使用して、クライアントが送信した内容をエコーバックし、接続を終了するメッセージを送信します。

この例は、HTTPプロトコルのレイヤーをバイパスして、カスタムプロトコルやWebSocketのようなアップグレードプロトコルを実装する際の基本的なパターンを示しています。

ExampleGet() の詳細

ExampleGet 関数は、http.Get 関数を使用して簡単なHTTP GETリクエストを送信する方法を示しています。

  1. GETリクエストの送信: http.Get("http://www.google.com/robots.txt") を呼び出して、Googleの robots.txt ファイルを取得します。http.Get は、*http.Responseerror を返します。
  2. エラーハンドリング: リクエスト中にエラーが発生した場合 (err != nil)、log.Fatal(err) を使用してプログラムを終了します。
  3. レスポンスボディの読み取り: ioutil.ReadAll(res.Body) を使用して、レスポンスボディの内容をすべて読み取ります。res.Bodyio.ReadCloser インターフェースを実装しており、読み取り後にクローズする必要があります。
  4. レスポンスボディのクローズ: res.Body.Close() を呼び出して、レスポンスボディをクローズします。これは、リソースリークを防ぐために非常に重要です。
  5. 内容の出力: 読み取った robots.txt の内容を標準出力に fmt.Printf で出力します。

この例は、Goで外部のHTTPリソースにアクセスする最も基本的な方法を示しており、Webスクレイピングや外部APIとの連携の出発点となります。

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

このコミットによって追加された唯一のファイルは src/pkg/net/http/example_test.go です。

--- /dev/null
+++ b/src/pkg/net/http/example_test.go
@@ -0,0 +1,51 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http_test
+
+import (
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net/http"
+)
+
+func ExampleHijacker() {
+	http.HandleFunc("/hijack", func(w http.ResponseWriter, r *http.Request) {
+		hj, ok := w.(http.Hijacker)
+		if !ok {
+			http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError)
+			return
+		}
+		conn, bufrw, err := hj.Hijack()
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+		// Don't forget to close the connection:
+		defer conn.Close()
+		bufrw.WriteString("Now we're speaking raw TCP. Say hi: ")
+		bufrw.Flush()
+		s, err := bufrw.ReadString('\n')
+		if err != nil {
+			log.Printf("error reading string: %v", err)
+			return
+		}
+		fmt.Fprintf(bufrw, "You said: %q\\nBye.\\n", s)
+		bufrw.Flush()
+	})
+}
+
+func ExampleGet() {
+	res, err := http.Get("http://www.google.com/robots.txt")
+	if err != nil {
+		log.Fatal(err)
+	}
+	robots, err := ioutil.ReadAll(res.Body)
+	if err != nil {
+		log.Fatal(err)
+	}
+	res.Body.Close()
+	fmt.Printf("%s", robots)
+}

コアとなるコードの解説

src/pkg/net/http/example_test.go

このファイルは、net/http パッケージの機能を示すためのGoのExample関数を含んでいます。

  • package http_test: この行は、このファイルが http パッケージの外部にあるテストパッケージであることを示しています。これにより、http パッケージの公開されたAPIのみをテストおよび例示することができます。
  • import ブロック: 必要なパッケージ (fmt, io/ioutil, log, net/http) をインポートしています。
  • func ExampleHijacker():
    • http.HandleFunc("/hijack", ...): /hijack というパスに対するHTTPリクエストを処理する匿名関数を登録しています。この関数がHTTPサーバーのハンドラとして機能します。
    • hj, ok := w.(http.Hijacker): http.ResponseWriter (w) が http.Hijacker インターフェースを実装しているかを確認しています。okfalse の場合、サーバーはハイジャックをサポートしていないため、エラーレスポンスを返します。
    • conn, bufrw, err := hj.Hijack(): Hijack メソッドを呼び出し、基盤となるネットワーク接続 (conn) とバッファリングされた読み書きオブジェクト (bufrw) を取得します。これにより、HTTPプロトコルからTCPレベルの通信に移行します。
    • defer conn.Close(): Hijack された接続は、明示的にクローズする必要があります。defer ステートメントにより、関数が終了する際に接続が確実に閉じられます。
    • bufrw.WriteString(...)bufrw.Flush(): クライアントに文字列を書き込み、バッファをフラッシュして即座に送信します。
    • bufrw.ReadString('\n'): クライアントからの入力を改行文字まで読み取ります。
    • fmt.Fprintf(bufrw, ...): 読み取った文字列を整形してクライアントにエコーバックし、再度バッファをフラッシュします。
  • func ExampleGet():
    • res, err := http.Get("http://www.google.com/robots.txt"): http.Get 関数を使用して、指定されたURLにGETリクエストを送信します。
    • if err != nil { log.Fatal(err) }: リクエスト中にエラーが発生した場合、プログラムを終了します。
    • robots, err := ioutil.ReadAll(res.Body): レスポンスボディの内容をすべて読み取り、バイトスライスとして取得します。
    • if err != nil { log.Fatal(err) }: レスポンスボディの読み取り中にエラーが発生した場合、プログラムを終了します。
    • res.Body.Close(): レスポンスボディをクローズします。これは、HTTPクライアントが使用するネットワークリソースを解放するために非常に重要です。
    • fmt.Printf("%s", robots): 読み取った robots.txt の内容を標準出力に出力します。

これらのExample関数は、Goのドキュメンテーションシステムによって自動的にテストされ、その出力が期待される出力と一致することが保証されます。これにより、ドキュメントの品質と正確性が維持されます。

関連リンク

参考にした情報源リンク

  • 元のGo CL (Change List): https://golang.org/cl/5673052
  • Go言語の公式ドキュメント (pkg.go.dev)
  • Go言語のブログ記事やチュートリアル (一般的な net/http の使用方法、Hijacker の概念理解のため)