[インデックス 14827] ファイルの概要
このコミットは、Go言語の net/http/cgi
パッケージにおいて、CGI環境変数 REQUEST_URI
が存在しない場合でもHTTPリクエストのURLを正しく構築できるようにするための修正です。具体的には、REQUEST_URI
が提供されない場合に、SCRIPT_NAME
、PATH_INFO
、および QUERY_STRING
といった他のCGI環境変数を組み合わせてURLを再構築するフォールバックロジックが追加されました。これにより、一部のCGI実装やWebサーバー環境で発生する互換性の問題が解決されます。
コミット
548e58781bd5d1201d3095351ec819bc447c0c47
Author: Alex Brainman alex.brainman@gmail.com
Date: Tue Jan 8 17:23:46 2013 +1100
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/548e58781bd5d1201d3095351ec819bc447c0c47
元コミット内容
net/http/cgi: make it work without REQUEST_URI environment variable
Fixes #4367.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/7062052
変更の背景
この変更は、Goの net/http/cgi
パッケージが、CGIアプリケーションとして動作する際に REQUEST_URI
環境変数が設定されていない環境で正しく機能しないという問題(Go issue #4367)を解決するために行われました。
CGI (Common Gateway Interface) は、Webサーバーが外部プログラム(CGIスクリプト)と情報をやり取りするための標準的なプロトコルです。Webサーバーは、HTTPリクエストに関する様々な情報を環境変数としてCGIスクリプトに渡します。REQUEST_URI
は、リクエストされたURI(Uniform Resource Identifier)のパス部分とクエリ文字列を含む変数であり、通常はリクエストされたリソースを特定するために使用されます。
しかし、すべてのWebサーバーやCGI実装が REQUEST_URI
を常に提供するわけではありません。特に古いCGI環境や特定のWebサーバー設定では、この変数が利用できない場合があります。net/http/cgi
パッケージが REQUEST_URI
に強く依存していると、そのような環境ではGoのCGIアプリケーションが正しくリクエストを処理できず、URLの解析に失敗する可能性がありました。
このコミットは、REQUEST_URI
が利用できない場合に備えて、CGI仕様で定義されている他の関連する環境変数(SCRIPT_NAME
、PATH_INFO
、QUERY_STRING
)を組み合わせてフォールバックとしてURLを構築するロジックを追加することで、この互換性の問題を解消することを目的としています。
前提知識の解説
CGI (Common Gateway Interface)
CGIは、Webサーバーが外部の実行可能プログラム(CGIスクリプト)と通信するための標準的なインターフェースです。WebサーバーはクライアントからのHTTPリクエストを受け取ると、そのリクエストを処理するためにCGIスクリプトを起動し、リクエストに関する情報を環境変数や標準入力(POSTリクエストの場合)を通じてスクリプトに渡します。スクリプトは処理結果を標準出力に書き出し、Webサーバーはそれを受け取ってクライアントに返します。
CGI環境変数
CGIスクリプトに渡される主要な環境変数には以下のようなものがあります。
-
REQUEST_URI
:- リクエストされたURIのパス部分とクエリ文字列全体を含みます。
- 例:
/path/to/script/extra/path?query=string
- この変数は便利ですが、すべてのCGI実装で利用可能とは限りません。
-
SCRIPT_NAME
:- 実行されているCGIスクリプトの仮想パス(Webサーバーのドキュメントルートからの相対パス)を示します。
- 例:
/path/to/script
-
PATH_INFO
:SCRIPT_NAME
の後に続くパス情報で、CGIスクリプトが追加のパスコンポーネントとして解釈する部分です。- 例:
/extra/path
(上記のREQUEST_URI
の例の場合)
-
QUERY_STRING
:- URLの
?
の後に続くクエリ文字列全体を含みます。 - 例:
query=string
- URLの
これらの変数を組み合わせることで、REQUEST_URI
が利用できない場合でも、リクエストされた完全なURIを再構築することが可能です。具体的には、SCRIPT_NAME
+ PATH_INFO
+ ?
+ QUERY_STRING
の形式でURIを構築できます。
Goの net/http/cgi
パッケージ
Go言語の標準ライブラリ net/http/cgi
パッケージは、GoプログラムをCGIスクリプトとして動作させるための機能を提供します。このパッケージは、CGI環境変数からHTTPリクエストを構築したり、HTTPレスポンスをCGIの形式で出力したりする役割を担います。特に RequestFromMap
関数は、CGI環境変数を表すマップから *http.Request
オブジェクトを生成する中心的な関数です。
技術的詳細
このコミットの主要な変更は、src/pkg/net/http/cgi/child.go
ファイル内の RequestFromMap
関数にあります。この関数は、CGI環境変数をキーと値のペアで持つ map[string]string
を受け取り、それに基づいて *http.Request
オブジェクトを構築します。
変更前は、http.Request
の URL
フィールドを構築する際に、主に REQUEST_URI
環境変数に依存していました。しかし、REQUEST_URI
が空の場合、URLの構築が不完全になるか、エラーになる可能性がありました。
今回の修正では、以下のフォールバックロジックが導入されました。
- まず、
params["REQUEST_URI"]
の値を取得し、uriStr
に格納します。 uriStr
が空文字列 (""
) であるかどうかをチェックします。- もし
uriStr
が空であれば、REQUEST_URI
が利用できないと判断し、以下のCGI環境変数を組み合わせてuriStr
を再構築します。uriStr = params["SCRIPT_NAME"] + params["PATH_INFO"]
- さらに、
QUERY_STRING
(params["QUERY_STRING"]
) が空でなければ、"?" + s
の形式でuriStr
に追加します。
- この再構築された
uriStr
を使用して、http.Request
のURL
フィールドが解析されます。
このロジックにより、REQUEST_URI
が提供されないCGI環境でも、GoのCGIアプリケーションがリクエストされたURLを正確に特定し、処理を続行できるようになります。
また、src/pkg/net/http/cgi/child_test.go
には、REQUEST_URI
が存在しない場合の新しいテストケース TestRequestWithoutRequestURI
が追加されました。このテストは、フォールバックロジックが期待通りに機能し、SCRIPT_NAME
、PATH_INFO
、QUERY_STRING
から正しいURLが構築されることを検証します。
コアとなるコードの変更箇所
src/pkg/net/http/cgi/child.go
--- a/src/pkg/net/http/cgi/child.go
+++ b/src/pkg/net/http/cgi/child.go
@@ -91,10 +91,19 @@ func RequestFromMap(params map[string]string) (*http.Request, error) {
// TODO: cookies. parsing them isn't exported, though.
+ uriStr := params["REQUEST_URI"]
+ if uriStr == "" {
+ // Fallback to SCRIPT_NAME, PATH_INFO and QUERY_STRING.
+ uriStr = params["SCRIPT_NAME"] + params["PATH_INFO"]
+ s := params["QUERY_STRING"]
+ if s != "" {
+ uriStr += "?" + s
+ }
+ }
if r.Host != "" {
// Hostname is provided, so we can reasonably construct a URL,
// even if we have to assume 'http' for the scheme.
- rawurl := "http://" + r.Host + params["REQUEST_URI"]
+ rawurl := "http://" + r.Host + uriStr
url, err := url.Parse(rawurl)
if err != nil {
return nil, errors.New("cgi: failed to parse host and REQUEST_URI into a URL: " + rawurl)
@@ -104,7 +113,6 @@ func RequestFromMap(params map[string]string) (*http.Request, error) {\n // Fallback logic if we don't have a Host header or the URL\n // failed to parse\n if r.URL == nil {\n- uriStr := params["REQUEST_URI"]
url, err := url.Parse(uriStr)
if err != nil {
return nil, errors.New("cgi: failed to parse REQUEST_URI into a URL: " + uriStr)
src/pkg/net/http/cgi/child_test.go
--- a/src/pkg/net/http/cgi/child_test.go
+++ b/src/pkg/net/http/cgi/child_test.go
@@ -82,6 +82,28 @@ func TestRequestWithoutHost(t *testing.T) {
t.Fatalf("unexpected nil URL")
}
if g, e := req.URL.String(), "/path?a=b"; e != g {
- t.Errorf("expected URL %q; got %q", e, g)
+ t.Errorf("URL = %q; want %q", g, e)
+ }
+}
+
+func TestRequestWithoutRequestURI(t *testing.T) {
+ env := map[string]string{
+ "SERVER_PROTOCOL": "HTTP/1.1",
+ "HTTP_HOST": "example.com",
+ "REQUEST_METHOD": "GET",
+ "SCRIPT_NAME": "/dir/scriptname",
+ "PATH_INFO": "/p1/p2",
+ "QUERY_STRING": "a=1&b=2",
+ "CONTENT_LENGTH": "123",
+ }
+ req, err := RequestFromMap(env)
+ if err != nil {
+ t.Fatalf("RequestFromMap: %v", err)
+ }
+ if req.URL == nil {
+ t.Fatalf("unexpected nil URL")
+ }
+ if g, e := req.URL.String(), "http://example.com/dir/scriptname/p1/p2?a=1&b=2"; e != g {
+ t.Errorf("URL = %q; want %q", g, e)
}
}
コアとなるコードの解説
child.go
の変更点
RequestFromMap
関数内で、まず uriStr := params["REQUEST_URI"]
で REQUEST_URI
の値を取得します。
その直後に以下の新しいブロックが追加されました。
if uriStr == "" {
// Fallback to SCRIPT_NAME, PATH_INFO and QUERY_STRING.
uriStr = params["SCRIPT_NAME"] + params["PATH_INFO"]
s := params["QUERY_STRING"]
if s != "" {
uriStr += "?" + s
}
}
この if
ブロックは、REQUEST_URI
が空の場合に実行されます。
uriStr = params["SCRIPT_NAME"] + params["PATH_INFO"]
:SCRIPT_NAME
とPATH_INFO
を連結してURIのパス部分を構築します。例えば、SCRIPT_NAME
が/dir/scriptname
でPATH_INFO
が/p1/p2
なら、uriStr
は/dir/scriptname/p1/p2
となります。s := params["QUERY_STRING"]
:QUERY_STRING
の値を取得します。if s != "" { uriStr += "?" + s }
:QUERY_STRING
が空でなければ、?
を前置してuriStr
に追加します。これにより、完全なURI(パス + クエリ文字列)がuriStr
に格納されます。
この変更により、r.Host != ""
の条件分岐内での rawurl
の構築や、r.URL == nil
のフォールバックロジック内での url.Parse(uriStr)
の呼び出しにおいて、常に有効な uriStr
が使用されるようになります。以前は params["REQUEST_URI"]
を直接使用していた箇所が、新しい uriStr
変数に置き換えられています。
child_test.go
の変更点
TestRequestWithoutRequestURI
という新しいテスト関数が追加されました。
このテストでは、REQUEST_URI
を含まないCGI環境変数のマップ env
を作成します。
SCRIPT_NAME
、PATH_INFO
、QUERY_STRING
にはそれぞれ値が設定されています。
env := map[string]string{
"SERVER_PROTOCOL": "HTTP/1.1",
"HTTP_HOST": "example.com",
"REQUEST_METHOD": "GET",
"SCRIPT_NAME": "/dir/scriptname",
"PATH_INFO": "/p1/p2",
"QUERY_STRING": "a=1&b=2",
"CONTENT_LENGTH": "123",
}
この env
マップを RequestFromMap
に渡し、返された req.URL.String()
が期待される完全なURL ("http://example.com/dir/scriptname/p1/p2?a=1&b=2"
) と一致するかどうかを検証しています。これにより、REQUEST_URI
がない場合のフォールバックロジックが正しく機能することが保証されます。
関連リンク
- Go Gerrit Change-ID: https://golang.org/cl/7062052
- Go Issue #4367 (関連する可能性のあるIssue): https://go.dev/issue/4367
参考にした情報源リンク
- CGI 1.1 Specification: https://datatracker.ietf.org/doc/html/rfc3875 (RFC 3875 - The Common Gateway Interface (CGI) Version 1.1)
- Go
net/http/cgi
package documentation: https://pkg.go.dev/net/http/cgi