[インデックス 15065] ファイルの概要
このコミットは、Go言語の標準ライブラリである net/url パッケージにおけるURLのスキーム正規化と、パスの曖昧性に関するドキュメントの追加を目的としています。具体的には、URLスキームを常に小文字に正規化する変更と、URLパス内の %2f と / の区別に関する注意書きが追加されています。
コミット
net/url パッケージにおいて、URLスキーム(例: http, https)を小文字に正規化する変更が加えられました。これにより、HTTP://example.com のような大文字のスキームも http://example.com として扱われるようになります。また、URLの Path フィールドにおける %2f (エンコードされたスラッシュ) と / (デコードされたスラッシュ) の曖昧性について、その挙動と注意点がドキュメントに追加されました。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4085426f7dcfdcb7eff86fe1fd0879e02398926b
元コミット内容
net/url: normalize scheme to lower case (http not HTTP)
Also document %2f vs / ambiguity in URL.Path.
Fixes #3913.
Fixes #3659.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/7225076
変更の背景
この変更には主に二つの背景があります。
-
URLスキームの正規化: URLスキームは大文字・小文字を区別しないとRFC 3986で定義されていますが、実装によっては大文字・小文字を区別してしまうケースがありました。これにより、異なるスキーム表記が異なるリソースとして扱われる可能性があり、一貫性のない挙動やセキュリティ上の問題を引き起こす可能性がありました。例えば、
HTTP://example.comとhttp://example.comが異なるものとして扱われると、キャッシュの不整合やアクセス制御のバイパスなどが発生する可能性があります。このコミットは、スキームを常に小文字に正規化することで、このような問題を回避し、URLの解釈における堅牢性を高めることを目的としています。 -
URLパスの曖昧性に関するドキュメント化: URLパスにおいて、スラッシュ (
/) はパスセグメントの区切り文字として機能します。しかし、スラッシュ自体がパスセグメントの一部として扱われる必要がある場合、%2fのようにパーセントエンコードされることがあります。net/urlパッケージのURL.Pathフィールドは、URLをデコードした形式で保持するため、/%47%6f%2fのようなパスは/Go/とデコードされます。この際、元のURLで%2fだった部分と、元々/だった部分を区別することができません。この曖昧性は通常問題になりませんが、特定の状況(例えば、WebサーバーがリクエストURIを厳密に解釈する必要がある場合や、特定のパス構造に依存するアプリケーション)では重要になることがあります。このコミットは、この挙動を明示的にドキュメント化することで、開発者がこの曖昧性を認識し、必要に応じてreq.RequestURIのような他の手段を用いることを促しています。
コミットメッセージに記載されている Fixes #3913. と Fixes #3659. については、現在のGoのIssueトラッカーでは直接的な関連が見つかりませんでした。これは、古いIssue番号であるか、または内部的なトラッキング番号である可能性があります。しかし、変更内容から推測すると、これらのIssueはスキームの正規化やパスの解釈に関するバグ報告や改善提案であったと考えられます。
前提知識の解説
- URL (Uniform Resource Locator): インターネット上のリソースの位置を示す識別子です。スキーム、ホスト、ポート、パス、クエリ、フラグメントなどの要素で構成されます。
- URLスキーム: URLの先頭部分で、リソースにアクセスするためのプロトコル(例:
http,https,ftp,mailto)を示します。RFC 3986では、スキームは大文字・小文字を区別しないと規定されています。 - URLパス: URLのホスト名の後に続く部分で、サーバー上のリソースの階層的な位置を示します。スラッシュ (
/) でセグメントが区切られます。 - パーセントエンコーディング: URLに使用できない文字(スペース、日本語など)や、URLの構文上で特別な意味を持つ文字(
/,?,#など)を、%の後に2桁の16進数で表現するエンコーディング方式です。例えば、スペースは%20、スラッシュは%2fとなります。 net/urlパッケージ: Go言語の標準ライブラリで、URLの解析、構築、エンコード、デコードを行うための機能を提供します。url.URL構造体は、解析されたURLの各要素を保持します。URL.Pathフィールド:url.URL構造体の一部で、URLのパス部分をデコードされた文字列として保持します。req.RequestURI(HTTPサーバー): HTTPリクエストの元のURI全体(パス、クエリ文字列を含む)を、サーバーが受け取ったそのままの形式で保持します。これは、net/urlパッケージが解析・デコードする前の生のリクエストURIが必要な場合に有用です。
技術的詳細
このコミットは、net/url パッケージの parse 関数と URL 構造体のドキュメントに焦点を当てています。
-
スキームの正規化:
parse関数内で、URLスキームを抽出した後、strings.ToLower()関数を使用してスキーム全体を小文字に変換しています。これにより、入力されたURLスキームが大文字・小文字のいずれであっても、url.URL構造体のSchemeフィールドには常に小文字のスキームが格納されるようになります。これは、URLの比較やルーティングにおいて一貫性を保つ上で非常に重要です。 -
パスの曖昧性に関するドキュメント追加:
URL構造体の定義部分にコメントが追加されました。このコメントは、URL.Pathフィールドがデコードされた形式でパスを格納するため、元のURLで%2fだったスラッシュと、元々/だったスラッシュを区別できないことを明確に説明しています。そして、この区別が重要な場合には、HTTPサーバーであればreq.RequestURIを参照すること、HTTPクライアントであればURL{Opaque: "/Go%2f"}のようにOpaqueフィールドを使用して生のパスを保持する方法を提案しています。Opaqueフィールドは、スキーム固有の階層的なパスを持たないURL(例:mailto:,ftp:)や、パスをデコードせずにそのまま保持したい場合に利用されます。
コアとなるコードの変更箇所
src/pkg/net/url/url.go
--- a/src/pkg/net/url/url.go
+++ b/src/pkg/net/url/url.go
@@ -220,6 +220,12 @@ func escape(s string, mode encoding) string {
//
//
// scheme:opaque[?query][#fragment]
//
+// Note that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/.\
+// A consequence is that it is impossible to tell which slashes in the Path were\
+// slashes in the raw URL and which were %2f. This distinction is rarely important,\
+// but when it is a client must use other routines to parse the raw URL or construct\
+// the parsed URL. For example, an HTTP server can consult req.RequestURI, and\
+// an HTTP client can use URL{Opaque: "/Go%2f"} instead of URL{Path: "/Go/"}.\
type URL struct {
Scheme string
Opaque string // encoded opaque data
@@ -371,6 +377,7 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) {
if url.Scheme, rest, err = getscheme(rawurl); err != nil {
goto Error
}
+ url.Scheme = strings.ToLower(url.Scheme)
rest, url.RawQuery = split(rest, '?', true)
src/pkg/net/url/url_test.go
--- a/src/pkg/net/url/url_test.go
+++ b/src/pkg/net/url/url_test.go
@@ -251,6 +251,15 @@ var urltests = []URLTest{
},
"file:///home/adg/rabbits",
},
+ // case-insensitive scheme
+ {
+ "MaIlTo:webmaster@golang.org",
+ &URL{
+ Scheme: "mailto",
+ Opaque: "webmaster@golang.org",
+ },
+ "mailto:webmaster@golang.org",
+ },
}
コアとなるコードの解説
src/pkg/net/url/url.go
-
URL構造体へのコメント追加:type URL struct { ... }の直前に、Pathフィールドの挙動に関する詳細な説明が追加されています。これは、/%47%6f%2fが/Go/とデコードされる例を挙げ、元のスラッシュが%2fだったのか、それとも元々/だったのかを区別できないことを強調しています。そして、この区別が重要な場合の対処法として、HTTPサーバーにおけるreq.RequestURIの利用と、HTTPクライアントにおけるURL{Opaque: "/Go%2f"}の利用が示されています。このドキュメントは、net/urlパッケージの利用者に対して、潜在的な曖昧性を認識させ、適切なAPIの選択を促すための重要な情報です。 -
parse関数内のスキーム正規化:url.Scheme = strings.ToLower(url.Scheme)の行が追加されました。これは、getscheme関数によって抽出されたURLスキーム(例:HTTP,MAILTO)を、stringsパッケージのToLower関数を使ってすべて小文字に変換しています。この変更により、url.URL構造体のSchemeフィールドには常に小文字のスキームが格納されることが保証され、URLの比較や処理における一貫性が向上します。
src/pkg/net/url/url_test.go
- テストケースの追加:
urltests変数に新しいテストケースが追加されました。このテストケースは、MaIlTo:webmaster@golang.orgのように大文字・小文字が混在したスキームを持つURLが、正しくmailtoスキームとして解析されることを検証しています。具体的には、Schemeフィールドがmailtoとなり、Opaqueフィールドがwebmaster@golang.orgとなることを期待しています。このテストは、url.Scheme = strings.ToLower(url.Scheme)の変更が意図通りに機能していることを確認するためのものです。
関連リンク
- RFC 3986 - Uniform Resource Identifier (URI): Generic Syntax: URLの構文に関する標準仕様。スキームの大文字・小文字の区別についても言及されています。
- Go言語
net/urlパッケージのドキュメント:
参考にした情報源リンク
- Go言語の公式ドキュメント
- RFC 3986
- GitHubのコミット履歴
- Go言語のIssueトラッカー (ただし、今回のIssue番号は直接的な関連が見つからなかったため、一般的な情報源として)