[インデックス 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/cgipackage documentation: https://pkg.go.dev/net/http/cgi