[インデックス 12846] ファイルの概要
このコミットは、Go言語のnet/url
パッケージにおけるURLエスケープ処理を、より厳密なRFC 3986の仕様に準拠させるための修正です。具体的には、shouldEscape
関数がRFC 3986で予約文字として定義されている一部の文字を正しくエスケープしていなかった問題を解決し、一部の厳格なWebサーバーとの互換性を向上させます。
コミット
commit 56024fa64ebe87d5b209fa5d126b71ab27f7cf3d
Author: Stéphane Travostino <stephane.travostino@gmail.com>
Date: Thu Apr 5 13:23:08 2012 -0400
net/url: Correctly escape URL as per RFC 3986
The shouldEscape function did not correctly escape the reserved characters listed in RFC 3986 §2.2, breaking some strict web servers.
Fixes #3433.
R=rsc
CC=golang-dev
https://golang.org/cl/5970050
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/56024fa64ebe87d5b209fa5d126b71ab27f7cf3d
元コミット内容
net/url: Correctly escape URL as per RFC 3986
The shouldEscape function did not correctly escape the reserved characters listed in RFC 3986 §2.2, breaking some strict web servers.
Fixes #3433.
R=rsc
CC=golang-dev
https://golang.org/cl/5970050
変更の背景
この変更の背景には、Go言語のnet/url
パッケージが提供するURLエスケープ機能が、一部の「予約文字(Reserved Characters)」を適切に処理していなかったという問題があります。
URLのエスケープ処理は、URLの構文を維持しつつ、特殊な意味を持つ文字や非ASCII文字を安全に表現するために不可欠です。RFC 2396はURLの一般的な構文を定義していましたが、その後のRFC 3986はURI(Uniform Resource Identifier)の一般的な構文をより詳細に定義し、URLのエスケープに関する規則も更新しました。
Goのnet/url
パッケージのshouldEscape
関数は、当初RFC 2396に基づいて実装されていましたが、RFC 3986のセクション2.2で定義されている予約文字の一部(特に!
, '
, (
, )
, *
)を「非予約文字(Unreserved Characters)」として扱っていました。これにより、これらの文字を含むURLが生成された場合、一部の厳格なWebサーバーがRFC 3986の仕様に厳密に従ってURLを解析するため、不正なURLとして認識し、リクエストが失敗する可能性がありました。
この問題は、GoのIssue #3433として報告され、特定のWebサーバーとの相互運用性の問題を引き起こしていました。このコミットは、この問題を解決し、GoのURLエスケープ処理をRFC 3986の最新かつ厳密な仕様に準拠させることを目的としています。
前提知識の解説
URLエスケープとは
URLエスケープ(パーセントエンコーディングとも呼ばれる)は、URL内で特別な意味を持つ文字(例: /
, ?
, &
, =
)や、URLとして直接表現できない文字(例: スペース、日本語などの非ASCII文字)を、安全にURLに含めるためのメカニズムです。これらの文字は、%
の後にその文字のASCII値またはUTF-8エンコーディングの16進数を続ける形式で表現されます(例: スペースは%20
)。
RFC 2396とRFC 3986
-
RFC 2396 (Uniform Resource Identifiers (URI): Generic Syntax): 1998年に公開されたURIの一般的な構文を定義した初期の標準です。URL(Uniform Resource Locator)はURIの一種です。このRFCでは、URIを構成する文字を「予約文字(Reserved Characters)」と「非予約文字(Unreserved Characters)」に分類し、エスケープの必要性を規定しました。
- 予約文字: URIの構文デリミタとして特別な意味を持つ文字(例:
?
,/
,#
,&
,=
など)。これらは、その文字が持つ特別な意味で使われる場合はエスケープされませんが、データの一部として使われる場合はエスケープが必要です。 - 非予約文字: URI内で特別な意味を持たず、常にそのまま使用できる文字。これらはエスケープする必要がありません。RFC 2396では、英数字と一部の記号(
-
,_
,.
,!
,~
,*
,'
,(
,)
)が非予約文字とされていました。
- 予約文字: URIの構文デリミタとして特別な意味を持つ文字(例:
-
RFC 3986 (Uniform Resource Identifier (URI): Generic Syntax): 2005年に公開されたRFC 2396の改訂版であり、現在のURIの主要な標準です。このRFCは、URIの構文をより明確にし、特にエスケープに関する規則を更新しました。RFC 3986では、RFC 2396で非予約文字とされていた一部の記号(
!
,'
,(
,)
,*
)が、より厳密な解釈の下で「予約文字」または「サブデリミタ(sub-delims)」の一部として扱われるようになりました。これにより、これらの文字は、特定のコンテキストで特別な意味を持たない限り、エスケープされるべきであるという解釈が強まりました。
予約文字と非予約文字(RFC 3986の観点から)
RFC 3986では、URIを構成する文字は以下のカテゴリに分類されます。
-
予約文字 (Reserved Characters):
gen-delims
(general delimiters):?
,/
,#
,[
,]
,@
,:
sub-delims
(sub-delimiters):!
,$
,&
,'
,(
,)
,*
,+
,,
,;
,=
これらの文字は、URIの構文上の区切り文字として使用されるため、データの一部として使用する場合はパーセントエンコードする必要があります。
-
非予約文字 (Unreserved Characters):
- 英数字 (
ALPHA
,DIGIT
) -
,.
,_
,~
これらの文字は、URI内で特別な意味を持たず、常にそのまま使用できます。したがって、パーセントエンコードする必要はありません。
- 英数字 (
このコミットのポイントは、RFC 2396では非予約文字とされていた!
, '
, (
, )
, *
が、RFC 3986ではsub-delims
の一部として予約文字に分類された点にあります。GoのshouldEscape
関数がこれらの文字をエスケープしないままだと、RFC 3986に厳密に従うシステムでは問題が発生する可能性がありました。
技術的詳細
このコミットの技術的な核心は、Go言語のnet/url
パッケージ内のshouldEscape
関数のロジック変更にあります。この関数は、与えられたバイトがURLの一部としてエスケープされるべきかどうかを決定します。
変更前は、shouldEscape
関数はRFC 2396の定義に基づいており、以下の文字を非予約文字(エスケープ不要)として扱っていました。
- 英数字 (
A-Z
,a-z
,0-9
) - マーク文字 (
-
,_
,.
,!
,~
,*
,'
,(
,)
)
しかし、RFC 3986のセクション2.3「Unreserved Characters」では、非予約文字は英数字と'-'
, '.'
, '_'
, '~'
のみと定義されています。RFC 2396で非予約文字とされていた'!'
, '*'
, ''
, '('
, ')'
は、RFC 3986では「予約文字」のサブカテゴリである「サブデリミタ(sub-delims)」に含まれるようになりました。
このコミットでは、shouldEscape
関数から、RFC 3986で非予約文字ではないとされた'!'
, '*'
, ''
, '('
, ')'
が、エスケープ不要な文字のリストから削除されました。これにより、これらの文字はデフォルトでエスケープされるようになります。
また、テストファイルsrc/pkg/net/url/url_test.go
のescapeTests
配列に、これらの変更された文字(!
, '
, (
, )
, *
)を含む新しいテストケースが追加され、それらが正しくエスケープされることを確認しています。さらに、RFC 3986で予約文字とされている:
、/
、@
、$
、,
、;
もテストケースに追加され、これらも適切にエスケープされることを確認しています。
この修正により、Goのnet/url
パッケージは、RFC 3986に厳密に準拠したURLエスケープ処理を提供し、より広範なWebサーバーやシステムとの互換性が確保されます。
コアとなるコードの変更箇所
diff --git a/src/pkg/net/url/url.go b/src/pkg/net/url/url.go
index 88ff7ebfef..b6e79adc29 100644
--- a/src/pkg/net/url/url.go
+++ b/src/pkg/net/url/url.go
@@ -61,16 +61,16 @@ func (e EscapeError) Error() string {
}
// Return true if the specified character should be escaped when
-// appearing in a URL string, according to RFC 2396.
+// appearing in a URL string, according to RFC 3986.
// When 'all' is true the full range of reserved characters are matched.
func shouldEscape(c byte, mode encoding) bool {
-\t// RFC 2396 §2.3 Unreserved characters (alphanum)
+\t// §2.3 Unreserved characters (alphanum)
\tif 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {
\t\treturn false
\t}\n-\t// TODO: Update the character sets after RFC 3986.\n+\n \tswitch c {\n-\tcase '-', '_', '.', '!', '~', '*', '\'', '(', ')': // §2.3 Unreserved characters (mark)\n+\tcase '-', '_', '.', '~': // §2.3 Unreserved characters (mark)\n \t\treturn false
\n \tcase '$', '&', '+', ',', '/', ':', ';', '=', '?', '@': // §2.2 Reserved characters (reserved)\ndiff --git a/src/pkg/net/url/url_test.go b/src/pkg/net/url/url_test.go
index 2d911ed505..d8b253142f 100644
--- a/src/pkg/net/url/url_test.go
+++ b/src/pkg/net/url/url_test.go
@@ -394,8 +394,8 @@ var escapeTests = []EscapeTest{
\t\tnil,\n \t},\n \t{\n-\t\t" ?&=#+%!<>#\"{}|\\\\^[]`☺\\t",\n-\t\t"+%3F%26%3D%23%2B%25!%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09",\n+\t\t" ?&=#+%!<>#\"{}|\\\\^[]`☺\\t:/@$'()*,;",\n+\t\t"+%3F%26%3D%23%2B%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09%3A%2F%40%24%27%28%29%2A%2C%3B",\n \t\tnil,\n \t},\n }\n```
## コアとなるコードの解説
### `src/pkg/net/url/url.go`
* **`shouldEscape`関数のコメント変更**:
* `// appearing in a URL string, according to RFC 2396.` から
`// appearing in a URL string, according to RFC 3986.` へ変更されました。
これは、この関数が準拠するRFCのバージョンがRFC 2396からRFC 3986へ更新されたことを明確に示しています。
* **非予約文字の定義の変更**:
* `// RFC 2396 §2.3 Unreserved characters (alphanum)` のコメントが
`// §2.3 Unreserved characters (alphanum)` に簡略化されました。
* `// TODO: Update the character sets after RFC 3986.` のコメントが削除されました。これは、このコミットによってRFC 3986への対応が完了したことを意味します。
* `switch c`文内の非予約文字のリストが変更されました。
* 変更前: `case '-', '_', '.', '!', '~', '*', '\'', '(', ')':`
* 変更後: `case '-', '_', '.', '~':`
これにより、`'!'`, `'*'`, `''`, `'('`, `')'`の5つの文字が非予約文字のリストから削除されました。これらの文字はRFC 3986では予約文字(サブデリミタ)として扱われるため、`shouldEscape`関数はこれらの文字に対して`true`を返すようになり、結果としてURLエンコードされるようになります。
### `src/pkg/net/url/url_test.go`
* **`escapeTests`配列のテストケースの更新**:
* 既存のテストケースが更新され、より多くの文字がエスケープされることを検証しています。
* 変更前: `" ?&=#+%!<>#\"{}|\\\\^[]`☺\\t"`
* 変更後: `" ?&=#+%!<>#\"{}|\\\\^[]`☺\\t:/@$'()*,;"`
* 追加された文字: `:/@$'()*,;`
* 対応する期待されるエスケープ結果も更新されています。
* 変更前: `"+%3F%26%3D%23%2B%25!%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09"`
* 変更後: `"+%3F%26%3D%23%2B%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09%3A%2F%40%24%27%28%29%2A%2C%3B"`
特に注目すべきは、`!`が`%21`に、`'`が`%27`に、`(`が`%28`に、`)`が`%29`に、`*`が`%2A`にエスケープされるようになった点です。これは、`shouldEscape`関数の変更が正しく機能していることを確認しています。また、`:`、`/`、`@`、`$`、`,`、`;`といったRFC 3986で予約文字とされる文字も適切にエスケープされることがテストされています。
これらの変更により、Goの`net/url`パッケージは、RFC 3986の仕様に厳密に準拠し、より堅牢なURLエスケープ処理を提供するようになりました。
## 関連リンク
* GitHubコミットページ: [https://github.com/golang/go/commit/56024fa64ebe87d5b209fa5d126b71ab27f7cf3d](https://github.com/golang/go/commit/56024fa64ebe87d5b209fa5d126b71ab27f7cf3d)
* Go CL (Code Review): [https://golang.org/cl/5970050](https://golang.org/cl/5970050)
* Go Issue #3433: [https://code.google.com/p/go/issues/detail?id=3433](https://code.google.com/p/go/issues/detail?id=3433) (現在はGitHub Issuesに移行している可能性がありますが、当時のリンクです)
## 参考にした情報源リンク
* RFC 3986 - Uniform Resource Identifier (URI): Generic Syntax: [https://datatracker.ietf.org/doc/html/rfc3986](https://datatracker.ietf.org/doc/html/rfc3986)
* RFC 2396 - Uniform Resource Identifiers (URI): Generic Syntax: [https://datatracker.ietf.org/doc/html/rfc2396](https://datatracker.ietf.org/doc/html/rfc2396)
* URLエンコードとデコード - MDN Web Docs: [https://developer.mozilla.org/ja/docs/Glossary/URL_encoding](https://developer.mozilla.org/ja/docs/Glossary/URL_encoding)
* URI scheme - Wikipedia: [https://ja.wikipedia.org/wiki/URI%E3%82%B9%E3%82%AD%E3%83%BC%E3%83%A0](https://ja.wikipedia.org/wiki/URI%E3%82%B9%E3%82%AD%E3%83%BC%E3%83%A0)
* パーセントエンコーディング - Wikipedia: [https://ja.wikipedia.org/wiki/%E3%83%91%E3%83%BC%E3%82%BB%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0](https://ja.wikipedia.org/wiki/%E3%83%91%E3%83%BC%E3%82%BB%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0)