[インデックス 14086] ファイルの概要
このコミットは、Go言語の標準ライブラリ net/url
パッケージにおける ParseQuery
関数が、クエリ文字列のパース中に発生した最初のエラーを正しく報告するように修正するものです。これにより、複数の不正なエスケープシーケンスが存在する場合でも、最初に検出されたエラーが返されるようになり、デバッグやエラーハンドリングが容易になります。
コミット
commit c7cc894ef5978746dce145227808287fee627dc0
Author: David Symonds <dsymonds@golang.org>
Date: Tue Oct 9 08:10:32 2012 +1100
net/url: report first error from ParseQuery.
Fixes #4175.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6610068
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c7cc894ef5978746dce145227808287fee627dc0
元コミット内容
net/url: report first error from ParseQuery.
このコミットは、ParseQuery
関数がクエリ文字列のパース中に発生した最初のエラーを報告するように修正します。
Fixes #4175.
これはIssue #4175を修正します。
変更の背景
Go言語の net/url
パッケージは、URLのパースやクエリパラメータの処理を行うための機能を提供します。ParseQuery
関数は、URLのクエリ文字列をキーと値のペアに分解し、url.Values
型のマップとして返します。
このコミットが導入される前は、ParseQuery
関数内部で QueryUnescape
を呼び出す際に複数のエスケープエラーが発生した場合、最後に発生したエラーが上書きされてしまい、最初のエラーが失われる可能性がありました。これは、クエリ文字列に複数の不正なエスケープシーケンスが含まれている場合に問題となります。例えば、%gh&%ij
のようなクエリ文字列では、%gh
と %ij
の両方が不正なエスケープシーケンスですが、以前の実装では %ij
のエラーのみが報告され、%gh
のエラーは無視されていました。
開発者やユーザーがクエリ文字列のパースエラーをデバッグする際、最初に発生したエラーを知ることは問題の根本原因を特定するために非常に重要です。そのため、ParseQuery
が最初のエラーを正確に報告するように修正する必要がありました。この修正は、GoのIssue #4175として報告され、その解決策としてこのコミットが作成されました。
前提知識の解説
- URL (Uniform Resource Locator): インターネット上のリソースを一意に識別するための文字列。スキーム、ホスト、パス、クエリ、フラグメントなどのコンポーネントから構成されます。
- クエリ文字列 (Query String): URLの一部で、リソースに渡される追加のパラメータを含む部分。通常、
?
の後にキー=値
の形式で記述され、複数のパラメータは&
で区切られます。例:?name=Alice&age=30
- URLエンコーディング (URL Encoding): URLに使用できない文字(スペース、日本語など)を、
%
の後に16進数で表現する形式に変換すること。例えば、スペースは%20
に、日本語の「あ」は%E3%81%82
のようにエンコードされます。 - URLデコーディング (URL Decoding): URLエンコードされた文字列を元の文字に戻すこと。
net/url
パッケージ (Go言語): Go言語の標準ライブラリの一部で、URLのパース、構築、クエリパラメータの操作など、URLに関連する機能を提供します。url.Values
型 (Go言語):map[string][]string
のエイリアスで、URLのクエリパラメータやフォームデータを表現するために使用されます。同じキーに対して複数の値を持つことができるため、[]string
のスライスとして値を保持します。QueryUnescape
関数 (Go言語):net/url
パッケージ内の関数で、URLエンコードされた文字列をデコードします。不正なエスケープシーケンス(例:%gh
)が含まれている場合はエラーを返します。- エラーハンドリング (Error Handling): プログラム実行中に発生する可能性のあるエラーを検出し、適切に処理するメカニズム。Go言語では、関数がエラーを返す場合、通常は戻り値の最後の要素として
error
型の値を返します。
技術的詳細
このコミットの核心は、src/pkg/net/url/url.go
ファイル内の parseQuery
関数におけるエラーハンドリングロジックの変更です。
parseQuery
関数は、与えられたクエリ文字列を &
で分割し、それぞれの キー=値
のペアを処理します。各キーと値は QueryUnescape
関数によってデコードされます。
変更前のコードでは、QueryUnescape
がエラーを返した場合、そのエラーが err
変数に直接代入されていました。
// 変更前
key, err1 := QueryUnescape(key)
if err1 != nil {
err = err1 // ここでエラーが上書きされる可能性があった
continue
}
value, err1 = QueryUnescape(value)
if err1 != nil {
err = err1 // ここでエラーが上書きされる可能性があった
continue
}
このロジックの問題点は、ループ内で複数の QueryUnescape
呼び出しがあり、それぞれがエラーを返す可能性があることです。もし最初の QueryUnescape
がエラーを返し、その後に続く QueryUnescape
もエラーを返した場合、err
変数は後者のエラーで上書きされてしまい、最初の重要なエラー情報が失われていました。
このコミットでは、この問題を解決するために、err
変数がまだ nil
である場合にのみ、QueryUnescape
から返されたエラーを代入するように変更されました。
// 変更後
key, err1 := QueryUnescape(key)
if err1 != nil {
if err == nil { // errがまだnilの場合のみ代入
err = err1
}
continue
}
value, err1 = QueryUnescape(value)
if err1 != nil {
if err == nil { // errがまだnilの場合のみ代入
err = err1
}
continue
}
この変更により、parseQuery
関数は、クエリ文字列全体をパースする過程で最初に遭遇したエラーを保持し、最終的にそのエラーを呼び出し元に返すことが保証されます。これにより、エラーの根本原因を特定しやすくなり、デバッグの効率が向上します。
テストケース TestParseFailure
が追加され、この新しい動作が検証されています。具体的には、%gh&%ij
という不正なクエリ文字列を ParseQuery
に渡し、返されたエラー文字列が最初の不正なシーケンスである %gh
を含んでいることを確認しています。
コアとなるコードの変更箇所
src/pkg/net/url/url.go
ファイルの parseQuery
関数内の変更です。
--- a/src/pkg/net/url/url.go
+++ b/src/pkg/net/url/url.go
@@ -521,12 +521,16 @@ func parseQuery(m Values, query string) (err error) {
}
key, err1 := QueryUnescape(key)
if err1 != nil {
- err = err1
+ if err == nil {
+ err = err1
+ }
continue
}
value, err1 = QueryUnescape(value)
if err1 != nil {
- err = err1
+ if err == nil {
+ err = err1
+ }
continue
}
m[key] = append(m[key], value)
src/pkg/net/url/url_test.go
ファイルに新しいテストケース TestParseFailure
が追加されました。
--- a/src/pkg/net/url/url_test.go
+++ b/src/pkg/net/url/url_test.go
@@ -7,6 +7,7 @@ package url
import (
"fmt"
"reflect"
+ "strings"
"testing"
)
@@ -779,3 +780,13 @@ func TestRequestURI(t *testing.T) {
}
}
}
+
+func TestParseFailure(t *testing.T) {
+ // Test that the first parse error is returned.
+ const url = "%gh&%ij"
+ _, err := ParseQuery(url)
+ errStr := fmt.Sprint(err)
+ if !strings.Contains(errStr, "%gh") {
+ t.Errorf(`ParseQuery(%q) returned error %q, want something containing %q"`, url, errStr, "%gh")
+ }
+}
コアとなるコードの解説
src/pkg/net/url/url.go
の変更点:
parseQuery
関数内で、QueryUnescape
からエラー err1
が返された際に、既存のエラー変数 err
が nil
である場合にのみ、err1
を err
に代入するように条件が追加されました。
if err1 != nil {
if err == nil { // ここが追加された条件
err = err1
}
continue
}
この if err == nil
という条件が、複数のエラーが発生した場合に最初のエラーを保持し、それ以降のエラーで上書きされないようにする役割を果たします。
src/pkg/net/url/url_test.go
の変更点:
TestParseFailure
という新しいテスト関数が追加されました。
このテストでは、%gh&%ij
という、複数の不正なエスケープシーケンスを含むURLクエリ文字列を定義しています。
ParseQuery
関数を呼び出し、返されたエラーを err
変数に格納します。
fmt.Sprint(err)
を使用してエラーを文字列に変換し、errStr
に格納します。
strings.Contains(errStr, "%gh")
を使って、エラー文字列が最初の不正なシーケンスである %gh
を含んでいるかどうかを検証します。
もし %gh
が含まれていなければ、テストは失敗し、期待されるエラーメッセージが出力されます。これにより、ParseQuery
が最初のエラーを正しく報告していることが保証されます。
関連リンク
- Go言語の
net/url
パッケージのドキュメント: https://pkg.go.dev/net/url - Go言語のIssueトラッカー: https://github.com/golang/go/issues
参考にした情報源リンク
- Go言語のIssue #4175 (FrozenDueToAge): https://goissues.org/issue/4175
- Go言語のコードレビューツール (Gerrit) の変更セット: https://golang.org/cl/6610068 (これはコミットメッセージに記載されているリンクですが、現在はGitHubに移行しているため、直接アクセスしてもリダイレクトされるか、古い情報が表示される可能性があります。)