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

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

このコミットは、Go言語の net/http/cgi パッケージ内のテストデータである test.cgi ファイルに対する変更です。具体的には、Windows環境におけるPerl CGIスクリプトのテストの堅牢性を向上させることを目的としています。特に、cmd.exe の特定のパスへの依存を排除し、Windowsパスの正規化をより適切に行うための修正が含まれています。

コミット

commit e070aeae779b641a5180c1807de19bfc9e5864c2
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Mon Nov 19 10:40:13 2012 -0800

    net/http/cgi: more windows perl test work
    
    Don't rely on finding cmd.exe in a particular spot.
    
    Fixes #4401
    
    R=golang-dev, krautz
    CC=golang-dev
    https://golang.org/cl/6842066

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

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

元コミット内容

このコミットは、net/http/cgi パッケージのテストスクリプト test.cgi において、Windows環境でのPerlの動作に関する問題を修正するものです。以前のバージョンでは、現在の作業ディレクトリ(CWD)を取得するために cmd.exe を呼び出すという、特定のパスに依存した回避策が用いられていました。このコミットは、その cmd.exe への依存を取り除き、Windowsパスの正規化をPerlスクリプト内で直接行う新しい関数を導入することで、テストの信頼性を高めています。

変更の背景

Go言語の net/http/cgi パッケージは、CGI(Common Gateway Interface)プロトコルを介して外部プログラムを実行し、その出力をHTTPレスポンスとして提供するための機能を提供します。このパッケージのテストには、Perlで書かれたCGIスクリプト test.cgi が使用されています。

Windows環境では、Perlの getcwd() 関数が返すパスの形式が、Goの期待する形式と異なる場合や、msys (Minimal SYStem) のような環境ではUnix形式のパス(例: /c/go/src/...)を返すことがありました。このため、以前の test.cgi では、Windows上で現在の作業ディレクトリを取得する際に、環境変数 COMSPEC または固定パス c:\\windows\\system32\\cmd.exe を使用して cmd.exe を呼び出し、その cd コマンドの出力からCWDを取得するという、やや複雑で環境に依存するロジックが組み込まれていました。

この cmd.exe への依存は、cmd.exe が特定の場所に存在しない場合や、異なるバージョンのWindowsでパスが変更された場合にテストが失敗する可能性がありました。また、msys 環境でのパスの不整合も問題となっていました。

このコミットは、これらの問題を解決し、Windows環境でのCGIテストの堅牢性と移植性を向上させることを目的としています。具体的には、GoのIssue #4401で報告された問題に対応しています。

前提知識の解説

  • CGI (Common Gateway Interface): Webサーバーが外部プログラム(CGIスクリプト)と連携するための標準的なインターフェースです。WebサーバーはHTTPリクエストの情報をCGIスクリプトに渡し、スクリプトは処理結果を標準出力に書き出し、WebサーバーがそれをHTTPレスポンスとしてクライアントに返します。
  • Perl: スクリプト言語の一つで、特にCGIスクリプトの記述によく用いられます。test.cgi はPerlで書かれています。
  • net/http/cgi パッケージ (Go言語): Go言語の標準ライブラリの一部で、CGIプロトコルを実装し、GoアプリケーションからCGIスクリプトを実行したり、Goアプリケーション自体をCGIとして動作させたりするための機能を提供します。
  • Windowsのパス形式: Windowsでは、パスは通常、ドライブレター(例: C:)から始まり、ディレクトリの区切り文字としてバックスラッシュ(\)を使用します(例: C:\Users\User\Documents)。
  • Unix/Linuxのパス形式: Unix系OSでは、パスはルートディレクトリ(/)から始まり、ディレクトリの区切り文字としてスラッシュ(/)を使用します(例: /home/user/documents)。
  • MSYS/Cygwin: Windows上でUnix/Linuxのような環境を提供するツールです。これらの環境では、WindowsのパスがUnix形式(例: C:\foo\bar/c/foo/bar)に変換されることがあります。
  • getcwd() (Perl): Perlの組み込み関数で、現在の作業ディレクトリのパスを返します。Windows環境では、この関数の挙動が環境によって異なることが問題の原因となっていました。
  • cmd.exe: Windowsのコマンドプロンプトの実行ファイルです。cd コマンドは現在の作業ディレクトリを表示するために使用されます。
  • $^O (Perl): Perlの特殊変数で、現在のオペレーティングシステムの名前を格納しています。Windowsでは MSWin32msys などの値を取ります。

