locked
CGI::SessionがGET呼び出しでは動作するが、POST呼び出しで動作しない RRS feed

  • 質問

  • こんにちは。

    下記環境でPerlで書いたCGIを動作させています。

    OS:Windows Server 2012 R2
    IIS:8.5
    Perl:StrawberryPerl 5.16.3.1
    モジュール:CGI::Session 4.48

    セッション管理をしたいのですが、CGI::Session->newしようとすると
    記述してあるcgiファイルをGETでの呼び出しでは動作しますが、
    POSTで呼び出した時にはエラーで止まってしまいます。
    問題部分を以下のコードにまとめました。

    #!/usr/bin/perl
    use CGI::Session;
    $chdir = $0;
    $chdir =~ s/[^\\]*$//;
    chdir($chdir);
    $_SESSION=CGI::Session->new(undef,undef,{Directory=>'session'});
    print "Content-type: text/html \n\nHello";
    exit;

    止まる場所は、CGI::Session->new()の部分です。
    GETの場合は、正常に表示され、sessionフォルダ内にファイルができます。
    このような現象についてIISやアクセス許可の設定で疑うべき場所や
    解決法をご存知の方はいらっしゃらないでしょうか。

    GETでの成功時、POSTでの失敗時それぞれ要求トレース結果の差分は、
    下記の通りです。

    トレース結果上のPOST側から見た違い
    (1) GENERAL_REQUEST_HEADERS にContent-Lengthがある。
    (2) GENERAL_REQUEST_HEADERS の後にGENERAL_REQUEST_ENTITY がある。
    (3) CGI_LAUNCH とCGI_REQUEST_ENTITY_SENT の間にNOTIFY_MODULE_COMPLETION がある。

    NOTIFY_MODULE_COMPLETION の内容
      ModuleName                  CgiModule
      Notification                EXECUTE_REQUEST_HANDLER
      fIsPostNotificationEvent    false
      CompletionBytes             14
      ErrorCode                   この操作を正しく終了しました。
                                  (0x0)

    上記の直後(タイムアウト時間経過後)、MODULE_SET_RESPONSE_ERROR_STATUS があり、
    HttpStatus が502になっています。


    2017年4月28日 7:07

回答

  • 単純な質問なんですが、readから制御が返った時、$PostData にはデータが入っているんでしょうか?
    readの戻り値は $ENV{'CONTENT_LENGTH'} になっているんでしょうか?

    どうも read でタイムアウトしている感じに見受けられるのですが・・・。
    IISでCGIを扱ったことはないので詳しくは分かりませんが、ちゃんと標準入力にリダイレクトされているんでしょうか???

    追記:

    ↓なので、コンソールなしで動いちゃっているんじゃないでしょうか?

    CreateCGIWithNewConsole 起動ごとに新しいコンソールを使用する

    このプロパティは、CGI アプリケーションがそれ自体のコンソール内で動作するかどうかを示します。この値が TRUE の場合、各CGI アプリケーションは起動時に新しいコンソールを作成します。この値が FALSE の場合、CGI アプリケーションはコンソールなしで動作することを示します。


    • 編集済み nk8 2017年5月10日 14:29
    • 回答としてマーク 千住水 2017年5月11日 7:31
    2017年5月10日 11:57
  • 解決できました。

    現象との因果関係までは分かりませんが、CGI::Session->newで第二引数であるSIDに
    undefではなく空文字列を指定する事でundefと同様の動作ができるようになりました。

    $_SESSION=CGI::Session->new(undef,undef,{Directory=>'session'});

    $sid = "";
    $_SESSION=CGI::Session->new(undef,$sid,{Directory=>'session'});


    以上の事をまとめると、下記になります。

    【現象】
    readでSTDINから1文字以上の文字列を読み出した場合、
    次に実行するCGI::Session->newで第二引数であるSIDに
    undefを入れると動作が止まる。

    【対策】
    CGI::Session->newの第二引数には、空文字列を指定する。

    【サンプルコード】
    #!c:/strawberry/perl/bin/perl
    use CGI::Session;
    $chdir = $0;
    $chdir =~ s/[^\\]*$//;
    chdir($chdir);
    $ret = read( STDIN, $PostData, $ENV{'CONTENT_LENGTH'} );
    print "Content-type: text/html \n\nHello ";
    $sid = "";
    $_SESSION=CGI::Session->new(undef,$sid,{Directory=>'session'});
    if($_SESSION) {
        $sid = $_SESSION->id();
        print "Session Create Succeeded! SID = $sid ";
    } else {
        print "Session Create Failed! ";
    }
    exit;



    knakagawa1971さん、回答協力ありがとうございました。

    • 回答としてマーク 千住水 2017年5月11日 7:31
    2017年5月11日 7:29

