[インデックス 10091] ファイルの概要
このコミットは、Go言語の標準ライブラリ net/http/httputil
パッケージ内の ReverseProxy
における、HTTP Connection
ヘッダーの処理に関する以前の変更(CL 5302057 / dac58d9c9e4a)を元に戻すものです。具体的には、リバースプロキシがバックエンドへのリクエストを転送する際に Connection
ヘッダーを削除するロジックと、レスポンスヘッダーをコピーするためのヘルパー関数 copyHeader
の導入を取り消しています。この変更は、別の「rune change」のためのクリーンなベースラインを確保するために行われました。
コミット
- コミットハッシュ:
5abb29d1b8b741dcadb22720ced7e5e8a131633f
- Author: Andrew Gerrand adg@golang.org
- Date: Wed Oct 26 14:16:34 2011 +0900
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5abb29d1b8b741dcadb22720ced7e5e8a131633f
元コミット内容
このコミットが「undo」している元のコミット(CL 5302057 / dac58d9c9e4a)は、以下の目的を持っていました。
http: remove Connection header in ReverseProxy
: リバースプロキシがバックエンドサーバーにリクエストを転送する際に、クライアントから受け取ったConnection
ヘッダーを削除すること。これは、リバースプロキシとバックエンド間の接続を永続化させたい場合(Keep-Aliveなど)に、クライアントからのConnection: close
ヘッダーがバックエンドに伝播するのを防ぐためです。Fixes #2342
: GoのIssue #2342 を修正することを意図していました。このIssueは、リバースプロキシがConnection: close
ヘッダーを適切に処理しないことによって発生する問題に関連していると考えられます。copyHeader
ヘルパー関数の導入: ヘッダーをコピーするための汎用的なヘルパー関数copyHeader
が導入されました。
変更の背景
このコミットの背景は、コミットメッセージに明確に示されています。
need a clean base from weekly.2011-10-25 for rune change
これは、Go言語の内部で進行中の「rune change」(おそらくUnicode文字の扱いに関する変更)のために、weekly.2011-10-25
という特定の時点からクリーンなコードベースが必要であったことを意味します。以前の Connection
ヘッダーに関する変更が、この「rune change」と競合したり、複雑さを増したりする可能性があったため、一時的にその変更を元に戻すことが決定されたと考えられます。これは、大規模なプロジェクトにおいて、特定の機能開発やリファクタリングを進める際に、他の独立した変更が干渉しないように、一時的にコードベースを特定の状態に戻す一般的なプラクティスです。
前提知識の解説
1. HTTP Connection
ヘッダー
HTTP Connection
ヘッダーは、現在のトランザクションが完了した後に、送信側と受信側の間で接続をどのように制御するかを決定するために使用されます。主な値は以下の通りです。
Connection: close
: 現在のトランザクションが完了したら、接続を閉じることを示します。Connection: Keep-Alive
: 接続を維持し、後続のリクエストにも再利用できることを示します。これにより、TCP接続の確立と終了のオーバーヘッドを削減し、パフォーマンスを向上させることができます。
リバースプロキシの文脈では、クライアントが Connection: close
を送ってきたとしても、プロキシとバックエンドサーバー間の接続は永続化させたい場合があります。これは、プロキシがバックエンドへの接続プールを管理し、効率的に再利用するためです。もしクライアントの Connection: close
がバックエンドにそのまま転送されると、バックエンドはリクエストごとに接続を閉じ、非効率的になる可能性があります。
2. リバースプロキシ (Goの net/http/httputil.ReverseProxy
)
リバースプロキシは、クライアントからのリクエストを受け取り、それを一つ以上のバックエンドサーバーに転送し、バックエンドからのレスポンスをクライアントに返すサーバーです。主な役割は以下の通りです。
- 負荷分散: 複数のバックエンドサーバーにリクエストを分散させ、負荷を均等にする。
- セキュリティ: バックエンドサーバーを直接インターネットに公開せず、セキュリティ層を提供する。
- SSLオフロード: SSL/TLS終端をプロキシで行い、バックエンドサーバーの負荷を軽減する。
- キャッシング: 静的コンテンツをキャッシュし、レスポンスタイムを短縮する。
- ヘッダー操作: リクエストやレスポンスのヘッダーを変更する。
Go言語の net/http/httputil.ReverseProxy
は、このようなリバースプロキシを簡単に構築するための機能を提供します。ReverseProxy
は http.Handler
インターフェースを実装しており、ServeHTTP
メソッドを通じてリクエストの転送とレスポンスの処理を行います。
3. GoのCL (Change List)
Goコミュニティでは、コードの変更は「Change List (CL)」として管理されます。これは、Perforceなどのバージョン管理システムで使われる用語に由来します。各CLは、特定の目的を持った一連の変更(コミット)をまとめたもので、レビュープロセスを経てGoのリポジトリにマージされます。https://golang.org/cl/
のようなURLは、Goのコードレビューシステム(Gerrit)上の特定のCLを指します。
4. Goの net/http
パッケージ
net/http
パッケージは、Go言語でHTTPクライアントとサーバーを実装するための基本的な機能を提供します。これには、HTTPリクエスト、レスポンス、ヘッダー、クッキーなどの構造体や、HTTPサーバーの起動、クライアントからのリクエストの処理、レスポンスの送信などの機能が含まれます。httputil.ReverseProxy
はこの net/http
パッケージの上に構築されています。
技術的詳細
このコミットは、以前の変更を元に戻すことで、ReverseProxy
のヘッダー処理の挙動を元に戻しています。
元のCL 5302057は、リバースプロキシがバックエンドにリクエストを転送する際に、クライアントから受け取った Connection
ヘッダーを削除することを目的としていました。これは、プロキシとバックエンド間の接続を永続化させたい場合に重要です。もしクライアントが Connection: close
を送ってきたとしても、プロキシはバックエンドとの接続を閉じたくないため、このヘッダーを削除する必要がありました。
この「undo」コミットでは、その Connection
ヘッダーを削除するロジックが再び削除されます。これにより、クライアントから受け取った Connection
ヘッダーが、そのままバックエンドに転送される可能性が再び生じます。これは、リバースプロキシの「永続接続」の意図に反する挙動に戻ることを意味します。
また、レスポンスヘッダーをコピーするために導入された copyHeader
ヘルパー関数も削除され、そのロジックは ServeHTTP
メソッド内でインライン化されています。これは、コードの重複を招く可能性がありますが、このコミットの主な目的は「rune change」のためのクリーンなベースラインを確保することであり、コードの美しさや重複の排除は二の次であったと考えられます。
この変更は一時的なものであり、後続のコミットで再び Connection
ヘッダーの適切な処理が導入されるか、あるいは別の方法で問題が解決されることが期待されます。
コアとなるコードの変更箇所
このコミットによって変更されたファイルは以下の2つです。
src/pkg/http/reverseproxy.go
src/pkg/http/reverseproxy_test.go
src/pkg/http/reverseproxy.go
の変更点
-
copyHeader
関数の削除:-func copyHeader(dst, src Header) { - for k, vv := range src { - for _, v := range vv { - dst.Add(k, v) - } - } -}
ヘッダーをコピーするためのヘルパー関数
copyHeader
が削除されました。 -
Connection
ヘッダー削除ロジックの削除:- // Remove the connection header to the backend. We want a - // persistent connection, regardless of what the client sent - // to us. This is modifying the same underlying map from req - // (shallow copied above) so we only copy it if necessary. - if outreq.Header.Get("Connection") != "" { - outreq.Header = make(Header) - copyHeader(outreq.Header, req.Header) - outreq.Header.Del("Connection") - }
バックエンドへのリクエスト (
outreq
) からConnection
ヘッダーを削除するロジックが削除されました。これにより、クライアントからのConnection
ヘッダーがバックエンドに転送される可能性があります。 -
レスポンスヘッダーコピーのインライン化:
- copyHeader(rw.Header(), res.Header) + hdr := rw.Header() + for k, vv := range res.Header { + for _, v := range vv { + hdr.Add(k, v) + } + }
res.Header
からrw.Header()
へのヘッダーコピーが、削除されたcopyHeader
関数を呼び出す代わりに、手動のループでインライン化されました。
src/pkg/http/reverseproxy_test.go
の変更点
Connection
ヘッダー関連テストの削除:- if c := r.Header.Get("Connection"); c != "" { - t.Errorf("handler got Connection header value %q", c) - }
- getReq.Header.Set("Connection", "close") - getReq.Close = true
ReverseProxy
がConnection
ヘッダーを適切に削除しているかを確認するテストケースが削除されました。これは、ヘッダー削除ロジックが削除されたことと整合しています。また、テストリクエストにConnection: close
ヘッダーを設定する部分も削除されています。
コアとなるコードの解説
このコミットの核心は、以前のコミットで導入された Connection
ヘッダーの明示的な削除と、ヘッダーコピーのヘルパー関数を元に戻すことです。
reverseproxy.go
において、ServeHTTP
メソッドはリバースプロキシの主要なロジックを含んでいます。
-
copyHeader
関数の削除とインライン化: 元のコミットでは、ヘッダーをコピーするための再利用可能な関数copyHeader
が導入されました。このコミットでは、その関数が削除され、そのロジックがServeHTTP
内で直接記述される形に戻されました。これは、コードの重複を招きますが、この「undo」の目的が特定の変更を元に戻すことであったため、許容されたと考えられます。 -
Connection
ヘッダー削除ロジックの削除: 最も重要な変更は、バックエンドへのリクエスト (outreq
) からConnection
ヘッダーを削除する以下のブロックが削除されたことです。// Remove the connection header to the backend. We want a // persistent connection, regardless of what the client sent // to us. This is modifying the same underlying map from req // (shallow copied above) so we only copy it if necessary. if outreq.Header.Get("Connection") != "" { outreq.Header = make(Header) copyHeader(outreq.Header, req.Header) outreq.Header.Del("Connection") }
このロジックは、クライアントが
Connection: close
を送ってきた場合でも、リバースプロキシとバックエンド間の接続を永続化させるために非常に重要でした。このブロックが削除されたことで、クライアントからのConnection
ヘッダーがそのままバックエンドに転送される可能性があり、バックエンドが不要な接続終了を行う原因となる可能性があります。
reverseproxy_test.go
におけるテストの削除は、上記のロジックの削除と直接関連しています。ヘッダー削除ロジックがなくなったため、その挙動をテストする意味がなくなったためです。
全体として、このコミットは機能的な後退を意味しますが、これは「rune change」というより大きな目的のための、一時的な措置であったと理解できます。
関連リンク
- このコミットのGitHubページ: https://github.com/golang/go/commit/5abb29d1b8b741dcadb22720ced7e5e8a131633f
- 元コミット (CL 5302057): https://golang.org/cl/5302057
- 関連するGo Issue #2342: https://github.com/golang/go/issues/2342
参考にした情報源リンク
- Go Documentation -
net/http/httputil
: https://pkg.go.dev/net/http/httputil - MDN Web Docs -
Connection
header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection - HTTP/1.1: Message Syntax and Routing (RFC 7230) - Connection Header Field: https://datatracker.ietf.org/doc/html/rfc7230#section-6.1
- Go Code Review Comments - CLs: https://go.dev/doc/contribute#cl
- Gerrit Code Review: https://gerrit-review.googlesource.com/