[インデックス 11828] ファイルの概要
このコミットは、Go言語の標準ライブラリであるnet/http
パッケージにおけるHTTPプロキシのパース処理のバグを修正するものです。具体的には、http_proxy
環境変数で指定されたプロキシURLの解析が不適切であったために、プロキシが正しく適用されないケースがあった問題(Issue 2919)を解決します。
コミット
commit fb2caa3244184d73d0185dce2c8b594ff6e60c06
Author: Russ Cox <rsc@golang.org>
Date: Sun Feb 12 23:19:50 2012 -0500
net/http: fix http_proxy parsing
Fixes #2919.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5645089
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fb2caa3244184d73d0185dce2c8b594ff6e60c06
元コミット内容
net/http: fix http_proxy parsing
Fixes #2919.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5645089
変更の背景
このコミットは、Go言語のnet/http
パッケージがHTTPプロキシを適切に処理できないという問題、特にIssue 2919で報告された「net/http: not using proxy for https get」というバグを修正するために行われました。
当時のnet/http
パッケージは、http_proxy
環境変数からプロキシ設定を読み取る際に、プロキシURLのパース方法に問題がありました。具体的には、http_proxy
の値がhttp://
のようなスキームを含まない形式(例: localhost:8080
)で指定された場合、url.ParseRequest
関数がこれを正しくURLとして認識できず、結果としてプロキシが適用されないという挙動を示していました。特にHTTPSリクエストの場合にこの問題が顕著でした。
この問題は、ユーザーが環境変数を通じてプロキシを設定しているにもかかわらず、Goアプリケーションがそのプロキシを使用しないという予期せぬ動作を引き起こし、ネットワーク接続に影響を与える可能性がありました。そのため、プロキシ設定の堅牢性を高め、ユーザーの期待通りの動作を保証するために、このパースロジックの修正が必要とされました。
前提知識の解説
1. net/http
パッケージ
Go言語の標準ライブラリであるnet/http
パッケージは、HTTPクライアントとサーバーの実装を提供します。ウェブアプリケーションの構築や、HTTPリクエストの送信など、ネットワーク通信の基盤となります。
2. HTTPプロキシ
HTTPプロキシは、クライアントとサーバーの間に位置し、クライアントからのリクエストをサーバーに転送し、サーバーからのレスポンスをクライアントに転送する役割を担います。これにより、セキュリティの強化、キャッシュによるパフォーマンス向上、アクセス制御、匿名性の確保などが可能になります。
3. http_proxy
環境変数
多くのオペレーティングシステムやアプリケーションでは、HTTPプロキシの設定を環境変数を通じて行います。http_proxy
環境変数は、HTTPリクエストに使用するプロキシサーバーのアドレスを指定するために広く利用されています。通常、http://host:port
のような形式で指定されます。
4. url.URL
構造体
Go言語のnet/url
パッケージに含まれるurl.URL
構造体は、URLの各構成要素(スキーム、ホスト、パス、クエリなど)を構造化して表現するためのものです。URLの解析や構築に利用されます。
5. url.Parse
とurl.ParseRequest
(当時の状況)
url.Parse(rawurl string) (*URL, error)
: この関数は、与えられた文字列を絶対URLとして解析しようとします。スキーム(例:http://
)が含まれていない場合、相対URLとして扱われる可能性があります。url.ParseRequest(rawurl string) (*URL, error)
: この関数は、HTTPリクエストラインの一部として解釈されるURLを解析するために設計されていました。これは、ブラウザがアドレスバーに入力された文字列を解釈する方法に似ており、スキームが省略されている場合でも、一般的なホスト名やパスをURLとして解釈しようとします。しかし、この関数は後に非推奨となり、Go 1.16で削除されました。これは、その挙動が曖昧で、セキュリティ上の懸念があったためです。このコミットが作成された2012年当時はまだ存在し、使用されていました。
このコミットの文脈では、url.ParseRequest
がスキームなしのプロキシURLを正しく解釈できないという問題が焦点となっています。
技術的詳細
このコミットの技術的な核心は、src/pkg/net/http/transport.go
ファイル内のProxyFromEnvironment
関数の修正にあります。この関数は、http_proxy
環境変数からプロキシURLを解析し、*url.URL
型で返す役割を担っています。
修正前のコードでは、http_proxy
の値(proxy
変数)を直接url.ParseRequest(proxy)
で解析しようとしていました。しかし、前述の通り、http_proxy
がhost:port
のようなスキームなしの形式で指定された場合、url.ParseRequest
はこれを有効なURLとして認識できず、エラーを返していました。このエラーが発生すると、ProxyFromEnvironment
関数は「invalid proxy address」というエラーを返してしまい、プロキシが使用されませんでした。
修正後のコードでは、この問題を解決するために以下のロジックが導入されました。
- まず、
url.Parse(proxy)
を試みます。これは、http_proxy
が完全なURL形式(例:http://host:port
)で指定されている場合に正しく解析されることを期待します。 - もし
url.Parse(proxy)
がエラーを返した場合、それはproxy
文字列がスキームを含まない形式である可能性が高いと判断します。 - この場合、
"http://"
というスキームをproxy
文字列の先頭に付加し、url.Parse("http://" + proxy)
を再度試みます。 - この再試行が成功した場合、
proxyURL
は正しく解析されたURLとなり、エラーもnil
にリセットされます。これにより、スキームが省略されたプロキシ設定も正しく処理できるようになります。 - 最終的に、どちらのパース試行も失敗した場合にのみ、
fmt.Errorf("invalid proxy address %q: %v", proxy, err)
というより詳細なエラーメッセージを返します。これにより、デバッグ時の情報も増えます。
この変更により、http_proxy
環境変数がhost:port
形式で指定された場合でも、net/http
パッケージが自動的にhttp://
スキームを補完してプロキシを正しく認識し、利用できるようになりました。
コアとなるコードの変更箇所
--- a/src/pkg/net/http/transport.go
+++ b/src/pkg/net/http/transport.go
@@ -85,16 +85,16 @@ func ProxyFromEnvironment(req *Request) (*url.URL, error) {
if !useProxy(canonicalAddr(req.URL)) {
return nil, nil
}
- proxyURL, err := url.ParseRequest(proxy)
+ proxyURL, err := url.Parse(proxy)
if err != nil {
- return nil, errors.New("invalid proxy address")
- }\n-\tif proxyURL.Host == "" {\n-\t\tproxyURL, err = url.ParseRequest("http://" + proxy)\n-\t\tif err != nil {\n-\t\t\treturn nil, errors.New("invalid proxy address")
+ if u, err := url.Parse("http://" + proxy); err == nil {
+ proxyURL = u
+ err = nil
}
}
+ if err != nil {
+ return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err)
+ }
return proxyURL, nil
}
コアとなるコードの解説
変更はsrc/pkg/net/http/transport.go
ファイルのProxyFromEnvironment
関数内で行われています。
-
- proxyURL, err := url.ParseRequest(proxy)
- 変更前は、
http_proxy
環境変数から取得したプロキシ文字列proxy
をurl.ParseRequest
関数で直接解析しようとしていました。これが、スキームなしのプロキシURLを正しく扱えない原因でした。
- 変更前は、
-
+ proxyURL, err := url.Parse(proxy)
- 変更後、まず
url.Parse
関数を使用してプロキシ文字列を解析します。url.Parse
はより厳密なURL解析を行い、完全なURL形式を期待します。
- 変更後、まず
-
- if err != nil { return nil, errors.New("invalid proxy address") }
- 変更前は、最初の
url.ParseRequest
がエラーを返した場合、すぐに「invalid proxy address」という汎用的なエラーを返して処理を終了していました。
- 変更前は、最初の
-
- if proxyURL.Host == "" { ... }
- 変更前は、
url.ParseRequest
で解析したproxyURL
のHost
フィールドが空の場合(つまり、スキームなしでホスト名が認識されなかった場合)、"http://"
を付加して再度url.ParseRequest
を試みていました。しかし、この二度目の試行もurl.ParseRequest
の挙動に依存しており、問題の根本的な解決にはなっていませんでした。
- 変更前は、
-
+ if err != nil { if u, err := url.Parse("http://" + proxy); err == nil { proxyURL = u; err = nil } }
- これが変更の核心部分です。最初の
url.Parse(proxy)
がエラーを返した場合(つまり、proxy
が完全なURL形式でなかった場合)、このブロックに入ります。 - ここで、
"http://"
をproxy
文字列の先頭に付加し、再度url.Parse
を試みます。 - もしこの再試行が成功した場合(
err == nil
)、新しく解析されたURLu
をproxyURL
に代入し、エラーもnil
にリセットします。これにより、host:port
形式のプロキシも正しくURLとして認識されるようになります。
- これが変更の核心部分です。最初の
-
+ if err != nil { return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err) }
- 最終的なエラーチェックです。最初の
url.Parse
も、http://
を付加した後のurl.Parse
も両方失敗した場合にのみ、このエラーハンドリングに到達します。 fmt.Errorf
を使用することで、元のプロキシ文字列proxy
と、発生した具体的なエラーerr
をエラーメッセージに含めるようになり、デバッグ情報が格段に向上しました。
- 最終的なエラーチェックです。最初の
この修正により、ProxyFromEnvironment
関数は、http_proxy
環境変数がhttp://host:port
形式でもhost:port
形式でも、より堅牢にプロキシURLを解析できるようになりました。
関連リンク
- Go Issue 2919: https://github.com/golang/go/issues/2919
- Gerrit Change 5645089: https://golang.org/cl/5645089
参考にした情報源リンク
- golang/go Issue #2919: net/http: not using proxy for https get
- GoDoc: net/url package (現在のドキュメント。
ParseRequest
は削除済み) - GoDoc: net/http package