[インデックス 11944] ファイルの概要
このコミットは、Go言語の標準ライブラリである net/http
パッケージに、example_test.go
という新しいテストファイルを追加するものです。このファイルには、http.Hijacker
インターフェースの使用例と、http.Get
関数の使用例が含まれています。これらの例は、go doc
コマンドや Go のドキュメントサイトで表示され、net/http
パッケージの特定の機能の利用方法を開発者に示すことを目的としています。
コミット
このコミットは、net/http
パッケージに ExampleHijacker
と ExampleGet
という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.Handler
とhttp.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.Reader
と bufio.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接続を「ハイジャック」する方法を示しています。
- ハンドラの登録:
http.HandleFunc("/hijack", ...)
を使用して、/hijack
パスに対するHTTPリクエストを処理するハンドラ関数を登録します。 Hijacker
インターフェースの確認: ハンドラ内で、http.ResponseWriter
がhttp.Hijacker
インターフェースを実装しているかを確認します。これは型アサーションw.(http.Hijacker)
を使用して行われます。もし実装していなければ、サーバーはハイジャックをサポートしていないため、エラーを返します。- 接続のハイジャック:
hj.Hijack()
を呼び出すことで、基盤となるTCP接続 (net.Conn
) と、その接続に対するバッファリングされた読み書きを行うための*bufio.ReadWriter
を取得します。この時点で、HTTPサーバーはこれ以上この接続を管理しません。 - 接続のクローズ:
defer conn.Close()
を使用して、関数が終了する際にTCP接続が確実にクローズされるようにします。これは、ハイジャックされた接続のライフサイクル管理がアプリケーションの責任となるため、非常に重要です。 - 生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リクエストを送信する方法を示しています。
- GETリクエストの送信:
http.Get("http://www.google.com/robots.txt")
を呼び出して、Googleのrobots.txt
ファイルを取得します。http.Get
は、*http.Response
とerror
を返します。 - エラーハンドリング: リクエスト中にエラーが発生した場合 (
err != nil
)、log.Fatal(err)
を使用してプログラムを終了します。 - レスポンスボディの読み取り:
ioutil.ReadAll(res.Body)
を使用して、レスポンスボディの内容をすべて読み取ります。res.Body
はio.ReadCloser
インターフェースを実装しており、読み取り後にクローズする必要があります。 - レスポンスボディのクローズ:
res.Body.Close()
を呼び出して、レスポンスボディをクローズします。これは、リソースリークを防ぐために非常に重要です。 - 内容の出力: 読み取った
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
インターフェースを実装しているかを確認しています。ok
がfalse
の場合、サーバーはハイジャックをサポートしていないため、エラーレスポンスを返します。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言語
net/http
パッケージの公式ドキュメント: https://pkg.go.dev/net/http - Go言語
bufio
パッケージの公式ドキュメント: https://pkg.go.dev/bufio - Go言語
io/ioutil
パッケージの公式ドキュメント (Go 1.16以降はio
およびos
パッケージに移行): https://pkg.go.dev/io/ioutil - Go言語のExample関数に関する公式ドキュメント: https://go.dev/blog/examples
参考にした情報源リンク
- 元のGo CL (Change List): https://golang.org/cl/5673052
- Go言語の公式ドキュメント (pkg.go.dev)
- Go言語のブログ記事やチュートリアル (一般的な
net/http
の使用方法、Hijacker
の概念理解のため)