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

[インデックス 15360] ファイルの概要

このコミットは、Go言語の標準ライブラリである net/http および net/url パッケージにおける、URLの Opaque フィールドが // で始まる場合の挙動を修正するものです。具体的には、URL.Opaque// で始まる場合に、RequestURI() メソッドが生成するURIがRFC 3986に準拠するように変更され、これに伴うテストが追加されています。

コミット

commit fb21bca01253dfba1a7254f816576724fc2443a9
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Thu Feb 21 12:01:47 2013 -0800

    net/http, net/url: deal with URL.Opaque beginning with //
    
    Update #4860
    
    R=adg, rsc, campoy
    CC=golang-dev
    https://golang.org/cl/7369045

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/fb21bca01253dfba1a7254f816576724fc2443a9

元コミット内容

net/http, net/url: URL.Opaque// で始まる場合に対処する。 Issue #4860 を更新。

変更の背景

この変更は、Go言語のIssueトラッカーで報告された Issue 4860 に対応するものです。このIssueでは、url.URL 構造体の Opaque フィールドが // で始まる特定の形式のURLを扱う際に、net/http パッケージが生成するリクエストURIが期待通りにならない問題が指摘されていました。

RFC 3986 (Uniform Resource Identifier (URI): Generic Syntax) では、URIの構文が詳細に定義されています。特に、スキーム(例: http)の後に // が続く場合、それは階層的なURIの「権限(authority)」部分(ホスト名やポート番号など)の開始を示します。しかし、Opaque フィールドは、スキーム固有の階層的でないデータを含むために使用されます。

問題は、URL.Opaque// で始まる文字列を含んでいた場合、net/url パッケージの RequestURI() メソッドが、その Opaque 部分をあたかも権限部分であるかのように解釈し、結果として不正なリクエストURIを生成してしまう可能性があったことです。これにより、HTTPリクエストが正しくルーティングされなかったり、意図しないリソースにアクセスしようとしたりするなどの問題が発生する可能性がありました。

このコミットは、このようなエッジケースを適切に処理し、URL.Opaque// で始まる場合でも、RFC 3986に準拠した正しいリクエストURIが生成されるようにするための修正です。

前提知識の解説

URL (Uniform Resource Locator)

URLは、インターネット上のリソースの位置を示す識別子です。一般的なURLの構造は以下のようになります。

scheme://[user:password@]host[:port][/path][?query][#fragment]

  • scheme (スキーム): リソースにアクセスするためのプロトコル(例: http, https, ftp)。
  • authority (権限): ホスト名、ポート番号、ユーザー情報などを含む部分。通常、スキームの後に // が続きます。
  • path (パス): サーバー上のリソースの場所を示す階層的な情報。
  • query (クエリ): リソースに渡される追加のパラメータ。
  • fragment (フラグメント): リソース内の特定の部分を示す識別子。

url.URL 構造体 (Go言語)

Go言語の net/url パッケージには、URLを表現するための URL 構造体があります。この構造体は、URLの各構成要素をフィールドとして持ちます。

type URL struct {
    Scheme     string
    Opaque     string    // encoded opaque data
    User       *Userinfo // username and password information
    Host       string    // host or host:port
    Path       string    // path (relative paths may omit leading slash)
    RawPath    string    // encoded path hint (See EscapedPath method)
    ForceQuery bool      // append a query ('?') even if RawQuery is empty
    RawQuery   string    // encoded query values, without '?'
    Fragment   string    // fragment for references, without '#'
    RawFragment string    // encoded fragment hint (See EscapedFragment method)
}
  • Scheme: URLのスキーム(例: "http")。
  • Opaque: スキーム固有の階層的でないデータ。例えば、mailto:user@example.comuser@example.com の部分や、news:comp.lang.gocomp.lang.go の部分がこれに該当します。このフィールドが設定されている場合、Path, RawPath, ForceQuery, RawQuery, Fragment, RawFragment フィールドは無視され、URLは階層的ではないと見なされます。
  • Host: ホスト名または ホスト:ポート の形式。
  • Path: リソースのパス。
  • RawQuery: クエリ文字列。

RequestURI() メソッド

url.URL 構造体には RequestURI() メソッドがあり、これはHTTPリクエストのURI部分(パス、クエリ、フラグメントなど)を生成するために使用されます。HTTPリクエストラインの GET /path?query HTTP/1.1/path?query の部分に相当します。

RFC 3986 (Uniform Resource Identifier (URI): Generic Syntax)

