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

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

このコミットは、Go言語の標準ライブラリnet/http/cgiパッケージにおけるWindows環境でのテストの安定性向上を目的としています。具体的には、Windows上でのCGIスクリプトの実行に関するパスの正規化と、テストのスキップ条件の調整が行われています。

コミット

commit 54b9c2015194b68135098c4eae564a122a4bccf6
Author: Alex Brainman <alex.brainman@gmail.com>
Date:   Tue Nov 20 16:24:12 2012 +1100

    net/http/cgi: another attempt to fix windows tests
    
    Also enables TestDirWindows test on windows.
    
    Fixes #4401.
    
    R=golang-dev, bradfitz
    CC=golang-dev, krautz
    https://golang.org/cl/6847072

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

https://github.com/golang/go/commit/54b9c2015194b68135098c4eae564a122a4bccf6

元コミット内容

net/http/cgi: another attempt to fix windows tests Also enables TestDirWindows test on windows. Fixes #4401.

変更の背景

このコミットは、Go言語のnet/http/cgiパッケージにおけるWindows環境でのテストの不安定性を解決するために行われました。特に、Issue #4401で報告されていた問題に対処しています。Windows環境では、ファイルパスの表現や改行コードの扱いがUnix系システムと異なるため、CGIスクリプトが期待通りに動作しない、あるいはテストが失敗するといった問題が発生しがちです。このコミットは、これらのWindows特有の問題を吸収し、テストの信頼性を向上させることを目的としています。また、これまでWindows環境でスキップされていたTestDirWindowsテストを有効にすることで、Windows環境でのCGI機能の動作保証を強化しています。

前提知識の解説

CGI (Common Gateway Interface)

CGIは、Webサーバーが外部プログラム(CGIスクリプト)を実行し、その出力をWebブラウザに返すための標準的なインターフェースです。Webサーバーは、HTTPリクエストの情報を環境変数としてCGIスクリプトに渡し、スクリプトは標準出力にHTTPヘッダとコンテンツを出力します。これにより、動的なWebページを生成したり、データベースと連携したりすることが可能になります。

net/http/cgiパッケージ

Go言語のnet/http/cgiパッケージは、GoプログラムをCGIスクリプトとして実行するための機能を提供します。これにより、既存のWebサーバー(Apache, Nginxなど)のCGI機能を利用してGoアプリケーションをデプロイすることができます。このパッケージは、CGI環境変数の処理、標準入力/出力のハンドリング、およびCGIスクリプトの実行を抽象化します。

Windowsにおけるパスと改行コード

WindowsとUnix系OSでは、ファイルパスの区切り文字と改行コードが異なります。

  • パス区切り文字:
    • Windows: \ (バックスラッシュ)
    • Unix系: / (スラッシュ)
  • 改行コード:
    • Windows: CRLF (\r\n)
    • Unix系: LF (\n)

これらの違いは、特にCGIスクリプトがファイルパスを扱ったり、HTTPヘッダやコンテンツを出力したりする際に問題を引き起こす可能性があります。PerlスクリプトのようなCGIスクリプトがWindows上で実行される場合、パスの正規化や改行コードの適切な処理が重要になります。

Perlの$^O変数とgetcwd()関数

  • $^O: Perlの特殊変数で、現在のオペレーティングシステムを示す文字列を格納します。例えば、WindowsではMSWin32msysといった値を取ります。
  • getcwd(): 現在の作業ディレクトリのパスを返します。Windows環境では、Perlのバージョンや実行環境(例: msys)によって、返されるパスの形式が異なる場合があります(例: C:\path\to\dir/c/path/to/dir)。

技術的詳細

このコミットは、主に以下の2つのファイルに変更を加えています。

  1. src/pkg/net/http/cgi/host_test.go: テストのスキップ条件を変更し、Windows環境でのTestDirWindowsテストを有効化しています。また、Perlが見つからない場合にテストをスキップするログを追加しています。
  2. src/pkg/net/http/cgi/testdata/test.cgi: CGIテスト用のPerlスクリプトです。Windows環境でのパスの正規化ロジックと、改行コードの扱いが変更されています。

host_test.goの変更点

  • TestDirWindowsテストのスキップ条件から|| runtime.GOOS != "windows"が削除されました。これにより、このテストはWindows環境で常に実行されるようになります。
  • Perlが見つからない場合にテストをスキップする際に、より詳細なログメッセージ(Skipping test: perl not found.)が出力されるようになりました。これはデバッグの際に役立ちます。

test.cgiの変更点

