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

[インデックス 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というユーティリティパッケージに移動することで、以下の目的を達成しようとしています。

  1. コアパッケージの軽量化: net/httpパッケージの依存関係とコード量を減らし、よりシンプルで保守しやすい状態にする。
  2. 責務の明確化: 各パッケージの役割をより明確にし、開発者が目的の機能を見つけやすくする。net/httpはHTTPプロトコルの基本操作に、httputilはHTTP関連のユーティリティ機能に特化する。
  3. モジュール性の向上: 機能が適切に分割されることで、将来的な拡張や変更が容易になる。

この変更は、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.ClientTransportフィールドに設定することで、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ヘッダーを追加するためのヘルパー関数です。

技術的詳細

このコミットの技術的な変更は、主に以下の点に集約されます。

  1. ファイルの移動とリネーム:

    • src/pkg/net/http/reverseproxy.gosrc/pkg/net/http/httputil/reverseproxy.go に移動し、パッケージ名が http から httputil に変更されました。
    • 同様に、テストファイル src/pkg/net/http/reverseproxy_test.gosrc/pkg/net/http/httputil/reverseproxy_test.go に移動し、パッケージ名が http_test から httputil に変更されました。
  2. Makefileの更新:

    • src/pkg/Makefile から net/http/httputil のテスト除外設定が削除されました。これは、httputilが独立したパッケージとして扱われるようになったためです。
    • src/pkg/net/http/Makefile から reverseproxy.goGOFILESリストから削除されました。
    • src/pkg/net/http/httputil/Makefilereverseproxy.goGOFILESリストに追加されました。
  3. パッケージインポートの変更:

    • reverseproxy.go 内で、httpパッケージの型(例: http.Request, http.ResponseWriter, http.Header, http.RoundTripper, http.DefaultTransport, http.StatusInternalServerError, http.Flusher)を参照する際に、明示的にhttpパッケージ名をプレフィックスとして付けるようになりました。これは、reverseproxy.gohttputilパッケージに属するようになったため、httpパッケージの要素を使用する際にはインポートが必要になるためです。
    • テストファイル reverseproxy_test.go でも同様に、httpパッケージの要素を参照する際に明示的なプレフィックスが追加されました。

これらの変更により、ReverseProxynet/httpパッケージの内部実装ではなく、net/http/httputilパッケージが提供する独立したユーティリティとして扱われるようになります。これにより、net/httpパッケージの依存関係が減少し、よりクリーンなAPI設計が促進されます。

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

主要な変更は、src/pkg/net/http/reverseproxy.gosrc/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パッケージへ完全に移行したことです。

以前は、ReverseProxynet/httpパッケージの一部として定義されており、http.Requesthttp.ResponseWriterなどの型を直接参照していました。しかし、パッケージがhttputilに移動したことで、ReverseProxyhttputilパッケージのメンバーとなります。

この変更に伴い、reverseproxy.go内のコードは、net/httpパッケージからエクスポートされた型や関数を使用する際に、明示的にhttp.プレフィックスを付ける必要があります。例えば、Director func(*Request)Director func(*http.Request)に、Transport RoundTripperTransport http.RoundTripperに、rw ResponseWriterrw http.ResponseWriterに変更されています。これは、httputilパッケージがhttpパッケージをインポートし、そのエクスポートされた要素を使用するという、Go言語のパッケージ間の標準的な依存関係の表現方法です。

テストファイルも同様に、パッケージ名がhttp_testからhttputilに変更され、httpパッケージの要素への参照にはhttp.プレフィックスが追加されています。これにより、テストコードも新しいパッケージ構造に適合しています。

この変更は、ReverseProxyの機能自体には影響を与えませんが、その位置づけと利用方法に影響を与えます。開発者は今後、リバースプロキシ機能を利用する際にnet/http/httputilパッケージをインポートする必要があります。これにより、net/httpパッケージはより基本的なHTTPプロトコル処理に特化し、httputilパッケージはより高度なHTTPユーティリティ機能を提供するという、明確な役割分担が実現されます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (net/httpおよびnet/http/httputilパッケージ)
  • Go言語のソースコード (このコミットのdiff)
  • Go言語の設計原則に関する一般的な知識 (パッケージの責務分離など)