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

[インデックス 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/urlParse関数がURLフラグメント(#以降の部分)を適切に処理するようになり、ParseRequestParseRequestURIにリネームされ、ParseWithFragmentが削除されました。また、httpパッケージのクッキー関連のエラーメッセージのタイポも修正されています。

変更の背景

このコミットは、Go 1のリリースに向けたGo言語の標準ライブラリのAPI安定化と改善という大きな流れの中で行われました。

  1. net/urlパッケージのAPI改善:

    • 従来のurl.ParseはURLフラグメント(#以降の部分)を無視していました。しかし、クライアントサイドのJavaScriptなどでフラグメントが重要な意味を持つ場合があり、これをGoのURLパーサーで適切に扱えるようにする必要がありました。
    • ParseRequestParseRequestURIの区別は、HTTPリクエストのURIが常に絶対URIまたは絶対パスとして解釈されるべきであるというHTTPの仕様に合わせたものです。これにより、URLの解釈における曖昧さを排除し、より堅牢な処理を可能にしました。
    • ParseWithFragmentの削除は、Parse関数自体がフラグメントを処理するようになったため、冗長になったためです。APIをシンプルに保つための変更と言えます。
  2. cmd/fixツールの汎用化:

    • cmd/fixは、Go言語のバージョンアップに伴うAPI変更や非互換性を自動的に修正するためのツールです。元々cryptotypeという特定の型のリネームに対応していましたが、Go 1のリリースでは他にも多くのAPI変更が予定されていたため、より汎用的なgo1renameという名前に変更され、様々なリネームに対応できるように拡張されました。これにより、開発者がGo 1への移行をスムーズに行えるように支援する目的がありました。
  3. os.Execからsyscall.Execへの移行:

    • os.Execは、Go 1のリリースに向けてsyscall.Execに統合されることになりました。これは、Goの標準ライブラリにおけるシステムコール関連の機能の整理と一貫性の向上を目的としています。syscallパッケージは低レベルなシステムコールへのアクセスを提供し、osパッケージはより高レベルなOS操作を提供するという役割分担が明確化されました。
  4. Issue #2946の修正:

    • このコミットは、GoのIssue #2946を修正しています。このIssueは、net/urlパッケージのParse関数がURLフラグメントを正しく扱わないという問題に関連していると考えられます。

前提知識の解説

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 へのリネーム:
    • ファイル名が変更され、cryptotypeFixgo1renameFixに、cryptotypeReplacego1renameReplaceにそれぞれリネームされました。
    • これにより、特定の暗号型のリネームだけでなく、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.Execsyscall.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言語の公式ドキュメント
  • Go言語のソースコード
  • Go言語のIssueトラッカー
  • Go言語のコードレビューシステム
  • RFC 2616 (HTTP/1.1)
  • URLの構造に関する一般的な情報源 (例: Wikipedia)
  • Go言語のosおよびsyscallパッケージに関するドキュメント