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

[インデックス 11198] ファイルの概要

このコミットは、Go言語の実験的なプロキシパッケージ (exp/proxy) におけるビルドエラーを修正するものです。具体的には、net/url パッケージの内部的な変更(URL構造におけるユーザー情報へのアクセス方法の変更)に対応し、プロキシがURLからユーザー名とパスワードを正しく抽出できるように更新しています。

コミット

commit 9b54af20204128a655ccea895bf668a5ef4e0309
Author: Gustavo Niemeyer <gustavo@niemeyer.net>
Date:   Tue Jan 17 00:55:35 2012 -0200

    exp/proxy: fix build after URL changes
    
    R=golang-dev
    CC=golang-dev
    https://golang.org/cl/5540062

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/9b54af20204128a655ccea895bf668a5ef4e0309

元コミット内容

exp/proxy: fix build after URL changes

このコミットは、net/url パッケージにおけるURLのユーザー情報(ユーザー名とパスワード)の扱いに関するAPI変更に対応するため、exp/proxy パッケージのビルドを修正するものです。以前は u.RawUserinfo というフィールドを直接文字列として解析していましたが、新しいAPIでは u.User という *url.Userinfo 型の構造体を通じて、より安全かつ構造化された方法でユーザー名とパスワードにアクセスするよう変更されました。

変更の背景

Go言語の標準ライブラリである net/url パッケージは、URLの解析と構築を扱う上で非常に重要な役割を担っています。このコミットが行われた2012年1月頃、net/url パッケージ内でURLのユーザー情報(user:password@ の部分)の表現方法とアクセス方法に重要な変更が加えられました。

具体的には、以前は url.URL 構造体の RawUserinfo フィールドが string 型で提供されており、開発者はこの文字列を自分でパースしてユーザー名とパスワードを抽出する必要がありました。しかし、これはセキュリティ上のリスク(パスワードが平文で扱われる可能性)や、パースロジックの重複、エラーハンドリングの複雑さといった問題を引き起こす可能性がありました。

この問題を解決するため、net/url パッケージは User フィールド(型は *url.Userinfo)を導入しました。url.Userinfo 構造体は、ユーザー名とパスワードを個別のフィールドとして持ち、パスワードへのアクセスには Password() メソッドを使用することで、パスワードの存在チェックや安全な取得を可能にしました。特に、Redacted() メソッドの導入(関連する変更リスト golang.org/cl/5540062 で確認できる)は、ログ出力などでパスワードが誤って露出するのを防ぐためのセキュリティ強化策の一環でした。

この net/url パッケージのAPI変更により、RawUserinfo を直接参照していた既存のコードはビルドエラーとなるか、意図しない動作をするようになりました。本コミットは、exp/proxy パッケージがこの新しいAPIに準拠し、引き続き正しく機能するようにするための修正です。

前提知識の解説

  • Go言語の net/url パッケージ: Go言語でURLを解析、構築、操作するための標準ライブラリです。url.URL 構造体は、スキーム、ホスト、パス、クエリパラメータ、フラグメント、そしてユーザー情報など、URLの各要素を表現します。
  • url.URL 構造体:
    • RawUserinfo string: (変更前) URLのユーザー情報部分(例: "user:password")をそのままの文字列で保持していました。
    • User *Userinfo: (変更後) URLのユーザー情報部分を構造化された *url.Userinfo 型で保持します。
  • url.Userinfo 構造体:
    • Username() string: ユーザー名を返します。
    • Password() (string, bool): パスワードを返します。パスワードが存在しない場合は空文字列と false を返します。これにより、パスワードの有無を安全に確認できます。
  • exp パッケージ: Go言語の標準ライブラリには、まだ安定版ではないが将来的に標準ライブラリに取り込まれる可能性のある実験的なパッケージが exp ディレクトリに置かれることがあります。exp/proxy もその一つで、プロキシ機能を提供します。
  • ビルドエラー: ソースコードがコンパイラの構文規則や型チェックに違反している場合に発生するエラーです。この場合、net/url のAPI変更により、古いコードが新しいAPIと互換性がなくなったためにビルドエラーが発生しました。

技術的詳細