このPerlスクリプトの変更はより重要で、Windows環境でのCGIの動作に関する根本的な問題を解決しようとしています。

  • パス正規化ロジックの削除と再構築:
    • 以前はon_windowsnormalize_windows_pathというサブルーチンが存在し、WindowsパスをGoの形式に正規化しようとしていました。しかし、このロジックは削除されました。
    • 新しいロジックでは、getcwd()で取得したパスがWindows形式(C:\...)でない場合(例: msys環境で/c/...のような形式で返される場合)、cmd.exeを使用して現在のディレクトリを正確に取得する試みが行われています。これは、Perlのgetcwd()が返すパスが環境によって一貫しない問題に対処するためです。
  • 改行コードの統一:
    • 以前は$NL変数を使って改行コードを動的に設定していましたが、この変数は削除され、すべてのprint文で明示的にWindowsの改行コードである\r\nを使用するように変更されました。これにより、HTTPヘッダやコンテンツの出力がWindows環境で正しく解釈されるようになります。
  • 不要なテストコードの削除:
    • Tests::test_normalize_windows_pathsという内部テストコードが削除されました。これは、新しいパス正規化アプローチにより不要になったためです。

これらの変更により、Windows環境におけるCGIスクリプトの実行がより堅牢になり、特にパスの解釈と改行コードの扱いの不一致によるテストの失敗が減少することが期待されます。

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

src/pkg/net/http/cgi/host_test.go

