File Coverage

blib/lib/FCGI/Client/Connection.pm
Criterion Covered Total %
statement 33 90 36.6
branch 0 26 0.0
condition 0 5 0.0
subroutine 11 18 61.1
pod 1 1 100.0
total 45 140 32.1


line stmt bran cond sub pod time code
1             package FCGI::Client::Connection;
2 1     1   5 use strict;
  1         1  
  1         20  
3 1     1   4 use warnings;
  1         1  
  1         17  
4 1     1   298 use FCGI::Client::Constant;
  1         2  
  1         3  
5 1     1   430 use Time::HiRes qw(time);
  1         1380  
  1         4  
6 1     1   157 use List::Util qw(max);
  1         2  
  1         88  
7 1     1   394 use POSIX qw(EAGAIN);
  1         4738  
  1         4  
8 1     1   1411 use FCGI::Client::Record;
  1         24  
  1         44  
9 1     1   569 use FCGI::Client::RecordFactory;
  1         3  
  1         39  
10              
11 1     1   8 use Types::Standard qw/Int/;
  1         3  
  1         8  
12              
13 1     1   629 use Moo;
  1         3  
  1         20  
14              
15             has sock => (
16             is => 'ro',
17             required => 1,
18             );
19              
20             has timeout => (
21             is => 'rw',
22             isa => Int,
23             default => 10,
24             );
25              
26 1     1   374 no Moo;
  1         2  
  1         4  
27              
28             sub request {
29 0     0 1   my ($self, $env, $content) = @_;
30 0           local $SIG{PIPE} = "IGNORE";
31             {
32 0     0     local $SIG{ALRM} = sub { Carp::confess('REQUEST_TIME_OUT') };
  0            
  0            
33 0   0       my $orig_alarm = alarm($self->timeout) || 0;
34 0           $self->_send_request($env, $content);
35 0           my @res = $self->_receive_response($self->sock);
36 0           alarm($orig_alarm);
37 0           return @res;
38             }
39             }
40              
41             sub _receive_response {
42 0     0     my ($self, $sock) = @_;
43 0           my ($stdout, $stderr);
44 0           while (my $res = $self->_read_record($self)) {
45 0           my $type = $res->type;
46 0 0         if ($type == FCGI_STDOUT) {
    0          
    0          
47 0           $stdout .= $res->content;
48             } elsif ($type == FCGI_STDERR) {
49 0           $stderr .= $res->content;
50             } elsif ($type == FCGI_END_REQUEST) {
51 0           my $appstatus = unpack('N', $res->content);
52 0           $sock->close();
53 0           return ($stdout, $stderr, $appstatus);
54             } else {
55 0           die "unknown response type: " . $res->type;
56             }
57             }
58 0           die 'connection breaked from server process?';
59             }
60             sub _send_request {
61 0     0     my ($self, $env, $content) = @_;
62 0           my $reqid = 1;
63 0           $self->sock->print(FCGI::Client::RecordFactory->create_request($reqid, $env, $content));
64             }
65              
66             sub _read_record {
67 0     0     my ($self) = @_;
68 0           my $header_raw = '';
69 0           while (length($header_raw) != FCGI_HEADER_LEN) {
70 0 0         $self->_read_timeout(\$header_raw, FCGI_HEADER_LEN-length($header_raw), length($header_raw)) or return;
71             }
72 0           my $header = FCGI::Client::RecordHeader->new(raw => $header_raw);
73 0           my $content_length = $header->content_length;
74 0           my $content = '';
75 0 0         if ($content_length != 0) {
76 0           while (length($content) != $content_length) {
77 0 0         $self->_read_timeout(\$content, $content_length-length($content), length($content)) or return;
78             }
79             }
80 0           my $padding_length = $header->padding_length;
81 0           my $padding = '';
82 0 0         if ($padding_length != 0) {
83 0           while (length($padding) != $padding_length) {
84 0 0         $self->_read_timeout(\$padding, $padding_length, 0) or return;
85             }
86             }
87 0           return FCGI::Client::Record->new(
88             header => $header,
89             content => $content,
90             );
91             }
92              
93             # returns 1 if socket is ready, undef on timeout
94             sub _wait_socket {
95 0     0     my ( $self, $sock, $is_write, $wait_until ) = @_;
96 0           do {
97 0           my $vec = '';
98 0           vec( $vec, $sock->fileno, 1 ) = 1;
99 0 0         if (
    0          
    0          
100             select(
101             $is_write ? undef : $vec,
102             $is_write ? $vec : undef,
103             undef,
104             max( $wait_until - time, 0 )
105             ) > 0
106             )
107             {
108 0           return 1;
109             }
110             } while ( time < $wait_until );
111 0           return;
112             }
113              
114             # returns (positive) number of bytes read, or undef if the socket is to be closed
115             sub _read_timeout {
116 0     0     my ( $self, $buf, $len, $off, ) = @_;
117 0           my $sock = $self->sock;
118 0           my $timeout = $self->timeout;
119 0           my $wait_until = time + $timeout;
120 0           while ( $self->_wait_socket( $sock, undef, $wait_until ) ) {
121 0 0 0       if ( my $ret = $sock->sysread( $$buf, $len, $off ) ) {
    0          
122 0           return $ret;
123             }
124             elsif ( !( !defined($ret) && $! == EAGAIN ) ) {
125 0           last;
126             }
127             }
128 0           return;
129             }
130              
131             1;
132             __END__