読者です 読者をやめる 読者になる 読者になる

IPC::Open2 が Catalyst のテストサーバで動かない件の対処

IPC::Open2 (Open3) が Catalyst のテストサーバでまともに動かない。ハンドルを操作しようとすると Broken pipe のエラーが起きる。

package MyApp::Controller::Foo;
sub foo :Private {
    my ( $self, $c ) = @_;
    open2( my $out, my $in, "/path/to/command" );
    print $in "input" or die $!;        # <-- Broken pipe

検索して Re: [Catalyst] Catalyst and IPC::Open2 を見つけた。それによると

STDOUT and STDERR probably aren't what it expects.
Try doing the same save+restore trick I use here -
http://dev.catalyst.perl.org/repos/Catalyst/trunk/Catalyst-Controller-WrapCGI/lib/Catalyst/Controller/WrapCGI.pm

STDIN と STDOUT が期待してるのと違っちゃってるから C::C::WrapCGI みたいなトリックを使えと。
実際、Catalyst::Engine::HTTP では

sub _handler {
    my ( $self, $class, $port, $method, $uri, $protocol ) = @_;

    local *STDIN  = \*Remote;
    local *STDOUT = \*Remote;

こんなことをしてるのだった。

てことで、Catalyst::Controller::WrapCGI からコードをもらってきて myapp_server.pl で以下のような対処をして解決。require MyApp; から MyApp->run の間にコードを追加。

require MyApp; 

open my $REAL_STDIN, "<&=".fileno(*STDIN); 
open my $REAL_STDOUT, ">>&=".fileno(*STDOUT); 

sub MyApp::dispatch { 
    my $class = shift; 
    local *STDIN  = $REAL_STDIN;    # restore the real ones so the filenos 
    local *STDOUT = $REAL_STDOUT;   # are 0 and 1 for the env setup 
    my $old = select($REAL_STDOUT); # in case somebody just calls 'print' 
    $class->NEXT::dispatch(@_); 
    select $old; 
}; 

MyApp->run( $port, $host, {