--- a/src/pkg/net/http/cgi/host_test.go
+++ b/src/pkg/net/http/cgi/host_test.go
@@ -404,7 +404,8 @@ func TestDirUnix(t *testing.T) {
 }
 
 func TestDirWindows(t *testing.T) {
-	if skipTest(t) || runtime.GOOS != "windows" {
+	if runtime.GOOS != "windows" {
+		t.Logf("Skipping windows specific test.")
 		return
 	}
 
@@ -414,6 +415,7 @@ func TestDirWindows(t *testing.T) {
 	var err error
 	perl, err = exec.LookPath("perl")
 	if err != nil {
+		t.Logf("Skipping test: perl not found.")
 		return
 	}
 	perl, _ = filepath.Abs(perl)
@@ -456,6 +458,7 @@ func TestEnvOverride(t *testing.T) {
 	var err error
 	perl, err = exec.LookPath("perl")
 	if err != nil {
+		t.Logf("Skipping test: perl not found.")
 		return
 	}
 	perl, _ = filepath.Abs(perl)

src/pkg/net/http/cgi/testdata/test.cgi

--- a/src/pkg/net/http/cgi/testdata/test.cgi
+++ b/src/pkg/net/http/cgi/testdata/test.cgi
@@ -10,23 +10,6 @@ use Cwd;
 
 binmode STDOUT;
 
-sub on_windows {
-    return $^O eq 'MSWin32' || $^O eq 'msys';
-}
-
-# normalize_windows_path normalizes the various Windows Perl path
-# formats into Go's format.
-sub normalize_windows_path {
-    my $dir = shift;
-    return $dir unless on_windows();
-    $dir =~ s!^[a-z]:!uc($&)!e;
-    if ($dir =~ s!^/([a-zA-Z])/!!) {
-        $dir = uc($1) . ":\\$dir";
-    }
-    $dir =~ s!/!\\!g;
-    return $dir;
-}
-
 my $q = MiniCGI->new;
 my $params = $q->Vars;
 
@@ -35,40 +18,43 @@ if ($params->{"loc"}) {
     exit(0);
 }
 
-my $NL = "\r\n";
-$NL = "\n" if $params->{mode} eq "NL";
-
-my $p = sub {
-    print "$_[0]$NL";
-};
-
-# With carriage returns
-$p->("Content-Type: text/html");
-$p->("X-CGI-Pid: $$");
-$p->("X-Test-Header: X-Test-Value");
-$p->("");
+print "Content-Type: text/html\r\n";
+print "X-CGI-Pid: $$\r\n";
+print "X-Test-Header: X-Test-Value\r\n";
+print "\r\n";
 
 if ($params->{"bigresponse"}) {
     for (1..1024) {
-        print "A" x 1024, "\n";
+        print "A" x 1024, "\r\n";
     }\
     exit 0;\
 }\
 
-print "test=Hello CGI\n";
+print "test=Hello CGI\r\n";
 
 foreach my $k (sort keys %$params) {
-    print "param-$k=$params->{$k}\n";
+    print "param-$k=$params->{$k}\r\n";
 }\
 
 foreach my $k (sort keys %ENV) {
     my $clean_env = $ENV{$k};\
     $clean_env =~ s/[\n\r]//g;\
-    print "env-$k=$clean_env\n";
+    print "env-$k=$clean_env\r\n";
 }\
 
-my $dir = normalize_windows_path(getcwd());
-print "cwd=$dir\n";
+# NOTE: msys perl returns /c/go/src/... not C:\go\....
+my $dir = getcwd();
+if ($^O eq 'MSWin32' || $^O eq 'msys') {
+    if ($dir =~ /^.:/) {
+        $dir =~ s!/!\\!g;
+    } else {
+        my $cmd = $ENV{'COMSPEC'} || 'c:\\windows\\system32\\cmd.exe';
+        $cmd =~ s!\\!/!g;
+        $dir = `$cmd /c cd`;
+        chomp $dir;
+    }
+}
+print "cwd=$dir\r\n";
 
 # A minimal version of CGI.pm, for people without the perl-modules
 # package installed.  (CGI.pm used to be part of the Perl core, but
@@ -102,24 +88,3 @@ sub _urldecode {
     $v =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
     return $v;
 }
-\
-package Tests;\
-\
-sub test_normalize_windows_paths {\
-    my @tests = (\
-        {in => "C:\\foo\\bar", want => "C:\\foo\\bar"},\
-        {in => "C:/foo/bar", want => "C:\\foo\\bar"},\
-        {in => "c:/foo/bar", want => "C:\\foo\\bar"},\
-        {in => "/c/foo/bar", want => "C:\\foo\\bar"},\
-    );\
-    foreach my $tt (@tests) {\
-        my $got = ::normalize_windows_path($tt->{in});\
-        unless ($got eq $tt->{want}) {\
-            die "For path $tt->{in}, normalize = $got; want $tt->{want}\\n";
-        }\
-    }\
-}\
-\
-BEGIN {\
-    test_normalize_windows_paths() if ::on_windows();\
-}\

コアとなるコードの解説

host_test.go

  • TestDirWindows関数内のif runtime.GOOS != "windows"の条件からskipTest(t) ||が削除されました。これは、このテストがWindows環境でのみ実行されるべきであり、他のプラットフォームではスキップされるべきであることを明確にしています。また、Windows環境以外でスキップされる場合にt.Logf("Skipping windows specific test.")というログが出力されるようになりました。
  • exec.LookPath("perl")でPerlが見つからなかった場合、t.Logf("Skipping test: perl not found.")というログが出力されるようになりました。これにより、テストがスキップされた理由が明確になり、デバッグが容易になります。

test.cgi

  • パス正規化ロジックの変更:
    • 以前のon_windowsおよびnormalize_windows_pathサブルーチンは削除されました。これらのサブルーチンは、Windowsパスの様々な形式をGoが期待する形式に変換しようとしていましたが、そのロジックが複雑で、すべてのケースをカバーできていなかった可能性があります。
    • 新しいロジックでは、getcwd()で取得したパスがWindowsのドライブレター形式(例: C:)で始まらない場合(これはmsys環境などで/c/go/src/...のような形式でパスが返される場合に発生します)、cmd.exeを呼び出して現在のディレクトリを正確に取得するようになりました。
      • my $cmd = $ENV{'COMSPEC'} || 'c:\\windows\\system32\\cmd.exe';COMSPEC環境変数(通常はcmd.exeのパス)を取得し、なければデフォルトのパスを使用します。
      • $cmd =~ s!\\!/!g;:コマンドパスのバックスラッシュをスラッシュに変換します。これは、Perlがシェルコマンドを実行する際にスラッシュパスを好むためです。
      • $dir = $cmd /c cd;cmd.exe /c cdを実行して、現在のディレクトリを正確に取得します。/cオプションは、コマンドを実行した後にcmd.exeを終了させます。
      • chomp $dir;:取得したディレクトリパスの末尾の改行文字を削除します。
    • この変更により、Perlのgetcwd()が返すパスの形式に依存せず、Windows環境で常に正しい現在の作業ディレクトリを取得できるようになります。
  • 改行コードの統一:
    • 以前は$NL変数を使って改行コードを動的に設定していましたが、この変数は削除されました。
    • すべてのprint文で、明示的にWindowsの改行コードである\r\nが使用されるようになりました。これにより、CGIスクリプトが出力するHTTPヘッダやコンテンツが、Windows環境で正しく解釈され、表示されることが保証されます。特に、HTTPヘッダはCRLFで区切られることがRFCで定められているため、この変更は重要です。
  • テストコードの削除:
    • Tests::test_normalize_windows_pathsという内部テストコードが削除されました。これは、新しいパス正規化アプローチにより、以前の正規化ロジックが不要になったためです。

これらの変更は、Windows環境におけるCGIスクリプトの実行の堅牢性を高め、特にパスの解釈と改行コードの扱いの不一致によるテストの失敗を減少させることを目的としています。

関連リンク

参考にした情報源リンク