技術的詳細

このコミットの主要な技術的変更点は、Windows環境におけるパスの正規化ロジックを test.cgi スクリプト内に直接組み込んだことです。

  1. on_windows サブルーチンの導入:

    • このサブルーチンは、現在の実行環境がWindowsであるかどうかを判定します。Perlの特殊変数 $^O の値が 'MSWin32' または 'msys' であるかをチェックします。これにより、Windows固有の処理を条件付きで実行できるようになります。
  2. normalize_windows_path サブルーチンの導入:

    • このサブルーチンは、様々な形式のWindowsパスをGoが期待する標準的なWindowsパス形式に正規化します。
    • ドライブレターの正規化: ^[a-z]: のような小文字のドライブレターを大文字に変換します(例: c:/fooC:/foo に)。
    • MSYS/Cygwin形式パスの変換: /c/foo/bar のようなUnix形式のパスで、先頭が /ドライブレター/ となっているものを、C:\foo\bar のようなWindows形式に変換します。これは正規表現 s!^/([a-zA-Z])/!! と置換 uc($1) . ":\\\\$dir" を用いて行われます。
    • スラッシュからバックスラッシュへの変換: すべてのスラッシュ(/)をバックスラッシュ(\)に変換します。これはWindowsのパス区切り文字に合わせるためです。
    • この関数により、getcwd() が返すパスがどのような形式であっても、Goのテストが期待する一貫したWindowsパス形式に変換されるようになります。
  3. cmd.exe 呼び出しロジックの削除と normalize_windows_path の適用:

    • 以前の cmd.exe を呼び出してCWDを取得する複雑なロジックが削除されました。
    • 代わりに、Perlの標準関数 getcwd() で取得したパスを、新しく導入された normalize_windows_path サブルーチンで正規化するようになりました。これにより、外部コマンドへの依存が解消され、コードが簡潔かつ堅牢になりました。
  4. テストケースの追加:

    • Tests パッケージ内に test_normalize_windows_paths というテストサブルーチンが追加されました。
    • このテストは、normalize_windows_path が正しく機能するかどうかを検証するためのものです。様々な入力パス(例: C:\\foo\\bar, C:/foo/bar, c:/foo/bar, /c/foo/bar)に対して、期待される正規化された出力(C:\\foo\\bar)が得られるかをチェックします。
    • このテストは on_windows() が真の場合、つまりWindows環境でのみ実行されます。これにより、Windows固有のパス正規化ロジックが正しく動作することが保証されます。

これらの変更により、test.cgi はWindows環境におけるパスの差異をより適切に扱い、cmd.exe の存在やパスに依存することなく、安定して動作するようになりました。

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

変更は 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,6 +10,23 @@ 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;
 
