2007-05-10 16:08:26

Catalyst::Engineでバグ?2

[Catalyst]

"Catalyst::Engineでバグ?"のつづき。

別のマシン(仮称:test2 - Fedora Core 6 i386 perl5.8.8)で実行すると普通に動く。しかも廻さず一回。
$maxlengthは ①65536
$remainingは ①6809
$self->read_positionは ①6809
$rcは ①6809

大きなサイズ104052バイトもテスト。こちらは二回り。
$maxlengthは ①65536 ②65536
$remainingは ①104052 ②43154
$self->read_positionは ①60898 ②104052
$rcは ①60898 ②43154
60708バイトもテスト。一回り。
$maxlengthは ①65536
$remainingは ①60708
$self->read_positionは ①60708
$rcは ①60708

Catalyst::Engineの readメソッドで
my $readlen = ( $remaining > $maxlength ) ? $maxlength : $remaining;
my $rc = $self->read_chunk( $c, my $buffer, $readlen );
のようにあるから、6809バイトならば一気に読めてよいはず。

Catalyst::Engine::CGIの read_chunkは以下。
sub read_chunk { shift; shift; *STDIN->sysread(@_); }

STDINが気になった。
perlのテスト。下のスクリプトを実行してみる。

#!/usr/bin/perl
print "input long length\n";
$string = <STDIN>;
my $length = length($string);
print "$length\n";

test2のターミナルで4100バイトの文字列を入れようとしても、4095バイトで入力受付終了。
Enterキー押下で普通にprintされる。Enterキー込みで4096バイト。
Catalyst::Engineで悩んでいるマシン(CentOS4.4 Server x86_64 perl5.8.8-x86_64)でも4095バイトで入力受付終了。
・・・なのだが、Enterキーを受け付けない。
4094バイトにすると、Enterキーが押せてprintされる。Enterキー込みで4095バイト。
泥沼の予感。

IO::HandleでCatalyst::Engine::CGIっぽく。

#!/usr/bin/perl
use strict;
use warnings;
use IO::Handle;
my $stlen = 40960;
print "input long length\n";
my $length = read_chunk(my $buffer, $stlen);
print "$length\n";
sub read_chunk {
        *STDIN->sysread(@_);
}

やはりマシンによって受けられるデータ量が4096バイトと4095バイトで違いが出る。
4095バイトでEnter出来ないのも同じ。

Cでも試す。

#include <stdio.h>
#include <string.h>
main(void) {
    char string[4100];
    int i;
    printf("input long length\n");
    fgets(string, 4100, stdin);
    i = strlen(string);
    printf("%d\n", i);
    return 0;
}

同じくマシンによって受けられるデータ量が4096バイトと4095バイトで違いが出る。
性質が悪いのが、4095バイトまでコンソール上は受け入れておいてEnterキーが押せないこと。

さらに別マシン(Fedora Core 6 i386)でもテストする。4096バイト。問題なし。
泥沼だ orz
STDINを追うのは中断。

次はsysreadを考える。
test2で以下のCGIを実行する。

#!/usr/bin/perl
use strict;
use CGI;
sysread( STDIN, my $string, $ENV{'CONTENT_LENGTH'} );
my $c_length = $ENV{'CONTENT_LENGTH'};
my $length = length($string);
my $cgi = new CGI;
#(余談)STDINをreadする前に new CGIするとSTDINの中身を先にインスタンスにとられる。
print $cgi->header,
    $cgi->start_html,
    $cgi->p($c_length),
    $cgi->p($length),
    $cgi->start_form(-method=>'post', -enctype=>'application/x-www-form-urlencoded'),
    $cgi->textarea('str', '', 10, 100),
    $cgi->submit,
    $cgi->end_form,
    $cgi->end_html;

4100バイトの文字列をフォームに入力する。
$c_length = 4141
$length = 4141
と確認できた。

問題のマシンで実行して、同様の文字列を入力した。
$c_length = 4141
$length = 1387

sysread が1387バイトしか読んでいない・・・悲しくなった。
sysread を read に変えると、$length = 4141 となった。

sysread参考
http://www2u.biglobe.ne.jp/~MAS/perl/ref/sysread.html
http://perldoc.perl.org/functions/sysread.html
(perldoc perlfunc)

Catalyst::Engine::CGIの read_chunk の sysread を read に変更。
一度の読み込みバイト数は Catalyst::Engine の $maxlength で指定しているので問題なし。
何故 sysread だったのか疑問。うちの環境がダメってのはあるが・・・。
Catalyst-MLに以下パッチを送る。

--- lib/Catalyst/Engine/CGI.pm    (revision 6278)
+++ lib/Catalyst/Engine/CGI.pm    (local)
@@ -211,7 +211,7 @@

 =cut

-sub read_chunk { shift; shift; *STDIN->sysread(@_); }
+sub read_chunk { shift; shift; *STDIN->read(@_); }

 =head2 $self->run

※2007.05.07の Test::Controller::Root(同一環境の別アプリケーション)がsysreadのまま普通に動作したのが納得できなくて気持ち悪い。。。


2007.05.11追記
パッチはNG。sysread で期待値(LENGTH)が帰ってこない事は折込済のようだ。
Catalyst::Engineの prepare_body
while ( my $buffer = $self->read($c) ) {
     $c->prepare_body_chunk($buffer);
}
でバッファが返ってくる限り処理を続けることはわかっていたけど、戻り値等全く検証してなくてダメダメ。説明さえ出来ない。
原因を完全特定できるようにもっとがんばることにすべし!
STDIN->sysread を STDIN->read にすると問題は再発しない・・・何故だ?
とは言ってもSTDINを追うのはやめたいなぁ。環境を作り直すことは現状不可能。STDINは今のままは確定。

英語は全く駄目 orz 書きたいことが書けない。勉強することにする(関係代名詞を今日生まれて初めて理解できた)。