すべての返信

  • 追加情報

    下記環境でも同様でした。
    Perl:ActivePerl 5.24.1.2402

    また、コードのDirectoryを下記に修正しても症状は変わりませんでした。

    'session' #元のコード
    './session'
    'c:/cgi-local/sessin' #フルパス

    2017年5月9日 5:31
  • さらに追加情報として、関連しそうなIISの現在の設定です。

    ■サイトの詳細設定
    ・バインド:https:*:443:
    ・物理パス資格情報のログオンの種類:ClearText
    ・有効化されたプリロード:False
    ・接続
     - 最大 URL セグメント:32
     - 最大帯域幅(バイト/秒):4294967295 (デフォルト)
     - 最大同時接続数:4294967295 (デフォルト)
     - 接続のタイムアウト(秒):1200
    ・有効なプロトコル:http
    ■CGI
    ・偽装ユーザー:True
    ・タイムアウト:00:15:00
    ・起動ごとに新しいコンソールを使用する:False
    ■SSL設定
    ・SSLが必要:チェックあり
    ・クライアント証明書:無視
    ■ハンドラーマッピング
    (CgiModule)
    要求の制限:
    ・要求のマップ左記が次の場合のみハンドラーを呼び出す:チェックあり
    ・ファイルまたはフォルダーを選択
    ・すべての動詞を選択
    ・スクリプトを選択
    ■認証
    ・ASP.NET 偽装:無効
    ・Windows 認証:無効
    ・フォーム認証:無効
    ・基本認証:無効
    ・匿名認証:有効
    ■要求フィルター
    ・特に追加要素なし

    修正:

    タイムアウト時間の値が実際に指定している値と異なっていました。

    00:00:00ではなく、00:15:00(デフォルト)でした。

    • 編集済み 千住水 2017年5月11日 2:12
    2017年5月9日 6:14
  • すみません、現象が発生原因になるコードが間違っていました。
    正しくは、POSTで呼び出したからではなく、CGI::Session->newより前に
    POSTのデータを読み出すため、STDINからreadした場合に発生していました。

    つまり、現象が発生するコードは、下記になります。

    #!c:/strawberry/perl/bin/perl
    use CGI::Session;
    $chdir = $0;
    $chdir =~ s/[^\\]*$//;
    chdir($chdir);
    read( STDIN, $PostData, $ENV{'CONTENT_LENGTH'} );
    $_SESSION=CGI::Session->new(undef,undef,{Directory=>'session'}); # POSTでは、ここで処理が止まる
    print "Content-type: text/html \n\nHello";
    exit;

    これについて、現象の発生原因(readとCGI::Sessionの因果関係)または
    改善策をご存知の方はいらっしゃらないでしょうか。
    尚、Session開始前にPOSTで渡されるIDとパスワードのチェックをしたいため、
    上記の順番で処理を行いたいと考えています。
    2017年5月10日 10:04
  • 単純な質問なんですが、readから制御が返った時、$PostData にはデータが入っているんでしょうか?
    readの戻り値は $ENV{'CONTENT_LENGTH'} になっているんでしょうか?

    どうも read でタイムアウトしている感じに見受けられるのですが・・・。
    IISでCGIを扱ったことはないので詳しくは分かりませんが、ちゃんと標準入力にリダイレクトされているんでしょうか???

    追記:

    ↓なので、コンソールなしで動いちゃっているんじゃないでしょうか?

    CreateCGIWithNewConsole 起動ごとに新しいコンソールを使用する

    このプロパティは、CGI アプリケーションがそれ自体のコンソール内で動作するかどうかを示します。この値が TRUE の場合、各CGI アプリケーションは起動時に新しいコンソールを作成します。この値が FALSE の場合、CGI アプリケーションはコンソールなしで動作することを示します。


    • 編集済み nk8 2017年5月10日 14:29
    • 回答としてマーク 千住水 2017年5月11日 7:31
    2017年5月10日 11:57
  • knakagawa1971さん

    ご返信ありがとうございます。

    $PostDataには、読み込んだデータが入っており、
    readの戻り値と、$ENV{'CONTENT_LENGTH'}は等しく、
    $PostDataに格納した文字列長と等しい値でした。

    readのみ確認したコードは、下記の通りです。

    #!c:/strawberry/perl/bin/perl
    use CGI::Session;
    $chdir = $0;
    $chdir =~ s/[^\\]*$//;
    chdir($chdir);
    $ret = read( STDIN, $PostData, $ENV{'CONTENT_LENGTH'} );
    #$_SESSION=CGI::Session->new(undef,undef,{Directory=>'session'}); # POSTでは、ここで処理が止まる
    print "Content-type: text/html \n\nHello ";
    $contentlen = $ENV{'CONTENT_LENGTH'};
    print "PostData = '$PostData' ";
    print "Content Length = $contentlen ";
    print "Read Return = $ret ";
    exit;

    > CreateCGIWithNewConsole 起動ごとに新しいコンソールを使用する
    については、Trueにしてみましたが、残念ながら症状は変わりませんでした。
    この場合でも$PostDataの取得状況は上記と同様で、正常に取得できておりました。

    追記:

    状況をもう少し判別しやすくするため
    コードを下記に修正しました。

    #!c:/strawberry/perl/bin/perl
    use CGI::Session;
    $chdir = $0;
    $chdir =~ s/[^\\]*$//;
    chdir($chdir);
    $ret = read( STDIN, $PostData, $ENV{'CONTENT_LENGTH'} );
    print "Content-type: text/html \n\nHello ";
    $contentlen = $ENV{'CONTENT_LENGTH'};
    print "PostData = '$PostData' ";
    print "Content Length = $contentlen ";
    print "Read Return = $ret ";
    $_SESSION=CGI::Session->new(undef,undef,{Directory=>'session'}); # POSTでは、ここで処理が止まる
    if($_SESSION) {
        print "Session Create Succeeded! ";
    } else {
        print "Session Create Failed! ";
    }
    exit;

    • 編集済み 千住水 2017年5月11日 2:10
    2017年5月11日 1:48
  • 違いましたか。

    GETメソッドで正常でしたら スクリプト自体に問題はないと思います。
    ちょっと検索してみたら、同じようなIIS CGI/PerlでPOSTメソッドの時、405エラーが出る、というような記事もありますが、今回とは状況が違うみたいですね。

    的外れな返信ですみませんでした。

    2017年5月11日 4:18
  • いえ、ありがとうございます。

    さらに試しているところとしては、同じPOST呼び出しでも
    渡すデータ長が0であれば動作しているようです。
    つまり、現状の現象としては、
    「readでSTDINから1文字以上の文字列を読み出した場合、
     次に実行するCGI::Session->newで停止する」
    となっています。
    2017年5月11日 5:08
  • 解決できました。

    現象との因果関係までは分かりませんが、CGI::Session->newで第二引数であるSIDに
    undefではなく空文字列を指定する事でundefと同様の動作ができるようになりました。

    $_SESSION=CGI::Session->new(undef,undef,{Directory=>'session'});

    $sid = "";
    $_SESSION=CGI::Session->new(undef,$sid,{Directory=>'session'});


    以上の事をまとめると、下記になります。

    【現象】
    readでSTDINから1文字以上の文字列を読み出した場合、
    次に実行するCGI::Session->newで第二引数であるSIDに
    undefを入れると動作が止まる。

    【対策】
    CGI::Session->newの第二引数には、空文字列を指定する。

    【サンプルコード】
    #!c:/strawberry/perl/bin/perl
    use CGI::Session;
    $chdir = $0;
    $chdir =~ s/[^\\]*$//;
    chdir($chdir);
    $ret = read( STDIN, $PostData, $ENV{'CONTENT_LENGTH'} );
    print "Content-type: text/html \n\nHello ";
    $sid = "";
    $_SESSION=CGI::Session->new(undef,$sid,{Directory=>'session'});
    if($_SESSION) {
        $sid = $_SESSION->id();
        print "Session Create Succeeded! SID = $sid ";
    } else {
        print "Session Create Failed! ";
    }
    exit;



    knakagawa1971さん、回答協力ありがとうございました。

    • 回答としてマーク 千住水 2017年5月11日 7:31
    2017年5月11日 7:29