@@ -41,29 +58,18 @@ if ($params->{"bigresponse"}) {
 print "test=Hello CGI\n";
 
 foreach my $k (sort keys %$params) {
-  print "param-$k=$params->{$k}\n";
+    print "param-$k=$params->{$k}\n";
 }
 
 foreach my $k (sort keys %ENV) {
-  my $clean_env = $ENV{$k};
-  $clean_env =~ s/[\n\r]//g;
-  print "env-$k=$clean_env\n";
+    my $clean_env = $ENV{$k};
+    $clean_env =~ s/[\n\r]//g;
+    print "env-$k=$clean_env\n";
 }
 
-# NOTE: don't call getcwd() for windows.
-# msys return /c/go/src/... not C:\go\...
-my $dir;
-if ($^O eq 'MSWin32' || $^O eq 'msys') {
-  my $cmd = $ENV{'COMSPEC'} || 'c:\\windows\\system32\\cmd.exe';
-  $cmd =~ s!\\!/!g;
-  $dir = `$cmd /c cd`;
-  chomp $dir;
-} else {
-  $dir = getcwd();
-}
+my $dir = normalize_windows_path(getcwd());
 print "cwd=$dir\n";
 
-\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
 # some distros now bundle perl-base and perl-modules separately...)
 @@ -96,3 +102,24 @@ sub _urldecode {
     $v =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
     return $v;
 }\n+\n+package Tests;\n+\n+sub test_normalize_windows_paths {\n+    my @tests = (\n+        {in => "C:\\\\foo\\\\bar", want => "C:\\\\foo\\\\bar"},\n+        {in => "C:/foo/bar", want => "C:\\\\foo\\\\bar"},\n+        {in => "c:/foo/bar", want => "C:\\\\foo\\\\bar"},\n+        {in => "/c/foo/bar", want => "C:\\\\foo\\\\bar"},\n+    );\n+    foreach my $tt (@tests) {\n+        my $got = ::normalize_windows_path($tt->{in});\n+        unless ($got eq $tt->{want}) {\n+            die "For path $tt->{in}, normalize = $got; want $tt->{want}\\n";\n+        }\n+    }\n+}\n+\n+BEGIN {\n+    test_normalize_windows_paths() if ::on_windows();\n+}\n```

## コアとなるコードの解説

*   **`sub on_windows`**:
    ```perl
    sub on_windows {
        return $^O eq 'MSWin32' || $^O eq 'msys';
    }
    ```
    この関数は、Perlの組み込み変数 `$^O` をチェックして、現在のOSがWindows(`MSWin32`)またはMSYS環境(`msys`)であるかを判定します。これにより、Windows固有のパス処理を条件付きで適用できます。

*   **`sub normalize_windows_path`**:
    ```perl
    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;
    }
    ```
    この関数がパス正規化の核心です。
    1.  `return $dir unless on_windows();`: Windows環境でない場合は、パスをそのまま返します。
    2.  `$dir =~ s!^[a-z]:!uc($&)!e;`: パスの先頭が小文字のドライブレター(例: `c:`)で始まる場合、それを大文字に変換します(例: `C:`)。`$&` はマッチした文字列全体を指します。
    3.  `if ($dir =~ s!^/([a-zA-Z])/!!) { $dir = uc($1) . ":\\\\$dir"; }`: パスが `/c/foo` のようなMSYS/Cygwin形式の場合、これを `C:\foo` のようなWindows形式に変換します。`$1` は正規表現の最初のキャプチャグループ(ここではドライブレター)を指します。
    4.  `$dir =~ s!/!\\\\!g;`: すべてのスラッシュ(`/`)をバックスラッシュ(`\`)に変換します。`\\\\` はPerlの文字列リテラルでバックスラッシュをエスケープしたものです。

*   **CWD取得ロジックの変更**:
    ```perl
    -my $dir;
    -if ($^O eq 'MSWin32' || $^O eq 'msys') {
    -  my $cmd = $ENV{'COMSPEC'} || 'c:\\windows\\system32\\cmd.exe';
    -  $cmd =~ s!\\!/!g;
    -  $dir = `$cmd /c cd`;
    -  chomp $dir;
    -} else {
    -  $dir = getcwd();
    -}
    +my $dir = normalize_windows_path(getcwd());
    ```
    以前の `cmd.exe` を呼び出す複雑なロジックが削除され、`getcwd()` で取得したパスを `normalize_windows_path` で処理する簡潔な形に変更されました。

*   **テストケース `test_normalize_windows_paths`**:
    ```perl
    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();
    }
    ```
    このテストは、`normalize_windows_path` 関数が様々な入力に対して正しくパスを正規化するかを検証します。`BEGIN` ブロック内で `on_windows()` が真の場合にのみ実行されるため、Windows環境でのみテストが実行されます。

## 関連リンク

*   Go Issue #4401: [https://github.com/golang/go/issues/4401](https://github.com/golang/go/issues/4401)
*   Go CL 6842066: [https://golang.org/cl/6842066](https://golang.org/cl/6842066)

## 参考にした情報源リンク

*   Perl `$^O` 変数に関するドキュメント: [https://perldoc.perl.org/perlvar#%24%5EO](https://perldoc.perl.org/perlvar#%24%5EO)
*   Perl `getcwd()` 関数に関するドキュメント: [https://perldoc.perl.org/Cwd#getcwd](https://perldoc.perl.org/Cwd#getcwd)
*   CGI (Common Gateway Interface) の概要: [https://ja.wikipedia.org/wiki/Common_Gateway_Interface](https://ja.wikipedia.org/wiki/Common_Gateway_Interface)
*   Windowsのパス形式に関する情報 (Microsoft Docsなど)
*   MSYS/Cygwinのパス変換に関する情報