File Coverage

blib/lib/Plack/App/FCGIDispatcher.pm
Criterion Covered Total %
statement 41 63 65.0
branch 7 20 35.0
condition 2 6 33.3
subroutine 8 8 100.0
pod 1 1 100.0
total 59 98 60.2


line stmt bran cond sub pod time code
1             package Plack::App::FCGIDispatcher;
2 3     3   408533 use strict;
  3         7  
  3         107  
3 3     3   17 use warnings;
  3         6  
  3         90  
4 3     3   821 use parent qw(Plack::Component);
  3         294  
  3         20  
5 3     3   17601 use Plack::Util::Accessor qw(host port socket timeout);
  3         461  
  3         21  
6              
7 3     3   278 use 5.008001;
  3         10  
  3         101  
8 3     3   3646 use FCGI::Client;
  3         123071  
  3         83  
9 3     3   1989 use HTTP::Response;
  3         65651  
  3         1971  
10              
11             our $VERSION = '0.10';
12              
13             sub call {
14 1     1 1 37879 my $self = shift;
15 1         5 my $env = shift;
16              
17 1         16 my $fcgi_env = +{ %$env }; # shallow copy
18              
19 1         4 my $sock;
20 1 50       21 if ($self->socket) {
    50          
21 0         0 require IO::Socket::UNIX;
22 0         0 $sock = IO::Socket::UNIX->new(
23             Peer => $self->socket,
24             );
25             } elsif ($self->port) {
26 1         56 require IO::Socket::INET;
27 1   50     13 $sock = IO::Socket::INET->new(
28             PeerAddr => $self->host || '127.0.0.1',
29             PeerPort => $self->port,
30             );
31             } else {
32 0         0 die "FCGI daemon host/port or socket is not specified";
33             }
34              
35 1 50       613 $sock or die "Can't create socket to FCGI daemon: $!";
36              
37 1   50     8 my $conn = FCGI::Client::Connection->new(
38             sock => $sock,
39             timeout => $self->timeout || 60,
40             );
41 1         465 my $input = delete $fcgi_env->{'psgi.input'};
42              
43 1         24 my $content_in = '';
44 1 50       6 if (my $cl = $env->{CONTENT_LENGTH}) {
45 0         0 while ($cl > 0) {
46 0         0 my $read = $input->read($content_in, $cl, length $content_in);
47 0         0 $cl -= $read;
48             }
49             }
50              
51 1         7 for my $key (keys %$fcgi_env) {
52 21 100       66 delete $fcgi_env->{$key} if $key =~ /\./;
53             }
54              
55 1         3 my($stdout, $stderr);
56 1         4 eval {
57 1         21 ($stdout, $stderr) = $conn->request($fcgi_env, $content_in);
58             };
59 1 50       1001837 if ($@) {
60 1         29 $env->{'psgi.errors'}->print($@);
61 1         156 return [ 502, ["Content-Type", "text/plain"], ["Bad FastCGI gateway"] ];
62             }
63              
64 0 0         print STDERR $stderr if $stderr;
65              
66 0 0         unless ( $stdout =~ /^HTTP/ ) {
67 0           $stdout = "HTTP/1.1 200 OK\015\012" . $stdout;
68             }
69              
70 0 0         ($stdout =~ /^(.+?\015?\012\015?\012)(.*)$/s) or die "No header/body separator";
71 0           my ($header_part, $content) = ($1, $2);
72              
73 0           my $res = HTTP::Response->parse($header_part);
74 0 0         if ($res->content) {
75             # we passed only headers but has body: Bad headers
76 0           return [ 500, [ 'Content-Type' => 'text/plain' ], [ 'Bad HTTP headers returned' ] ];
77             }
78              
79 0           my($status) = $res->remove_header('Status');
80 0   0       $status ||= "200";
81 0           $status =~ s/\s+.*$//; # remove ' OK' in '200 OK'
82              
83 0           my $headers = [
84             map {
85 0           my $k = $_;
86 0           map { ( $k => $_ ) } $res->headers->header($_);
  0            
87             } $res->headers->header_field_names
88             ];
89              
90 0           return [ $status, $headers, [ $content ] ];
91             }
92              
93             1;
94              
95             __END__