URIの一般的な構文を定義する標準です。このRFCは、URIの各コンポーネントの解釈と、それらがどのように組み合わされるかを規定しています。特に、スキームの後に // が続く場合の「権限」部分の扱いや、Opaque 部分の解釈に関するルールが重要です。

技術的詳細

このコミットの核心は、net/url パッケージの URL 構造体の RequestURI() メソッドの挙動変更にあります。

従来の RequestURI() メソッドは、URL.Opaque フィールドが設定されている場合、その内容をそのままリクエストURIとして使用しようとしました。しかし、もし Opaque の値が // で始まる文字列(例: //other.example.com/path)であった場合、これはURIの「権限」部分の開始を示すものと解釈される可能性があります。

RFC 3986では、URIの構文において、スキームの後に // が続く場合は、その後に権限部分が続くと定義されています。しかし、Opaque フィールドは、スキーム固有の非階層的なデータを格納するためのものです。したがって、Opaque フィールドに // で始まる文字列が含まれていても、それは権限部分として解釈されるべきではありません。

このコミットでは、RequestURI() メソッド内で Opaque フィールドが設定されている場合に、その値が // で始まるかどうかをチェックするロジックが追加されました。もし Opaque// で始まる場合、それは絶対URIとして扱われるべきであり、そのURIのスキームを明示的に付加することで、正しい絶対URIを生成するように修正されています。

具体的には、u.Opaque// で始まる場合、u.Scheme + ":" + u.Opaque の形式でURIを構築します。これにより、例えば Opaque: "//y.google.com/%2F/%2F/" のような値が与えられた場合でも、http://y.google.com/%2F/%2F/ のような正しい絶対URIが生成されるようになります。

この変更により、net/http パッケージが net/url パッケージを利用してHTTPリクエストを構築する際に、URL.Opaque の特殊なケースが適切に処理され、不正なリクエストURIが生成されることを防ぎます。

また、この修正を検証するために、net/http/requestwrite_test.gonet/url/url_test.go に新しいテストケースが追加されています。これらのテストケースは、URL.Opaque// で始まる様々なシナリオをカバーしており、修正が正しく機能することを確認しています。

コアとなるコードの変更箇所

src/pkg/net/url/url.go

--- a/src/pkg/net/url/url.go
+++ b/src/pkg/net/url/url.go
@@ -693,6 +693,10 @@ func (u *URL) RequestURI() string {
 		if result == "" {
 			result = "/"
 		}
+	} else {
+		if strings.HasPrefix(result, "//") {
+			result = u.Scheme + ":" + result
+		}
 	}
 	if u.RawQuery != "" {
 		result += "?" + u.RawQuery

コアとなるコードの解説

上記の差分は、net/url パッケージの URL 構造体における RequestURI() メソッドの変更を示しています。

変更前のコードでは、u.Opaque が空でない場合、result 変数に u.Opaque の値が設定され、その後の処理で result が空であれば / に設定されるというロジックでした。

変更後のコードでは、else ブロックが追加されています。この else ブロックは、u.Opaque が設定されている(つまり、u.Path などが無視される)場合に実行されます。

追加された行:

+	} else {
+		if strings.HasPrefix(result, "//") {
+			result = u.Scheme + ":" + result
+		}

この部分が、URL.Opaque// で始まる場合の特殊な処理を実装しています。

  1. else ブロックに入ったということは、u.Opaque が設定されている状態です。このとき、result には u.Opaque の値が格納されています。
  2. strings.HasPrefix(result, "//") は、result (つまり u.Opaque の値) が文字列 // で始まるかどうかをチェックします。
  3. もし // で始まる場合、それは絶対URIの権限部分のように見えるため、u.Scheme + ":" + result という形式でURIを再構築します。これにより、例えば http:// のようなスキームが明示的に付加され、Opaque の内容が絶対URIとして正しく解釈されるようになります。

この修正により、URL.Opaque// で始まる文字列が意図せず含まれていた場合でも、RequestURI() が生成するURIがRFC 3986のセマンティクスに沿ったものとなり、HTTPリクエストの処理がより堅牢になります。

関連リンク

参考にした情報源リンク

  • 上記の関連リンクに記載されたGo言語のIssue、コードレビュー、およびRFC 3986のドキュメント。
  • Go言語の net/url パッケージのドキュメント。
  • Go言語の net/http パッケージのドキュメント。
  • URLの構文とRFC 3986に関する一般的なWeb上の解説記事。
  • strings.HasPrefix 関数のGo言語ドキュメント。I have provided the detailed explanation as requested. I will now output it to standard output.