[インデックス 12275] ファイルの概要
このコミットは、Go言語の標準ライブラリである net/http
パッケージ内の ProxyFromEnvironment
関数に関するバグ修正と、それに関連するドキュメントの改善、およびテストの追加を行っています。具体的には、src/pkg/net/http/transport.go
と src/pkg/net/http/transport_test.go
の2つのファイルが変更されています。
コミット
commit f5df930618a65c1b8ef9e798e679a618301fdbe9
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Wed Feb 29 09:52:52 2012 -0800
net/http: fix ProxyFromEnvironment bug, docs, add tests
Fixes #2919 I believe. (gets as far as sending a CONNECT
request to my little dummy logging proxy that doesn't actually
support CONNECT now.) Untested with a real CONNECT-supporting
proxy, though.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5708055
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f5df930618a65c1b8ef9e798e679a618301fdbe9
元コミット内容
net/http
: ProxyFromEnvironment
のバグを修正し、ドキュメントを更新し、テストを追加。
これは #2919 を修正していると思われます。(CONNECTリクエストを、実際にはCONNECTをサポートしていないダミーのロギングプロキシに送信するところまでは到達します。)ただし、実際のCONNECTをサポートするプロキシでのテストは行われていません。
変更の背景
このコミットは、Go言語のIssue #2919 に関連するバグを修正するために行われました。Issue #2919 は、ProxyFromEnvironment
関数が環境変数 HTTP_PROXY
からプロキシURLを解析する際に、スキーム(http://
や https://
など)が指定されていない場合に正しく処理できないという問題でした。
具体的には、HTTP_PROXY
環境変数に 127.0.0.1:8080
のようにスキームなしでホストとポートのみが指定された場合、url.Parse
関数はエラーを返さず、結果として proxyURL.Scheme
が空文字列になります。この状態では、net/http
パッケージがプロキシを正しく利用できず、HTTPリクエストが失敗したり、意図しない動作をしたりする可能性がありました。
このコミットは、このスキームが欠落しているケースを適切にハンドリングし、http://
スキームを補完することで、より堅牢なプロキシ設定の解釈を実現することを目的としています。
前提知識の解説
1. HTTPプロキシと環境変数 (HTTP_PROXY
, NO_PROXY
)
- HTTPプロキシ: クライアントとサーバーの間に入って通信を中継するサーバーです。セキュリティ、キャッシュ、アクセス制御などの目的で利用されます。
HTTP_PROXY
/http_proxy
: HTTPリクエストに使用するプロキシサーバーのURLを指定するための環境変数です。通常、http://proxy.example.com:8080
のような形式で指定されます。Goのnet/http
パッケージを含む多くのHTTPクライアントライブラリは、この環境変数を自動的に読み取り、プロキシ設定として利用します。HTTPS_PROXY
/https_proxy
: HTTPSリクエストに使用するプロキシサーバーのURLを指定するための環境変数です。NO_PROXY
/no_proxy
: プロキシを使用しないホスト名のリストを指定するための環境変数です。カンマ区切りで複数のホストを指定できます。例えば、localhost,*.example.com
のように指定すると、これらのホストへのリクエストではプロキシが使用されません。
2. net/http
パッケージ
Go言語の標準ライブラリで、HTTPクライアントとサーバーの実装を提供します。Webアプリケーションの構築や、外部APIとの連携など、HTTP通信を扱う上で中心的な役割を果たします。
3. url.Parse
関数
Go言語の net/url
パッケージに含まれる関数で、文字列形式のURLを url.URL
構造体に解析します。この構造体には、スキーム(http
, https
など)、ホスト、パス、クエリパラメータなどのURLの各要素が格納されます。
4. HTTP CONNECT
メソッド
HTTP/1.1のメソッドの一つで、主にHTTPS通信をプロキシ経由で行う際に使用されます。クライアントはプロキシに対して CONNECT
メソッドを使って特定のホストとポートへのTCP接続の確立を要求します。プロキシが接続を確立すると、クライアントと目的のサーバー間で直接TCPストリームが確立され、プロキシは単にデータを中継するトンネルとして機能します。これにより、プロキシは暗号化されたHTTPSトラフィックの内容を解読することなく中継できます。
技術的詳細
ProxyFromEnvironment
関数は、環境変数 HTTP_PROXY
(または http_proxy
) の値を読み取り、それをプロキシURLとして解析します。元の実装では、url.Parse
がエラーを返した場合にのみ、http://
を補完して再解析を試みていました。
しかし、127.0.0.1:8080
のようにスキームが欠落している文字列を url.Parse
に渡した場合、url.Parse
はエラーを返しません。その代わり、url.URL
構造体の Scheme
フィールドが空文字列になります。元のコードではこのケースが考慮されていなかったため、proxyURL.Scheme
が空のまま処理が進み、結果としてプロキシが正しく設定されないというバグがありました。
このコミットでは、以下の変更によってこの問題を解決しています。
-
url.Parse
の結果チェックの強化:proxyURL, err := url.Parse(proxy)
の結果をチェックする条件に、err != nil
に加えてproxyURL.Scheme == ""
を追加しました。if err != nil || proxyURL.Scheme == ""
これにより、url.Parse
がエラーを返さなかった場合でも、解析されたURLのスキームが空であれば、それはスキームが欠落している不正な形式であると判断されます。 -
http://
スキームの補完: スキームが欠落していると判断された場合、http://
をプレフィックスとして元のプロキシ文字列に追加し、再度url.Parse
を試みます。if u, err := url.Parse("http://" + proxy); err == nil { proxyURL = u; err = nil }
この修正により、127.0.0.1:8080
のような形式のプロキシ設定もhttp://127.0.0.1:8080
として正しく解釈されるようになります。 -
ドキュメントの更新:
ProxyFromEnvironment
関数のコメントが更新され、プロキシが環境変数で定義されていない場合や、リクエストにプロキシが使用されるべきでない場合に、nil URL
とnil error
が返されることが明示されました。これにより、関数の挙動がより明確になりました。 -
テストの追加:
src/pkg/net/http/transport_test.go
にTestProxyFromEnvironment
という新しいテスト関数が追加されました。このテストは、スキームあり/なしのプロキシURL、HTTPSプロキシURL、空のプロキシ設定など、様々なシナリオでProxyFromEnvironment
が期待通りに動作するかを検証します。特に、127.0.0.1:8080
のようなスキームなしのケースが正しくhttp://127.0.0.1:8080
として解釈されることを確認しています。
コアとなるコードの変更箇所
src/pkg/net/http/transport.go
の ProxyFromEnvironment
関数内の以下の行が変更されました。
--- a/src/pkg/net/http/transport.go
+++ b/src/pkg/net/http/transport.go
@@ -76,7 +76,9 @@ type Transport struct {
// ProxyFromEnvironment returns the URL of the proxy to use for a
// given request, as indicated by the environment variables
// $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy).
-// Either URL or an error is returned.
+// An error is returned if the proxy environment is invalid.
+// A nil URL and nil error are returned if no proxy is defined in the
+// environment, or a proxy should not be used for the given request.
func ProxyFromEnvironment(req *Request) (*url.URL, error) {
proxy := getenvEitherCase("HTTP_PROXY")
if proxy == "" {
@@ -86,7 +88,7 @@ func ProxyFromEnvironment(req *Request) (*url.URL, error) {
return nil, nil
}
proxyURL, err := url.Parse(proxy)
- if err != nil {
+ if err != nil || proxyURL.Scheme == "" {
if u, err := url.Parse("http://\" + proxy); err == nil {
proxyURL = u
err = nil
また、src/pkg/net/http/transport_test.go
に TestProxyFromEnvironment
テストが追加されました。
--- a/src/pkg/net/http/transport_test.go
+++ b/src/pkg/net/http/transport_test.go
@@ -16,6 +16,7 @@ import (
. "net/http"
"net/http/httptest"
"net/url"
+ "os"
"runtime"
"strconv"
"strings"
@@ -727,6 +728,36 @@ func TestTransportAltProto(t *testing.T) {
}\n
}\n
\n+var proxyFromEnvTests = []struct {\n+ env string\n+ wanturl string\n+ wanterr error\n+}{\n+ {\"127.0.0.1:8080\", \"http://127.0.0.1:8080\", nil},\n+ {\"http://127.0.0.1:8080\", \"http://127.0.0.1:8080\", nil},\n+ {\"https://127.0.0.1:8080\", \"https://127.0.0.1:8080\", nil},\n+ {\"\", \"<nil>\", nil},\n+}\n+\n+func TestProxyFromEnvironment(t *testing.T) {\n+ os.Setenv(\"HTTP_PROXY\", \"\")\n+ os.Setenv(\"http_proxy\", \"\")\n+ os.Setenv(\"NO_PROXY\", \"\")\n+ os.Setenv(\"no_proxy\", \"\")\n+ for i, tt := range proxyFromEnvTests {\n+ os.Setenv(\"HTTP_PROXY\", tt.env)\n+ req, _ := NewRequest(\"GET\", \"http://example.com\", nil)\n+ url, err := ProxyFromEnvironment(req)\n+ if g, e := fmt.Sprintf(\"%v\", err), fmt.Sprintf(\"%v\", tt.wanterr); g != e {\n+ t.Errorf(\"%d. got error = %q, want %q\", i, g, e)\n+ continue\n+ }\n+ if got := fmt.Sprintf(\"%s\", url); got != tt.wanturl {\n+ t.Errorf(\"%d. got URL = %q, want %q\", i, url, tt.wanturl)\n+ }\n+ }\n+}\n+\n // rgz is a gzip quine that uncompresses to itself.\n var rgz = []byte{\n 0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,\n```
## コアとなるコードの解説
### `src/pkg/net/http/transport.go` の変更
* **ドキュメントの更新**:
`ProxyFromEnvironment` 関数のコメントが修正され、プロキシが環境変数で定義されていない場合や、リクエストにプロキシが使用されるべきでない場合に、`nil URL` と `nil error` が返されることが明記されました。これは関数の振る舞いをより正確に記述し、利用者が期待する戻り値を理解しやすくするための改善です。
* **プロキシURL解析ロジックの修正**:
変更前: `if err != nil {`
変更後: `if err != nil || proxyURL.Scheme == "" {`
この変更がバグ修正の核心です。`url.Parse(proxy)` の結果、`err` が `nil` であっても、`proxyURL.Scheme` が空文字列である場合(例: `127.0.0.1:8080` のようにスキームが指定されていない場合)も、不正なプロキシURLとして扱われるようになりました。
この条件が真の場合、内部の `if u, err := url.Parse("http://" + proxy); err == nil { ... }` ブロックが実行され、`http://` スキームを補完して再解析を試みます。これにより、スキームが欠落しているプロキシ設定も正しく解釈され、`net/http` クライアントがプロキシを介して通信できるようになります。
### `src/pkg/net/http/transport_test.go` の変更
* **`proxyFromEnvTests` 変数の追加**:
様々なプロキシ環境変数の入力 (`env`) と、それに対応する期待されるURL (`wanturl`) およびエラー (`wanterr`) を定義した構造体のスライスです。これにより、複数のテストケースを簡潔に記述できます。
特に `{"127.0.0.1:8080", "http://127.0.0.1:8080", nil}` のテストケースは、スキームが欠落している場合の修正が正しく機能するかを検証しています。
* **`TestProxyFromEnvironment` 関数の追加**:
このテスト関数は、`proxyFromEnvTests` の各テストケースをループで実行します。
1. `os.Setenv` を使用して、テストケースの `env` 値を `HTTP_PROXY` 環境変数に設定します。
2. `NewRequest` でダミーのHTTPリクエストを作成します。
3. `ProxyFromEnvironment` 関数を呼び出し、結果のURLとエラーを取得します。
4. 取得したURLとエラーが、期待される `wanturl` および `wanterr` と一致するかを `t.Errorf` を使って検証します。
このテストの追加により、`ProxyFromEnvironment` 関数の堅牢性が向上し、将来の回帰を防ぐのに役立ちます。
## 関連リンク
* Go Issue #2919: [https://github.com/golang/go/issues/2919](https://github.com/golang/go/issues/2919)
* Go CL 5708055: [https://golang.org/cl/5708055](https://golang.org/cl/5708055)
## 参考にした情報源リンク
* Go Issue #2919 の内容
* Go言語の `net/http` および `net/url` パッケージのドキュメント
* HTTPプロキシ、環境変数に関する一般的な知識
* HTTP `CONNECT` メソッドに関する情報