このコミットの技術的詳細は、net/url パッケージのAPI変更への適応に集約されます。

  1. strings パッケージの削除: 変更前は u.RawUserinfo という単一の文字列からユーザー名とパスワードを抽出するために strings.SplitN 関数を使用していました。このため strings パッケージをインポートしていました。 変更後、u.User フィールドが *url.Userinfo 型となり、その中にユーザー名とパスワードを個別に取得するメソッド (Username(), Password()) が提供されたため、strings パッケージは不要となり、インポートリストから削除されました。これはコードの依存関係を減らし、クリーンさを保つ上で良い変更です。

  2. ユーザー情報抽出ロジックの変更:

    • 旧ロジック:

      if len(u.RawUserinfo) > 0 {
          auth = new(Auth)
          parts := strings.SplitN(u.RawUserinfo, ":", 1)
          if len(parts) == 1 {
              auth.User = parts[0]
          } else if len(parts) >= 2 {
              auth.User = parts[0]
              auth.Password = parts[1]
          }
      }
      

      このコードは RawUserinfo: で分割し、ユーザー名とパスワードを手動で抽出していました。パスワードがない場合(例: user@example.com)や、パスワードに : が含まれる場合(非常に稀ですが)に、ロジックが複雑になったり、意図しない動作をする可能性がありました。

    • 新ロジック:

      if u.User != nil {
          auth = new(Auth)
          auth.User = u.User.Username()
          if p, ok := u.User.Password(); ok {
              auth.Password = p
          }
      }
      

      新しいロジックでは、まず u.Usernil でないかを確認します。これはURLにユーザー情報が含まれているかどうかのチェックです。 次に、u.User.Username() を呼び出すことで、URLからユーザー名を直接取得します。 パスワードについては、u.User.Password() を呼び出します。このメソッドは2つの戻り値を持ちます。1つ目はパスワード文字列、2つ目はパスワードが存在するかどうかを示す bool 値です。これにより、パスワードが存在しない場合に誤って空文字列をパスワードとして扱ってしまうことを防ぎ、より堅牢なコードになっています。

この変更は、net/url パッケージが提供する新しい、より安全で構造化されたAPIを利用することで、コードの可読性、保守性、そして堅牢性を向上させています。

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

src/pkg/exp/proxy/proxy.go ファイルの FromURL 関数内。

--- a/src/pkg/exp/proxy/proxy.go
+++ b/src/pkg/exp/proxy/proxy.go
@@ -11,7 +11,6 @@ import (
 	"net"
 	"net/url"
 	"os"
-	"strings"
 )
 
 // A Dialer is a means to establish a connection.
@@ -70,14 +69,11 @@ func RegisterDialerType(scheme string, f func(*url.URL, Dialer) (Dialer, error))
 // Dialer for it to make network requests.
 func FromURL(u *url.URL, forward Dialer) (Dialer, error) {
 	var auth *Auth
-	if len(u.RawUserinfo) > 0 {
+	if u.User != nil {
 		auth = new(Auth)
-		parts := strings.SplitN(u.RawUserinfo, ":", 1)
-		if len(parts) == 1 {
-			auth.User = parts[0]
-		} else if len(parts) >= 2 {
-			auth.User = parts[0]
-			auth.Password = parts[1]
+		auth.User = u.User.Username()
+		if p, ok := u.User.Password(); ok {
+			auth.Password = p
 		}
 	}
 

コアとなるコードの解説

変更の中心は FromURL 関数です。この関数は url.URL オブジェクトを受け取り、それに基づいてプロキシの Dialer を構築する際に、URLに含まれる認証情報(ユーザー名とパスワード)を抽出します。

  1. import "strings" の削除: strings パッケージは、以前 u.RawUserinfo を手動でパースするために使用されていましたが、新しい u.User APIでは不要になったため削除されました。これにより、不要な依存関係が取り除かれ、コードがより簡潔になりました。

  2. if len(u.RawUserinfo) > 0 から if u.User != nil への変更: URLにユーザー情報が含まれているかどうかのチェック方法が変更されました。

    • 旧: RawUserinfo 文字列の長さが0より大きいかで判断。
    • 新: u.User フィールドが nil でないかで判断。u.User*url.Userinfo 型のポインタであり、ユーザー情報が存在しない場合は nil となります。これはよりGoらしい(idiomatic Go)チェック方法です。
  3. ユーザー名とパスワードの抽出ロジックの変更:

    • 旧: strings.SplitN を使って RawUserinfo: で分割し、ユーザー名とパスワードを配列から取得していました。この方法は手動でのパースが必要で、エラーハンドリングが複雑になる可能性がありました。
    • 新: u.User.Username()u.User.Password() メソッドを使用します。
      • auth.User = u.User.Username(): url.Userinfo オブジェクトからユーザー名を直接取得します。
      • if p, ok := u.User.Password(); ok { auth.Password = p }: Password() メソッドはパスワード文字列と、パスワードが存在するかどうかを示すブール値を返します。この ok 変数を使って、パスワードが存在する場合のみ auth.Password に設定することで、より安全かつ正確にパスワードを扱っています。

この変更により、exp/proxy パッケージは net/url パッケージの最新かつ推奨されるAPIを使用するようになり、将来的な互換性と堅牢性が向上しました。

関連リンク

  • Go言語の net/url パッケージのドキュメント: https://pkg.go.dev/net/url
  • Go言語の exp/proxy パッケージのドキュメント (当時のもの): https://pkg.go.dev/exp/proxy (現在のGoのバージョンでは exp パッケージは標準ライブラリには含まれていない可能性があります。当時の実験的なパッケージの場所を示しています。)

参考にした情報源リンク