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

[インデックス 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

変更の背景

この変更には主に二つの背景があります。

  1. URLスキームの正規化: URLスキームは大文字・小文字を区別しないとRFC 3986で定義されていますが、実装によっては大文字・小文字を区別してしまうケースがありました。これにより、異なるスキーム表記が異なるリソースとして扱われる可能性があり、一貫性のない挙動やセキュリティ上の問題を引き起こす可能性がありました。例えば、HTTP://example.comhttp://example.com が異なるものとして扱われると、キャッシュの不整合やアクセス制御のバイパスなどが発生する可能性があります。このコミットは、スキームを常に小文字に正規化することで、このような問題を回避し、URLの解釈における堅牢性を高めることを目的としています。

  2. 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 構造体のドキュメントに焦点を当てています。

  1. スキームの正規化: parse 関数内で、URLスキームを抽出した後、strings.ToLower() 関数を使用してスキーム全体を小文字に変換しています。これにより、入力されたURLスキームが大文字・小文字のいずれであっても、url.URL 構造体の Scheme フィールドには常に小文字のスキームが格納されるようになります。これは、URLの比較やルーティングにおいて一貫性を保つ上で非常に重要です。

  2. パスの曖昧性に関するドキュメント追加: 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) の変更が意図通りに機能していることを確認するためのものです。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • RFC 3986
  • GitHubのコミット履歴
  • Go言語のIssueトラッカー (ただし、今回のIssue番号は直接的な関連が見つからなかったため、一般的な情報源として)