[インデックス 10239] ファイルの概要
このコミットは、Go言語の標準ライブラリであるnet/http
パッケージからReverseProxy
の実装をnet/http/httputil
パッケージへ移動させるものです。これは、net/http
パッケージの「ダイエット計画」の一環として、コアパッケージの責務を明確にし、より専門的な機能は専用のユーティリティパッケージに分離するという設計思想に基づいています。
コミット
- コミットハッシュ:
54049767ae43c0547b53f239164ac95f19a5db06
- Author: Brad Fitzpatrick bradfitz@golang.org
- Date: Thu Nov 3 15:54:08 2011 -0700
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/54049767ae43c0547b53f239164ac95f19a5db06
元コミット内容
httputil: move ReverseProxy out of http
http diet plan, continued.
R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/5305090
変更の背景
このコミットの背景には、Go言語のnet/http
パッケージの「ダイエット計画 (http diet plan)」という設計方針があります。これは、net/http
パッケージが提供する機能が多岐にわたり、パッケージのサイズや複雑さが増大していたことに対する改善策です。
ReverseProxy
はHTTPリバースプロキシ機能を提供する重要なコンポーネントですが、その機能はHTTPプロトコルの基本的な処理というよりは、より高度なネットワークアプリケーションの構築に特化したユーティリティと見なすことができます。そのため、net/http
パッケージのコア部分から分離し、httputil
というユーティリティパッケージに移動することで、以下の目的を達成しようとしています。
- コアパッケージの軽量化:
net/http
パッケージの依存関係とコード量を減らし、よりシンプルで保守しやすい状態にする。 - 責務の明確化: 各パッケージの役割をより明確にし、開発者が目的の機能を見つけやすくする。
net/http
はHTTPプロトコルの基本操作に、httputil
はHTTP関連のユーティリティ機能に特化する。 - モジュール性の向上: 機能が適切に分割されることで、将来的な拡張や変更が容易になる。
この変更は、Go言語の標準ライブラリ全体におけるモジュール性と保守性の向上を目指す、より大きな取り組みの一部です。
前提知識の解説
このコミットを理解するためには、以下のGo言語の標準ライブラリに関する知識が必要です。
net/http
パッケージ: Go言語でHTTPクライアントおよびサーバーを実装するための主要なパッケージです。HTTPリクエストの処理、レスポンスの生成、ルーティング、ミドルウェアの統合など、HTTP通信に関する基本的な機能を提供します。net/http/httputil
パッケージ:net/http
パッケージに関連する様々なユーティリティ機能を提供するパッケージです。例えば、リバースプロキシ、ダンプ、圧縮/解凍などの機能が含まれます。ReverseProxy
: HTTPリバースプロキシを実装するための構造体です。クライアントからのリクエストを受け取り、それを別のバックエンドサーバーに転送し、バックエンドからのレスポンスをクライアントに返す役割を担います。これにより、ロードバランシング、SSLオフロード、キャッシュなどの機能を実現できます。Makefile
: Goプロジェクトのビルドプロセスを自動化するために使用されるファイルです。GOFILES
変数には、パッケージに含まれるGoソースファイルのリストが定義されます。GOFILES
:Makefile
内で使用される変数で、特定のGoパッケージに属するGoソースファイルのリストを指定します。RoundTripper
インターフェース:net/http
パッケージで定義されているインターフェースで、HTTPリクエストを送信し、HTTPレスポンスを受信する単一のHTTPトランザクションを実行する能力を抽象化します。http.Client
のTransport
フィールドに設定することで、HTTPリクエストの送信方法をカスタマイズできます。Director
関数:ReverseProxy
構造体のフィールドの一つで、オリジナルのHTTPリクエストをバックエンドサーバーに転送する前に、そのリクエストをどのように変更するかを定義する関数です。例えば、リクエストURLの書き換え、ヘッダーの追加/削除などを行います。http.ResponseWriter
インターフェース: HTTPレスポンスをクライアントに書き込むためのインターフェースです。http.Request
構造体: クライアントから受信したHTTPリクエストを表す構造体です。http.Header
型: HTTPヘッダーを表すmap[string][]string
型のエイリアスです。http.DefaultTransport
:net/http
パッケージで提供されるデフォルトのRoundTripper
実装です。特別な設定が不要な場合に利用されます。http.StatusInternalServerError
: HTTPステータスコード500 (Internal Server Error) を表す定数です。http.SetCookie
関数: HTTPレスポンスにSet-Cookie
ヘッダーを追加するためのヘルパー関数です。
技術的詳細
このコミットの技術的な変更は、主に以下の点に集約されます。
-
ファイルの移動とリネーム:
src/pkg/net/http/reverseproxy.go
がsrc/pkg/net/http/httputil/reverseproxy.go
に移動し、パッケージ名がhttp
からhttputil
に変更されました。- 同様に、テストファイル
src/pkg/net/http/reverseproxy_test.go
もsrc/pkg/net/http/httputil/reverseproxy_test.go
に移動し、パッケージ名がhttp_test
からhttputil
に変更されました。
-
Makefile
の更新:src/pkg/Makefile
からnet/http/httputil
のテスト除外設定が削除されました。これは、httputil
が独立したパッケージとして扱われるようになったためです。src/pkg/net/http/Makefile
からreverseproxy.go
がGOFILES
リストから削除されました。src/pkg/net/http/httputil/Makefile
にreverseproxy.go
がGOFILES
リストに追加されました。
-
パッケージインポートの変更:
reverseproxy.go
内で、http
パッケージの型(例:http.Request
,http.ResponseWriter
,http.Header
,http.RoundTripper
,http.DefaultTransport
,http.StatusInternalServerError
,http.Flusher
)を参照する際に、明示的にhttp
パッケージ名をプレフィックスとして付けるようになりました。これは、reverseproxy.go
がhttputil
パッケージに属するようになったため、http
パッケージの要素を使用する際にはインポートが必要になるためです。- テストファイル
reverseproxy_test.go
でも同様に、http
パッケージの要素を参照する際に明示的なプレフィックスが追加されました。
これらの変更により、ReverseProxy
はnet/http
パッケージの内部実装ではなく、net/http/httputil
パッケージが提供する独立したユーティリティとして扱われるようになります。これにより、net/http
パッケージの依存関係が減少し、よりクリーンなAPI設計が促進されます。
コアとなるコードの変更箇所
主要な変更は、src/pkg/net/http/reverseproxy.go
が src/pkg/net/http/httputil/reverseproxy.go
に移動し、その内容が変更された点です。
--- a/src/pkg/net/http/reverseproxy.go
+++ b/src/pkg/net/http/httputil/reverseproxy.go
@@ -4,9 +4,10 @@
// HTTP reverse proxy handler
-package http
+package httputil
import (
+\t"http"
\t"io"
\t"log"
\t"net"
@@ -24,11 +25,11 @@ type ReverseProxy struct {
\t// the request into a new request to be sent
\t// using Transport. Its response is then copied
\t// back to the original client unmodified.
-\tDirector func(*Request)
+\tDirector func(*http.Request)
-\t// The Transport used to perform proxy requests.
-\t// If nil, DefaultTransport is used.\n-\tTransport RoundTripper
+\t// The transport used to perform proxy requests.
+\t// If nil, http.DefaultTransport is used.\n+\tTransport http.RoundTripper
\t// FlushInterval specifies the flush interval, in
\t// nanoseconds, to flush to the client while
@@ -54,7 +55,7 @@ func singleJoiningSlash(a, b string) string {
// target's path is " /base" and the incoming request was for " /dir",
// the target request will be for /base/dir.
func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy {
-\tdirector := func(req *Request) {
+\tdirector := func(req *http.Request) {
\t\treq.URL.Scheme = target.Scheme
\t\treq.URL.Host = target.Host
\t\treq.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
@@ -68,7 +69,7 @@ func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy {
\treturn &ReverseProxy{Director: director}\n }\n \n-func copyHeader(dst, src Header) {
+func copyHeader(dst, src http.Header) {
\tfor k, vv := range src {
\t\tfor _, v := range vv {
\t\t\tdst.Add(k, v)
@@ -76,13 +77,13 @@ func copyHeader(dst, src Header) {
\t}\n }\n \n-func (p *ReverseProxy) ServeHTTP(rw ResponseWriter, req *Request) {
+\tfunc (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
\ttransport := p.Transport
\tif transport == nil {
-\t\ttransport = DefaultTransport
+\t\ttransport = http.DefaultTransport
\t}\n \n-\toutreq := new(Request)
+\toutreq := new(http.Request)
\t*outreq = *req // includes shallow copies of maps, but okay
\tp.Director(outreq)
@@ -96,7 +97,7 @@ func (p *ReverseProxy) ServeHTTP(rw ResponseWriter, req *Request) {
\t// to us. This is modifying the same underlying map from req
\t// (shallow copied above) so we only copy it if necessary.\n \tif outreq.Header.Get("Connection") != "" {
-\t\toutreq.Header = make(Header)
+\t\toutreq.Header = make(http.Header)
\t\tcopyHeader(outreq.Header, req.Header)
\t\toutreq.Header.Del("Connection")
\t}\n@@ -108,7 +109,7 @@ func (p *ReverseProxy) ServeHTTP(rw ResponseWriter, req *Request) {
\tres, err := transport.RoundTrip(outreq)\n \tif err != nil {
\t\tlog.Printf("http: proxy error: %v", err)\n-\t\trw.WriteHeader(StatusInternalServerError)
+\t\trw.WriteHeader(http.StatusInternalServerError)
\t\treturn
\t}\n \n@@ -129,7 +130,7 @@ func (p *ReverseProxy) ServeHTTP(rw ResponseWriter, req *Request) {
type writeFlusher interface {
\tio.Writer
-\tFlusher
+\thttp.Flusher
}
type maxLatencyWriter struct {
テストファイル src/pkg/net/http/reverseproxy_test.go
も同様に移動し、パッケージ名とインポートが変更されています。
--- a/src/pkg/net/http/reverseproxy_test.go
+++ b/src/pkg/net/http/httputil/reverseproxy_test.go
@@ -4,10 +4,10 @@
// Reverse proxy tests.
-package http_test
+package httputil
import (
-\t. "http"
+\t"http"
\t"http/httptest"
\t"io/ioutil"
\t"testing"
@@ -17,7 +17,7 @@ import (
func TestReverseProxy(t *testing.T) {
\tconst backendResponse = "I am the backend"
\tconst backendStatus = 404
-\tbackend := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+\tbackend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
\t\tif len(r.TransferEncoding) > 0 {
\t\t\tt.Errorf("backend got unexpected TransferEncoding: %v", r.TransferEncoding)
\t\t}\n@@ -31,7 +31,7 @@ func TestReverseProxy(t *testing.T) {
\t\t\tt.Errorf("backend got Host header %q, want %q", g, e)
\t\t}\n \t\tw.Header().Set("X-Foo", "bar")
-\t\tSetCookie(w, &Cookie{Name: "flavor", Value: "chocolateChip"})
+\t\thttp.SetCookie(w, &http.Cookie{Name: "flavor", Value: "chocolateChip"})
\t\tw.WriteHeader(backendStatus)
\t\tw.Write([]byte(backendResponse))
\t}))
@@ -44,11 +44,11 @@ func TestReverseProxy(t *testing.T) {
\tfrontend := httptest.NewServer(proxyHandler)
\tdefer frontend.Close()\n \n-\tgetReq, _ := NewRequest("GET", frontend.URL, nil)
+\tgetReq, _ := http.NewRequest("GET", frontend.URL, nil)
\tgetReq.Host = "some-name"
\tgetReq.Header.Set("Connection", "close")
\tgetReq.Close = true
-\tres, err := DefaultClient.Do(getReq)
+\tres, err := http.DefaultClient.Do(getReq)
\tif err != nil {
\t\tt.Fatalf("Get: %v", err)
\t}\n
コアとなるコードの解説
このコミットの核心は、ReverseProxy
構造体とその関連関数がnet/http
パッケージからnet/http/httputil
パッケージへ完全に移行したことです。
以前は、ReverseProxy
はnet/http
パッケージの一部として定義されており、http.Request
やhttp.ResponseWriter
などの型を直接参照していました。しかし、パッケージがhttputil
に移動したことで、ReverseProxy
はhttputil
パッケージのメンバーとなります。
この変更に伴い、reverseproxy.go
内のコードは、net/http
パッケージからエクスポートされた型や関数を使用する際に、明示的にhttp.
プレフィックスを付ける必要があります。例えば、Director func(*Request)
はDirector func(*http.Request)
に、Transport RoundTripper
はTransport http.RoundTripper
に、rw ResponseWriter
はrw http.ResponseWriter
に変更されています。これは、httputil
パッケージがhttp
パッケージをインポートし、そのエクスポートされた要素を使用するという、Go言語のパッケージ間の標準的な依存関係の表現方法です。
テストファイルも同様に、パッケージ名がhttp_test
からhttputil
に変更され、http
パッケージの要素への参照にはhttp.
プレフィックスが追加されています。これにより、テストコードも新しいパッケージ構造に適合しています。
この変更は、ReverseProxy
の機能自体には影響を与えませんが、その位置づけと利用方法に影響を与えます。開発者は今後、リバースプロキシ機能を利用する際にnet/http/httputil
パッケージをインポートする必要があります。これにより、net/http
パッケージはより基本的なHTTPプロトコル処理に特化し、httputil
パッケージはより高度なHTTPユーティリティ機能を提供するという、明確な役割分担が実現されます。
関連リンク
- Gerrit Change-ID: https://golang.org/cl/5305090
参考にした情報源リンク
- Go言語の公式ドキュメント (
net/http
およびnet/http/httputil
パッケージ) - Go言語のソースコード (このコミットのdiff)
- Go言語の設計原則に関する一般的な知識 (パッケージの責務分離など)