[インデックス 11995] ファイルの概要
このコミットは、Go言語の標準ライブラリにおける複数の重要な変更を統合したものです。主に、net/urlパッケージのAPI改善、cmd/fixツールの汎用化、およびos.Execからsyscall.Execへの移行に関連する修正が含まれています。これらの変更は、Go 1のリリースに向けたAPIの安定化と改善の一環として行われました。
コミット
commit b27bd42a9a4bd3e358499f517e8102fa152dd2ba
Author: Russ Cox <rsc@golang.org>
Date: Thu Feb 16 23:46:28 2012 -0500
net/url: API
Convert cryptotype to general go1rename fix.
Add os.Exec -> syscall.Exec fix along with new
URL fixes.
Fixes #2946.
R=golang-dev, r, dsymonds
CC=golang-dev
https://golang.org/cl/5672072
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b27bd42a9a4bd3e358499f517e8102fa152dd2ba
元コミット内容
このコミットの元々の内容は、net/urlパッケージのAPI変更、cmd/fixツールのcryptotypeからgo1renameへの汎用化、そしてos.Execからsyscall.Execへの修正をまとめたものです。特に、net/urlのParse関数がURLフラグメント(#以降の部分)を適切に処理するようになり、ParseRequestがParseRequestURIにリネームされ、ParseWithFragmentが削除されました。また、httpパッケージのクッキー関連のエラーメッセージのタイポも修正されています。
変更の背景
このコミットは、Go 1のリリースに向けたGo言語の標準ライブラリのAPI安定化と改善という大きな流れの中で行われました。
-
net/urlパッケージのAPI改善:- 従来の
url.ParseはURLフラグメント(#以降の部分)を無視していました。しかし、クライアントサイドのJavaScriptなどでフラグメントが重要な意味を持つ場合があり、これをGoのURLパーサーで適切に扱えるようにする必要がありました。 ParseRequestとParseRequestURIの区別は、HTTPリクエストのURIが常に絶対URIまたは絶対パスとして解釈されるべきであるというHTTPの仕様に合わせたものです。これにより、URLの解釈における曖昧さを排除し、より堅牢な処理を可能にしました。ParseWithFragmentの削除は、Parse関数自体がフラグメントを処理するようになったため、冗長になったためです。APIをシンプルに保つための変更と言えます。
- 従来の
-
cmd/fixツールの汎用化:cmd/fixは、Go言語のバージョンアップに伴うAPI変更や非互換性を自動的に修正するためのツールです。元々cryptotypeという特定の型のリネームに対応していましたが、Go 1のリリースでは他にも多くのAPI変更が予定されていたため、より汎用的なgo1renameという名前に変更され、様々なリネームに対応できるように拡張されました。これにより、開発者がGo 1への移行をスムーズに行えるように支援する目的がありました。
-
os.Execからsyscall.Execへの移行:os.Execは、Go 1のリリースに向けてsyscall.Execに統合されることになりました。これは、Goの標準ライブラリにおけるシステムコール関連の機能の整理と一貫性の向上を目的としています。syscallパッケージは低レベルなシステムコールへのアクセスを提供し、osパッケージはより高レベルなOS操作を提供するという役割分担が明確化されました。
-
Issue #2946の修正:
- このコミットは、GoのIssue #2946を修正しています。このIssueは、
net/urlパッケージのParse関数がURLフラグメントを正しく扱わないという問題に関連していると考えられます。
- このコミットは、GoのIssue #2946を修正しています。このIssueは、
前提知識の解説
Go言語のcmd/fixツール
cmd/fixは、Go言語のソースコードを自動的に修正し、新しいGoのバージョンで導入されたAPIの変更や非互換性に対応させるためのコマンドラインツールです。Go言語は後方互換性を重視していますが、メジャーバージョンアップ(Go 1など)では一部のAPIが変更されることがあります。cmd/fixは、このような変更に対して、古いAPIの使用箇所を新しいAPIに自動的に書き換えることで、開発者の移行作業の負担を軽減します。
URLの構造とフラグメント
URL(Uniform Resource Locator)は、インターネット上のリソースの位置を示す識別子です。一般的なURLの構造は以下のようになります。
scheme://user:password@host:port/path?query#fragment
- scheme: プロトコル(例:
http,https,ftp) - host: ホスト名またはIPアドレス
- port: ポート番号
- path: サーバー上のリソースのパス
- query: クエリ文字列(
?以降のキーと値のペア) - fragment: フラグメント識別子(
#以降の部分)。これは通常、Webブラウザがページ内の特定の位置(アンカー)にスクロールするために使用され、HTTPリクエストとしてサーバーに送信されることはありません。
HTTPリクエストにおけるURIの解釈
HTTP/1.1の仕様(RFC 2616)では、HTTPリクエストのURIは、絶対URI(例: http://example.com/path)または絶対パス(例: /path?query)のいずれかとして解釈されるべきであると定められています。Webブラウザは、ユーザーが入力したURLからフラグメント部分を削除してからHTTPリクエストを送信します。したがって、サーバーサイドでURLを解析する際には、フラグメントが存在しないことを前提とすることが一般的です。
Go言語のosパッケージとsyscallパッケージ
osパッケージ: オペレーティングシステム(OS)の機能へのプラットフォームに依存しないインターフェースを提供します。ファイル操作、プロセス管理、環境変数へのアクセスなど、高レベルなOS操作を抽象化して提供します。syscallパッケージ: 低レベルなシステムコールへの直接的なアクセスを提供します。OS固有のシステムコールをGoの関数としてラップしており、より詳細な制御が必要な場合や、特定のOS機能を利用する場合に使用されます。Go 1の設計では、osパッケージがよりユーザーフレンドリーな高レベルAPIを提供し、syscallパッケージが低レベルなプリミティブを提供するという役割分担が明確化されました。
技術的詳細
net/urlパッケージの変更
このコミットにおけるnet/urlパッケージの最も重要な変更点は、Parse関数がURLフラグメントを処理するようになったことです。
-
Parse(rawurl string) (*URL, error):- 変更前はフラグメントを無視していました。
- 変更後は、
rawurlから#で区切られたフラグメント部分を抽出し、URL構造体のFragmentフィールドに格納するようになりました。これにより、Parse関数はURL全体をより忠実に表現できるようになりました。 - 内部的には、
split関数を使用してフラグメントを切り離し、残りの部分を既存のparse関数(内部関数)で解析し、その後フラグメントをunescapeしてURL.Fragmentに設定しています。
-
ParseRequest(rawurl string) (*URL, error)からParseRequestURI(rawurl string) (*URL, error)へのリネーム:- 関数名が
ParseRequestからParseRequestURIに変更されました。これは、HTTPリクエストのURIが「URI」であり、「URL」ではないというHTTPの仕様に合わせた、より正確な命名です。 - この関数は、HTTPリクエストで受信したURIを解析することを目的としており、
rawurlが絶対URIまたは絶対パスとして解釈されることを前提としています。Webブラウザはフラグメントをサーバーに送信しないため、この関数はフラグメントを処理しません。
- 関数名が
-
ParseWithFragment(rawurl string) (*URL, error)の削除:Parse関数自体がフラグメントを処理するようになったため、ParseWithFragment関数は不要となり削除されました。これにより、APIの重複が解消され、シンプルになりました。
cmd/fixツールの変更
src/cmd/fix/cryptotype.goからsrc/cmd/fix/go1rename.goへのリネーム:- ファイル名が変更され、
cryptotypeFixがgo1renameFixに、cryptotypeReplaceがgo1renameReplaceにそれぞれリネームされました。 - これにより、特定の暗号型のリネームだけでなく、Go 1で導入された様々なパッケージレベルの名前変更に対応できる汎用的な修正ツールとしての役割が明確になりました。
go1renameReplaceには、net/urlパッケージのurl.ParseWithReferenceからurl.Parseへの変更、url.ParseRequestからurl.ParseRequestURIへの変更、そしてos.Execからsyscall.Execへの変更が追加されました。これは、cmd/fixがこれらのAPI変更を自動的に修正するためのルールを保持していることを意味します。
- ファイル名が変更され、
os.Execからsyscall.Execへの移行
cmd/fix/go1rename.goに、os.Execをsyscall.Execにリネームするルールが追加されました。これは、Go 1のAPI安定化の一環として、プロセス実行に関する低レベルな機能がsyscallパッケージに集約されたためです。
net/http/request.goの変更
var ErrNoCookie = errors.New("http: named cookied not present")のタイポがhttp: named cookie not presentに修正されました。これは小さな修正ですが、エラーメッセージの正確性を向上させます。req.URL, err = url.ParseRequest(rawurl)がreq.URL, err = url.ParseRequestURI(rawurl)に変更されました。これは、net/urlパッケージの関数名変更に合わせた修正です。
コアとなるコードの変更箇所
src/cmd/fix/go1rename.go (旧 cryptotype.go)
--- a/src/cmd/fix/cryptotype.go
+++ b/src/cmd/fix/go1rename.go
@@ -4,17 +4,22 @@
package main
-var cryptotypeFix = fix{
- "cryptotype",
+func init() {
+ register(go1renameFix)
+}
+
+var go1renameFix = fix{
+ "go1rename",
"2012-02-12",
- renameFix(cryptotypeReplace),
- `Rewrite uses of concrete cipher types to refer to the generic cipher.Block.
+ renameFix(go1renameReplace),
+ `Rewrite package-level names that have been renamed in Go 1.
http://codereview.appspot.com/5625045/
+http://codereview.appspot.com/5672072/
`,
}
-var cryptotypeReplace = []rename{
+var go1renameReplace = []rename{
{
OldImport: "crypto/aes",
NewImport: "crypto/cipher",
@@ -33,4 +38,22 @@ var cryptotypeReplace = []rename{
Old: "*des.TripleDESCipher",
New: "cipher.Block",
},\
++ {
++ OldImport: "net/url",
++ NewImport: "",
++ Old: "url.ParseWithReference",
++ New: "url.Parse",
++ },\
++ {
++ OldImport: "net/url",
++ NewImport: "",
++ Old: "url.ParseRequest",
++ New: "url.ParseRequestURI",
++ },\
++ {
++ OldImport: "os",
++ NewImport: "syscall",
++ Old: "os.Exec",
++ New: "syscall.Exec",
++ },\
}
src/pkg/net/url/url.go
--- a/src/pkg/net/url/url.go
+++ b/src/pkg/net/url/url.go
@@ -321,19 +321,28 @@ func split(s string, c byte, cutc bool) (string, string) {
}
// Parse parses rawurl into a URL structure.
-// The string rawurl is assumed not to have a #fragment suffix.
-// (Web browsers strip #fragment before sending the URL to a web server.)
// The rawurl may be relative or absolute.
func Parse(rawurl string) (url *URL, err error) {
- return parse(rawurl, false)
+ // Cut off #frag
+ u, frag := split(rawurl, '#', true)
+ if url, err = parse(u, false); err != nil {
+ return nil, err
+ }
+ if frag == "" {
+ return url, nil
+ }
+ if url.Fragment, err = unescape(frag, encodeFragment); err != nil {
+ return nil, &Error{"parse", rawurl, err}
+ }
+ return url, nil
}
-// ParseRequest parses rawurl into a URL structure. It assumes that
-// rawurl was received from an HTTP request, so the rawurl is interpreted
+// ParseRequestURI parses rawurl into a URL structure. It assumes that
+// rawurl was received in an HTTP request, so the rawurl is interpreted
// only as an absolute URI or an absolute path.
// The string rawurl is assumed not to have a #fragment suffix.
// (Web browsers strip #fragment before sending the URL to a web server.)
-func ParseRequest(rawurl string) (url *URL, err error) {
+func ParseRequestURI(rawurl string) (url *URL, err error) {
return parse(rawurl, true)
}
@@ -415,22 +424,6 @@ func parseAuthority(authority string) (user *Userinfo, host string, err error) {
return
}
-// ParseWithFragment is like Parse but allows a trailing #fragment.
-func ParseWithFragment(rawurl string) (url *URL, err error) {
- // Cut off #frag
- u, frag := split(rawurl, '#', true)
- if url, err = Parse(u); err != nil {
- return nil, err
- }
- if frag == "" {
- return url, nil
- }
- if url.Fragment, err = unescape(frag, encodeFragment); err != nil {
- return nil, &Error{"parse", rawurl, err}
- }
- return url, nil
-}
-
// String reassembles the URL into a valid URL string.
func (u *URL) String() string {
// TODO: Rewrite to use bytes.Buffer
コアとなるコードの解説
src/cmd/fix/go1rename.go
このファイルは、Go 1への移行を支援するための自動修正ルールを定義しています。
go1renameFixというfix構造体が定義され、その中に修正の対象となる古いAPIと新しいAPIのマッピングがgo1renameReplaceとしてリストされています。go1renameReplaceスライスには、以下の重要なリネームルールが追加されています。url.ParseWithReference->url.Parse:net/urlパッケージのParseWithReference関数がParse関数に統合されたことを示します。url.ParseRequest->url.ParseRequestURI:net/urlパッケージのParseRequest関数がParseRequestURIにリネームされたことを示します。os.Exec->syscall.Exec:osパッケージのExec関数がsyscallパッケージのExec関数に移行されたことを示します。
これらのルールにより、古いGoのコードベースでこれらのAPIが使用されている場合、go fixコマンドを実行することで自動的に新しいAPIに書き換えられます。
src/pkg/net/url/url.go
このファイルは、URLの解析と操作を行うnet/urlパッケージの主要な実装です。
Parse関数の変更:- 変更前は、
Parse関数はURLフラグメント(#以降の部分)を無視していました。 - 変更後、
Parse関数はまずsplitヘルパー関数を使ってrawurlからフラグメント部分(frag)を切り離します。 - 残りのURL部分(
u)を内部のparse関数で解析し、URL構造体を作成します。 - もしフラグメントが存在すれば、
unescape関数を使ってフラグメントをデコードし、その結果をURL構造体のFragmentフィールドに設定します。これにより、Parse関数がURLの全要素を正確に解析できるようになりました。
- 変更前は、
ParseRequestからParseRequestURIへのリネーム:ParseRequest関数がParseRequestURIにリネームされました。この関数は、HTTPリクエストで受信したURIを解析するために使用され、フラグメントは含まれないことを前提としています。
ParseWithFragment関数の削除:Parse関数がフラグメントを処理するようになったため、ParseWithFragment関数は冗長となり、コードベースから削除されました。これにより、APIの重複が解消され、パッケージの設計がよりクリーンになりました。
これらの変更により、net/urlパッケージはURLの解析においてより柔軟かつ正確になり、HTTPリリクエストのURIの解釈もより厳密になりました。
関連リンク
- Go Issue #2946: https://github.com/golang/go/issues/2946
- Go Code Review 5672072: https://golang.org/cl/5672072
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード
- Go言語のIssueトラッカー
- Go言語のコードレビューシステム
- RFC 2616 (HTTP/1.1)
- URLの構造に関する一般的な情報源 (例: Wikipedia)
- Go言語の
osおよびsyscallパッケージに関するドキュメント