| 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__ |