File Coverage

lib/Net/FastCGI/Protocol.pm
Criterion Covered Total %
statement 98 102 96.0
branch 52 56 92.8
condition 25 27 92.5
subroutine 8 8 100.0
pod 2 2 100.0
total 185 195 94.8


line stmt bran cond sub pod time code
1             package Net::FastCGI::Protocol;
2              
3 20     20   574934 use strict;
  20         50  
  20         875  
4 20     20   111 use warnings;
  20         38  
  20         729  
5              
6 20     20   263 use Carp qw[croak];
  20         36  
  20         1568  
7 20     20   7701 use Net::FastCGI qw[];
  20         50  
  20         452  
8 20     20   110 use Net::FastCGI::Constant qw[:type :common FCGI_KEEP_CONN];
  20         38  
  20         10249  
9              
10             BEGIN {
11 20     20   47 our $VERSION = '0.14';
12 20         196 our @EXPORT_OK = qw[ build_begin_request
13             build_begin_request_body
14             build_begin_request_record
15             build_end_request
16             build_end_request_body
17             build_end_request_record
18             build_header
19             build_params
20             build_record
21             build_stream
22             build_unknown_type_body
23             build_unknown_type_record
24             check_params
25             parse_begin_request_body
26             parse_end_request_body
27             parse_header
28             parse_params
29             parse_record
30             parse_record_body
31             parse_unknown_type_body
32             get_record_length
33             get_type_name
34             get_role_name
35             get_protocol_status_name
36             is_known_type
37             is_management_type
38             is_discrete_type
39             is_stream_type ];
40              
41 20         105 our %EXPORT_TAGS = ( all => \@EXPORT_OK );
42              
43 20   33     170 my $use_pp = $ENV{NET_FASTCGI_PP} || $ENV{NET_FASTCGI_PROTOCOL_PP};
44              
45 20 50       95 if (!$use_pp) {
46 0         0 eval {
47 0         0 require Net::FastCGI::Protocol::XS;
48             };
49 0         0 $use_pp = !!$@;
50             }
51              
52 20 50       75 if ($use_pp) {
53 20         16547 require Net::FastCGI::Protocol::PP;
54 20         6521 Net::FastCGI::Protocol::PP->import(@EXPORT_OK);
55             }
56             else {
57 0         0 Net::FastCGI::Protocol::XS->import(@EXPORT_OK);
58             }
59              
60             # shared between XS and PP implementation
61 20         100 push @EXPORT_OK, 'dump_record', 'dump_record_body';
62              
63 20         204 require Exporter;
64 20         45048 *import = \&Exporter::import;
65             }
66              
67             our $DUMP_RECORD_MAX = 78; # undocumented
68             our $DUMP_RECORD_ALIGN = !!0; # undocumented
69              
70             my %ESCAPES = (
71             "\a" => "\\a",
72             "\b" => "\\b",
73             "\t" => "\\t",
74             "\n" => "\\n",
75             "\f" => "\\f",
76             "\r" => "\\r",
77             );
78              
79             sub dump_record {
80 8 100 100 8 1 8094 goto \&dump_record_body if (@_ == 2 || @_ == 3); # deprecated
81 6 100       48 @_ == 1 || croak(q/Usage: dump_record(octets)/);
82              
83 5         16 my $len = &get_record_length;
84 5 100 100     53 ($len && $len <= length $_[0] && vec($_[0], 0, 8) == FCGI_VERSION_1)
      100        
85             || return '{Malformed FCGI_Record}';
86              
87 1         4 return dump_record_body(&parse_record);
88             }
89              
90             sub dump_record_body {
91 65 100 100 65 1 28303 @_ == 2 || @_ == 3 || croak(q/Usage: dump_record_body(type, request_id [, content])/);
92 63         886 my ($type, $request_id) = @_;
93              
94 63 100       161 my $content_length = defined $_[2] ? length $_[2] : 0;
95              
96 63 50       132 my $max = $DUMP_RECORD_MAX > 0 ? $DUMP_RECORD_MAX : FCGI_MAX_CONTENT_LEN;
97 63         92 my $out = '';
98              
99 63 100 100     1288 if ( $type == FCGI_PARAMS
    100 100        
    100 100        
      100        
100             || $type == FCGI_GET_VALUES
101             || $type == FCGI_GET_VALUES_RESULT) {
102 21 100       401 if ($content_length == 0) {
    100          
103 6         7 $out = q[""];
104             }
105             elsif (check_params($_[2])) {
106 12         17 my ($off, $klen, $vlen) = (0);
107 12         27 while ($off < $content_length) {
108 12         14 my $pos = $off;
109 12         21 for ($klen, $vlen) {
110 24         31 $_ = vec($_[2], $off, 8);
111 24 100       60 $_ = vec(substr($_[2], $off, 4), 0, 32) & 0x7FFF_FFFF
112             if $_ > 0x7F;
113 24 100       61 $off += $_ > 0x7F ? 4 : 1;
114             }
115              
116 12         32 my $head = substr($_[2], $pos, $off - $pos);
117 12         60 $head =~ s/(.)/sprintf('\\%.3o',ord($1))/egs;
  33         141  
118 12         19 $out .= $head;
119              
120 12         26 my $body = substr($_[2], $off, $klen + $vlen);
121 12         21 for ($body) {
122 12         20 s/([\\\"])/\\$1/g;
123 12         39 s/([\a\b\t\n\f\r])/$ESCAPES{$1}/g;
124 12         39 s/([^\x20-\x7E])/sprintf('\\x%.2X',ord($1))/eg;
  6         69  
125             }
126 12         19 $out .= $body;
127 12         18 $off += $klen + $vlen;
128 12 100       46 last if $off > $max;
129             }
130 12 100       190 substr($out, $max - 5) = ' ... '
131             if length $out > $max;
132 12         28 $out = qq["$out"];
133             }
134             else {
135 3         9 $out = 'Malformed FCGI_NameValuePair(s)';
136             }
137             }
138             elsif ( $type == FCGI_BEGIN_REQUEST
139             || $type == FCGI_END_REQUEST
140             || $type == FCGI_UNKNOWN_TYPE) {
141 30 100       85 if ($content_length != 8) {
    100          
    100          
142 9 100       36 my $name = $type == FCGI_BEGIN_REQUEST ? 'FCGI_BeginRequestBody'
    100          
143             : $type == FCGI_END_REQUEST ? 'FCGI_EndRequestBody'
144             : 'FCGI_UnknownTypeBody';
145 9         44 $out = sprintf '{Malformed %s (expected 8 octets got %d)}', $name, $content_length;
146             }
147             elsif ($type == FCGI_BEGIN_REQUEST) {
148 4         15 my ($role, $flags) = parse_begin_request_body($_[2]);
149 4 100       11 if ($flags != 0) {
150 3         5 my @set;
151 3 100       9 if ($flags & FCGI_KEEP_CONN) {
152 2         4 $flags &= ~FCGI_KEEP_CONN;
153 2         5 push @set, 'FCGI_KEEP_CONN';
154             }
155 3 100       7 if ($flags) {
156 2         10 push @set, sprintf '0x%.2X', $flags;
157             }
158 3         10 $flags = join '|', @set;
159             }
160 4         12 $out = sprintf '{%s, %s}', get_role_name($role), $flags;
161             }
162             elsif($type == FCGI_END_REQUEST) {
163 5         16 my ($astatus, $pstatus) = parse_end_request_body($_[2]);
164 5         16 $out = sprintf '{%d, %s}', $astatus, get_protocol_status_name($pstatus);
165             }
166             else {
167 12         40 my $unknown_type = parse_unknown_type_body($_[2]);
168 12         33 $out = sprintf '{%s}', get_type_name($unknown_type);
169             }
170             }
171             elsif ($content_length) {
172 6         8 my $looks_like_binary = do {
173 6         96 my $count = () = $_[2] =~ /[\r\n\t\x20-\x7E]/g;
174 6         33 ($count / $content_length) < 0.7;
175             };
176 6         19 $out = substr($_[2], 0, $max + 1);
177 6         13 for ($out) {
178 6 100       15 if ($looks_like_binary) {
179 3         21 s/(.)/sprintf('\\x%.2X',ord($1))/egs;
  24         447  
180             }
181             else {
182 3         6 s/([\\\"])/\\$1/g;
183 3         20 s/([\a\b\t\n\f\r])/$ESCAPES{$1}/g;
184 3         12 s/([^\x20-\x7E])/sprintf('\\x%.2X',ord($1))/eg;
  2         12  
185             }
186             }
187 6 100       21 substr($out, $max - 5) = ' ... '
188             if length $out > $max;
189 6         18 $out = qq["$out"];
190             }
191             else {
192 6         13 $out = q[""];
193             }
194              
195 63         186 my $name = get_type_name($type);
196 63         90 my $width = 0;
197 63 50       141 $width = 27 - length $name # length("FCGI_GET_VALUES_RESULT") == 22
198             if $DUMP_RECORD_ALIGN; # + length(0xFFFF) == 5
199 63         569 return sprintf '{%s, %*d, %s}', $name, $width, $request_id, $out;
200             }
201              
202             1;
203