File Coverage

blib/lib/Net/Inspect/L7/HTTP.pm
Criterion Covered Total %
statement 425 601 70.7
branch 255 464 54.9
condition 75 238 31.5
subroutine 24 35 68.5
pod 14 17 82.3
total 793 1355 58.5


line stmt bran cond sub pod time code
1             ############################################################################
2             # finds HTTP requests + responses in tcp connection
3             # chunked HTTP responses are supported
4             ############################################################################
5 1     1   814 use strict;
  1         4  
  1         36  
6 1     1   8 use warnings;
  1         3  
  1         54  
7             package Net::Inspect::L7::HTTP;
8 1     1   9 use base 'Net::Inspect::Flow';
  1         3  
  1         871  
9 1     1   594 use Net::Inspect::Debug qw(:DEFAULT $DEBUG %TRACE);
  1         4  
  1         6  
10 1     1   893 use Hash::Util 'lock_ref_keys';
  1         3814  
  1         9  
11 1     1   151 use Carp 'croak';
  1         3  
  1         71  
12 1     1   11 use Scalar::Util 'weaken';
  1         3  
  1         82  
13             use fields (
14 1         11 'replay', # collected and replayed in guess_protocol
15             'meta', # meta data from connection
16             'requests', # list of open requests, see _in0 for fields
17             'error', # connection has error like server sending data w/o request
18             'upgrade', # true if got upgrade, CONNECT, WebSockets..
19             'connid', # connection id
20             'lastreqid',# id of last request
21             'offset', # offset in data stream
22             'gap_upto', # up to which offset we could manage a gap, that is where we
23             # only get body data (no header, chunked info..).
24             # [off,off] similar to offset and off is set to -1 if umlimited
25             # (i.e. body ends with end of file)
26             'hdr_maxsz',# maximum header size for request(0), response(1) and
27             # chunk header(2). Defaults to 64k, 16k and 2k.
28 1     1   9 );
  1         3  
29              
30 1     1   331 use Exporter 'import';
  1         6  
  1         245  
31             our (@EXPORT_OK,%EXPORT_TAGS);
32             {
33             %EXPORT_TAGS = (
34             need_body => [qw(
35             METHODS_WITHOUT_RQBODY METHODS_WITH_RQBODY METHODS_WITHOUT_RPBODY
36             CODE_WITHOUT_RPBODY
37             )]
38             );
39             push @EXPORT_OK,@$_ for (values %EXPORT_TAGS);
40             push @EXPORT_OK,'parse_hdrfields','parse_reqhdr','parse_rsphdr';
41             }
42              
43             use constant {
44 1         175 METHODS_WITHOUT_RQBODY => [qw(GET HEAD DELETE CONNECT)],
45             METHODS_WITH_RQBODY => [qw(POST PUT)],
46             METHODS_WITHOUT_RPBODY => [qw(HEAD CONNECT)],
47             CODE_WITHOUT_RPBODY => [100..199, 204, 205, 304],
48 1     1   15 };
  1         3  
49              
50             use constant {
51 1         10629 RQHDR_DONE => 0b00001,
52             RQBDY_DONE => 0b00010,
53             RQ_ERROR => 0b00100,
54             RPHDR_DONE => 0b01000,
55             RPBDY_DONE_ON_EOF => 0b10000,
56 1     1   9 };
  1         3  
57              
58             # rfc2616, 2.2
59             # token = 1*
60             # separators = "(" | ")" | "<" | ">" | "@"
61             # | "," | ";" | ":" | "\" | <">
62             # | "/" | "[" | "]" | "?" | "="
63             # | "{" | "}" | SP | HT
64              
65             my $separator = qr{[()<>@,;:\\"/\[\]?={} \t]};
66             my $token = qr{[^()<>@,;:\\"/\[\]?={}\x00-\x20\x7f]+};
67             my $token_value_cont = qr{
68             ($token): # key:
69             [\040\t]*([^\r\n]*?)[\040\t]* # value
70             ((?:\r?\n[\040\t][^\r\n]*)*) # continuation lines
71             \r?\n # (CR)LF
72             }x;
73              
74             # common error: "Last Modified" instead of "Last-Modified"
75             # squid seems to just strip invalid headers, try the same
76             my $xtoken = qr{[^()<>@,;:\\"/\[\]?={}\x00-\x20\x7f][^:[:^print:]]*};
77              
78             my %METHODS_WITHOUT_RQBODY = map { ($_,1) } @{METHODS_WITHOUT_RQBODY()};
79             my %METHODS_WITH_RQBODY = map { ($_,1) } @{METHODS_WITH_RQBODY()};
80             my %METHODS_WITHOUT_RPBODY = map { ($_,1) } @{METHODS_WITHOUT_RPBODY()};
81             my %CODE_WITHOUT_RPBODY = map { ($_,1) } @{CODE_WITHOUT_RPBODY()};
82              
83             sub guess_protocol {
84 0     0 1 0 my ($self,$guess,$dir,$data,$eof,$time,$meta) = @_;
85              
86 0 0       0 if ( $dir == 0 ) {
87 0   0     0 my $rp = $self->{replay} ||= [];
88 0         0 push @$rp,[$data,$eof,$time];
89 0         0 my $buf = join('',map { $_->[0] } @$rp);
  0         0  
90 0 0 0     0 if ( $buf =~m{
    0          
91             \A[\r\n]* # initial junk
92             [A-Z]{2,20}[\040\t]{1,3} # method
93             \S+[\040\t]{1,3} # path/URI
94             HTTP/1\.[01][\040\t]{0,3} # version
95             \r?\n # (CR)LF
96             (?:$xtoken:.*\r?\n(?:[\t\040].*\r?\n)* )* # field:..+cont
97             \r?\n # empty line
98             }xi) {
99             # looks like HTTP request
100 0         0 my $obj = $self->new_connection($meta);
101             # replay as one piece
102 0         0 my $n = $obj->in(0,$buf,$rp->[-1][1],$rp->[-1][2]);
103 0         0 undef $self->{replay};
104 0         0 $n += -length($buf) + length($data);
105 0 0       0 $n<=0 and die "object $obj did not consume alle replayed bytes";
106 0         0 debug("consumed $n of ".length($data)." bytes");
107 0         0 return ($obj,$n);
108              
109             } elsif ( $buf =~m{[^\n]\r?\n\r?\n}
110             or length($buf)>2**16 ) {
111             # does not look like a HTTP header for me
112 0         0 debug("does not look like HTTP header: $buf");
113 0         0 $guess->detach($self);
114             } else {
115 0         0 debug("need more data to decide if HTTP");
116 0         0 return;
117             }
118             } else {
119             # data from server but no request header from
120             # client yet - cannot be HTTP
121 0         0 debug("got data from server before getting request from client -> no HTTP");
122 0         0 $guess->detach($self);
123             }
124 0         0 return;
125             }
126              
127              
128             {
129             my $connid = 0;
130 0     0 0 0 sub syn { 1 }; # in case it is attached to Net::Inspect::Tcp
131             sub new_connection {
132 14     14 1 105 my ($self,$meta,%args) = @_;
133 14         44 my $obj = $self->new;
134 14         35 $obj->{meta} = $meta;
135 14         29 $obj->{requests} = [];
136 14         31 $obj->{connid} = ++$connid;
137 14         27 $obj->{lastreqid} = 0;
138 14         38 $obj->{offset} = [0,0];
139 14         34 $obj->{gap_upto} = [0,0];
140 14         30 $obj->{hdr_maxsz} = delete $args{header_maxsize};
141 14   50     78 $obj->{hdr_maxsz}[0] ||= 2**16;
142 14   50     68 $obj->{hdr_maxsz}[1] ||= 2**14;
143 14   50     62 $obj->{hdr_maxsz}[2] ||= 2**11;
144              
145 14         42 return $obj;
146             }
147             }
148              
149             sub in {
150 92     92 1 11042 my ($self,$dir,$data,$eof,$time) = @_;
151 92 0 0     226 $DEBUG && $self->xdebug("got %s bytes from %d, eof=%d",
    50          
152             ref($data) ? join(":",@$data): length($data),
153             $dir,$eof//0
154             );
155 92 100       263 my $bytes = $dir == 0
156             ? _in0($self,$data,$eof,$time)
157             : _in1($self,$data,$eof,$time);
158             #$self->dump_state if $DEBUG;
159 92         252 return $bytes;
160             }
161              
162             sub offset {
163 0     0 1 0 my $self = shift;
164 0 0       0 return @{ $self->{offset} }[wantarray ? @_:$_[0]];
  0         0  
165             }
166              
167             sub gap_diff {
168 29     29 1 2637 my $self = shift;
169 29         46 my @rv;
170 29         58 for(@_) {
171 58         102 my $off = $self->{gap_upto}[$_];
172             push @rv,
173             $off == -1 ? -1 :
174 58 100       167 ($off-=$self->{offset}[$_]) > 0 ? $off :
    100          
175             0;
176             }
177 29 50       94 return wantarray ? @rv : $rv[0];
178             }
179              
180             sub set_gap_diff {
181 2     2 1 4 my ($self,$dir,$diff) = @_;
182             $self->{gap_upto}[$dir] = defined($diff)
183 2 50       8 ? $self->{offset}[$dir] + $diff # add to offset
184             : 0; # reset gap_upto
185             }
186              
187             sub gap_offset {
188 16     16 1 879 my $self = shift;
189 16         23 my @rv;
190 16         34 for(@_) {
191 32         51 my $off = $self->{gap_upto}[$_];
192             push @rv,
193             $off == -1 ? -1 :
194 32 100       95 $off > $self->{offset}[$_] ? $off :
    100          
195             0
196             }
197 16 50       51 return wantarray ? @rv : $rv[0];
198             }
199              
200             # give requests a chance to cleanup before destroying connection
201             sub DESTROY {
202 14     14   7409 my $self = shift;
203 14         28 @{$self->{requests}} = ();
  14         159  
204             }
205              
206              
207             # process request data
208             sub _in0 {
209 55     55   113 my ($self,$data,$eof,$time) = @_;
210 55         95 my $bytes = 0; # processed bytes
211 55         118 my $rqs = $self->{requests};
212              
213 55 100       134 if ( ref($data)) {
214             # process gap in request data
215 7 50       21 croak "unknown type $data->[0]" if $data->[0] ne 'gap';
216 7         11 my $len = $data->[1];
217              
218 7 50       17 croak 'existing error in connection' if $self->{error};
219              
220 7         13 my $rqs = $self->{requests};
221             croak 'no open request' if ! @$rqs or
222 7 50 66     50 $rqs->[0]{state} & RQBDY_DONE && ! $self->{upgrade};
      33        
223 7 50       21 croak 'existing error in request' if $rqs->[0]{state} & RQ_ERROR;
224             croak "gap too large" if $self->{gap_upto}[0]>=0
225 7 50 33     37 && $self->{gap_upto}[0] < $self->{offset}[0] + $len;
226              
227 7 100       18 if (defined $rqs->[0]{rqclen}) {
228 2         5 $rqs->[0]{rqclen} -= $len;
229 2 50 33     12 if ( ! $rqs->[0]{rqclen} && ! $rqs->[0]{rqchunked} ) {
230 2         3 $rqs->[0]{state} |= RQBDY_DONE;
231             }
232             }
233              
234 7         13 $self->{offset}[0] += $len;
235 7         11 my $obj = $rqs->[0]{obj};
236 7 100       20 if ($self->{upgrade}) {
    50          
237 5         18 $self->{upgrade}(0,[ gap => $len ],$eof,$time);
238             } elsif ($obj) {
239             $obj->in_request_body(
240             [ gap => $len ],
241 2   33     15 $eof || ($rqs->[0]{state} & RQBDY_DONE ? 1:0),
242             $time
243             );
244             }
245 7         19 return $len;
246             }
247              
248             READ_DATA:
249              
250 85 50       215 if ($self->{error}) {
251 0 0       0 $DEBUG && $self->xdebug("no more data because of server side error");
252 0         0 return $bytes;
253             }
254              
255 85 100       205 if ($self->{upgrade}) {
256 10         19 $self->{offset}[0] += length($data);
257 10         33 $self->{upgrade}(0,$data,$eof,$time);
258 10         31 return $bytes + length($data);
259             }
260              
261 75 50 66     318 if (@$rqs and $rqs->[0]{state} & RQ_ERROR ) {
262             # error reading request
263 0 0       0 $DEBUG && $self->xdebug("no more data because of client side error");
264 0         0 return $bytes;
265             }
266              
267 75 50 100     422 if ( ( ! @$rqs or $rqs->[0]{state} & RQBDY_DONE )
      66        
268             and $data =~m{\A[\r\n]+}g ) {
269             # first request or previous request body done
270             # new request might follow but maybe we only have trailing lines after
271             # the last request: eat empty lines
272 0         0 my $n = pos($data);
273 0         0 $bytes += $n;
274 0         0 $self->{offset}[0] += $n;
275 0         0 substr($data,0,$n,'');
276 0 0       0 %TRACE && $self->xtrace("eat empty lines before request header");
277             }
278              
279 75 100       218 if ( $data eq '' ) {
280 28 50       71 $DEBUG && $self->xdebug("no data, eof=$eof, bytes=$bytes");
281 28 50       97 return $bytes if ! $eof; # need more data
282              
283             # handle EOF
284             # check if we got request body for last request
285 0 0 0     0 if ( @$rqs and not $rqs->[0]{state} & RQBDY_DONE ) {
286             # request body not done yet
287 0 0 0     0 %TRACE && ($rqs->[0]{obj}||$self)->xtrace("request body not done but eof");
288 0   0     0 ($rqs->[0]{obj}||$self)->fatal('eof but request body not done',0,$time);
289 0         0 $rqs->[0]{state} |= RQ_ERROR;
290 0         0 return $bytes;
291             }
292              
293 0         0 return $bytes; # request body done
294             }
295              
296             # create new request if no open request or last open request has the
297             # request body already done (pipelining)
298 47 100 100     182 if ( ! @$rqs or $rqs->[0]{state} & RQBDY_DONE ) {
299 17         38 my $reqid = ++$self->{lastreqid};
300             my $obj = $self->new_request({
301 17         27 %{$self->{meta}},
  17         85  
302             time => $time,
303             reqid => $reqid,
304             });
305 17         134 my $rq = {
306             obj => $obj,
307             # bitmask what is done: rpbody|rphdr|rqerror|rqbody|rqhdr
308             state => 0,
309             rqclen => undef, # open content-length request
310             rpclen => undef, # open content-length response
311             # chunked mode for request|response:
312             # false - no chunking
313             # 1,r[qp]clen == 0 - next will be chunk size
314             # 1,r[qp]clen > 0 - inside chunk data, need *clen
315             # 2 - next will be chunk
316             # 3 - after last chunk, next will be chunk trailer
317             rqchunked => undef, # chunked mode for request
318             rpchunked => undef, # chunked mode for response
319             request => undef, # result from parse_reqhdr
320             };
321              
322 17 50       48 if ($DEBUG) {
323 0         0 $rq->{reqid} = $reqid;
324 0         0 weaken($rq->{conn} = $self);
325 0         0 bless $rq, 'Net::Inspect::L7::HTTP::_DebugRequest';
326 0         0 $rq->xdebug("create new request");
327             }
328 17         57 lock_ref_keys($rq);
329 17         191 unshift @$rqs, $rq;
330             }
331              
332 47         83 my $rq = $rqs->[0];
333 47         81 my $obj = $rq->{obj};
334              
335             # read request header if not done
336 47 100       117 if ( not $rq->{state} & RQHDR_DONE ) {
337             # no request header yet, check if data contains it
338              
339             # leading newlines at beginning of request are legally ignored junk
340 20 50       74 if ( $data =~s{\A([\r\n]+)}{} ) {
341 0   0     0 ($obj||$self)->in_junk(0,$1,0,$time);
342             }
343              
344 20 50       58 $DEBUG && $rq->xdebug("need to read request header");
345 20 100       120 if ($data =~s{\A(\A.*?\n\r?\n)}{}s) {
    50          
    50          
    50          
346 17 50       53 $DEBUG && $rq->xdebug("got request header");
347 17         56 my $hdr = $1;
348 17         33 my $n = length($hdr);
349 17         37 $self->{offset}[0] += $n;
350 17         32 $bytes += $n;
351 17         30 $rq->{state} |= RQHDR_DONE; # rqhdr done
352              
353 17         34 my (%hdr,@warn);
354 17         49 my $err = parse_reqhdr($hdr,\%hdr,0);
355 17 50 66     70 if ($err and my $sub = $obj->can('fix_reqhdr')) {
356 0         0 $hdr = $sub->($obj,$hdr);
357 0         0 $err = parse_reqhdr($hdr,\%hdr,0);
358             }
359              
360 17 100       45 if ($err) {
361 1   33     16 ($obj||$self)->fatal($err,0,$time);
362 1         8 $rq->{state} |= RQ_ERROR;
363 1         7 return $bytes;
364             }
365              
366 16         28 my $body_done;
367 16 100       46 if ($hdr{chunked}) {
    100          
368 4         9 $rq->{rqchunked} = 1;
369             } elsif ($hdr{content_length}) {
370 3         6 $rq->{rqclen} = $hdr{content_length};
371 3         10 $self->{gap_upto}[0]= $self->{offset}[0] + $hdr{content_length};
372             } else {
373 9         17 $body_done = 1;
374             }
375              
376 16         31 $rq->{request} = \%hdr;
377              
378 16 0 0     47 %TRACE && $hdr{junk} && ($obj||$self)->xtrace(
      33        
379             "invalid request header data: $hdr{junk}");
380              
381 16 50       81 $obj && $obj->in_request_header($hdr,$time,\%hdr);
382              
383 16 100       99 if ($body_done) {
384 9 50       24 $DEBUG && $rq->xdebug("request done (no body)");
385 9         22 $rq->{state} |= RQBDY_DONE;
386 9 100 66     39 if ($hdr{method} eq 'CONNECT' || $hdr{upgrade}) {
387             # Don't propagate an empty request body:
388             # with CONNECT there will be no body and with Upgrade we
389             # will have no body if the upgrade succeeded. If it failed
390             # we submit the body later.
391             } else {
392 7 50       29 $obj && $obj->in_request_body('',1,$time);
393             }
394             }
395              
396             } elsif ($data =~m{[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]}) {
397             # junk data, maybe attempt to use SOCKS instead of proxy request
398 0   0     0 ($obj||$self)->fatal( sprintf(
399             "junk data instead of request header '%s...'",
400             substr($data,0,10)),0,$time);
401 0         0 $rq->{state} |= RQ_ERROR;
402 0         0 return $bytes;
403             } elsif ( length($data) > $self->{hdr_maxsz}[0] ) {
404 0   0     0 ($obj||$self)->fatal('request header too big',0,$time);
405 0         0 $rq->{state} |= RQ_ERROR;
406 0         0 return $bytes;
407             } elsif ( $eof ) {
408 0   0     0 ($obj||$self)->fatal('eof in request header',0,$time);
409 0         0 $rq->{state} |= RQ_ERROR;
410 0         0 return $bytes;
411             } else {
412             # will be called on new data from upper flow
413 3 50       9 $DEBUG && $rq->xdebug("need more bytes for request header");
414 3         7 return $bytes;
415             }
416             }
417              
418             # read request body if not done
419 43 100 66     182 if ( $data ne '' and not $rq->{state} & RQBDY_DONE ) {
420             # request body
421 27 100       62 if ( my $want = $rq->{rqclen} ) {
422 10         20 my $l = length($data);
423 10 100       26 if ( $l>=$want) {
424             # got all request body
425 8 50       21 $DEBUG && $rq->xdebug("need $want bytes, got all");
426 8         25 my $body = substr($data,0,$rq->{rqclen},'');
427 8         18 $self->{offset}[0] += $rq->{rqclen};
428 8         23 $bytes += $rq->{rqclen};
429 8         16 $rq->{rqclen} = 0;
430 8 100       18 if ( ! $rq->{rqchunked} ) {
431 1 50       5 $DEBUG && $rq->xdebug("request done (full clen)");
432 1         4 $rq->{state} |= RQBDY_DONE; # req body done
433 1 50       8 $obj && $obj->in_request_body($body,1,$time)
434             } else {
435 7 50       31 $obj && $obj->in_request_body($body,$eof,$time);
436 7         34 $rq->{rqchunked} = 2; # get CRLF after chunk
437             }
438             } else {
439             # only part
440 2 50       48 $DEBUG && $rq->xdebug("need $want bytes, got only $l");
441 2         7 my $body = substr($data,0,$l,'');
442 2         5 $self->{offset}[0] += $l;
443 2         4 $bytes += $l;
444 2         5 $rq->{rqclen} -= $l;
445 2 50       8 $obj && $obj->in_request_body($body,0,$time);
446             }
447              
448             # Chunking: rfc2616, 3.6.1
449             } else {
450             # [2] must get CRLF after chunk
451 17 100       45 if ( $rq->{rqchunked} == 2 ) {
452 7 50       28 $DEBUG && $rq->xdebug("want CRLF after chunk");
453 7 50       38 if ( $data =~m{\A\r?\n}g ) {
    0          
454 7         14 my $n = pos($data);
455 7         13 $self->{offset}[0] += $n;
456 7         13 $bytes += $n;
457 7         15 substr($data,0,$n,'');
458 7         14 $rq->{rqchunked} = 1; # get next chunk header
459 7 50       19 $DEBUG && $rq->xdebug("got CRLF after chunk");
460             } elsif ( length($data)>=2 ) {
461 0   0     0 ($obj||$self)->fatal("no CRLF after chunk",0,$time);
462 0         0 $self->{error} = 1;
463 0         0 return $bytes;
464             } else {
465             # need more
466 0         0 return $bytes;
467             }
468             }
469              
470             # [1] must read chunk header
471 17 50       46 if ( $rq->{rqchunked} == 1 ) {
472 17 50       39 $DEBUG && $rq->xdebug("want chunk header");
473 17 100 33     105 if ( $data =~m{\A([\da-fA-F]+)[ \t]*(?:;.*)?\r?\n}g ) {
    50          
474 11         32 $rq->{rqclen} = hex($1);
475 11         34 my $chdr = substr($data,0,pos($data),'');
476 11         22 $self->{offset}[0] += length($chdr);
477 11         19 $bytes += length($chdr);
478              
479             $self->{gap_upto}[0] = $self->{offset}[0] + $rq->{rqclen}
480 11 100       33 if $rq->{rqclen};
481              
482 11 50       45 $obj->in_chunk_header(0,$chdr,$time) if $obj;
483 11 50       67 $DEBUG && $rq->xdebug(
484             "got chunk header - want $rq->{rqclen} bytes");
485 11 100       30 if ( ! $rq->{rqclen} ) {
486             # last chunk
487 4         9 $rq->{rqchunked} = 3;
488 4 50       16 $obj && $obj->in_request_body('',1,$time);
489             }
490             } elsif ( $data =~m{\n} or length($data)>8192 ) {
491 0   0     0 ($obj||$self)->fatal("invalid chunk header",0,$time);
492 0         0 $self->{error} = 1;
493 0         0 return $bytes;
494             } else {
495             # need more data
496 6         18 return $bytes;
497             }
498             }
499              
500             # [3] must read chunk trailer
501 11 100       39 if ( $rq->{rqchunked} == 3 ) {
502 4 50       11 $DEBUG && $rq->xdebug("want chunk trailer");
503 4 50 0     19 if ( $data =~m{\A
    0          
    0          
504             (?:\w[\w\-]*:.*\r?\n(?:[\t\040].*\r?\n)* )* # field:..+cont
505             \r?\n
506             }xg) {
507 4 50       13 $DEBUG && $rq->xdebug("request done (chunk trailer)");
508 4         11 my $trailer = substr($data,0,pos($data),'');
509 4         9 $self->{offset}[0] += length($trailer);
510 4         8 $bytes += length($trailer);
511 4 50       23 $obj->in_chunk_trailer(0,$trailer,$time) if $obj;
512 4         24 $rq->{state} |= RQBDY_DONE; # request done
513             } elsif ( $data =~m{\n\r?\n}
514             or length($data) > $self->{hdr_maxsz}[2] ) {
515 0   0     0 ($obj||$self)->fatal("invalid chunk trailer",0,$time);
516 0         0 $self->{error} = 1;
517 0         0 return $bytes;
518             } elsif ( $eof ) {
519             # not fatal, because we got all data
520 0 0 0     0 %TRACE && ($obj||$self)->xtrace(
521             "eof before end of chunk trailer");
522 0         0 $self->{error} = 1;
523 0         0 return $bytes;
524             } else {
525             # need more
526 0 0       0 $DEBUG && $rq->xdebug("need more bytes for chunk trailer");
527 0         0 return $bytes
528             }
529             }
530             }
531             }
532              
533 37         91 goto READ_DATA;
534             }
535              
536              
537              
538             # process response data
539             sub _in1 {
540 37     37   81 my ($self,$data,$eof,$time) = @_;
541              
542 37         72 my $rqs = $self->{requests};
543 37         58 my $bytes = 0; # processed bytes
544              
545 37 100       92 if ( ref($data)) {
546             # process gap in response data
547 2 50       6 croak "unknown type $data->[0]" if $data->[0] ne 'gap';
548 2         4 my $len = $data->[1];
549              
550 2 50       7 croak 'existing error in connection' if $self->{error};
551              
552 2         3 my $rqs = $self->{requests};
553 2 50       5 croak 'no open response' if ! @$rqs;
554 2         5 my $rq = $rqs->[-1];
555 2 50       6 croak 'existing error in request' if $rq->{state} & RQ_ERROR;
556             croak "gap too large" if $self->{gap_upto}[1]>=0
557 2 50 33     15 && $self->{gap_upto}[1] < $self->{offset}[1] + $len;
558              
559 2 50       7 $rq->{rpclen} -= $len if defined $rq->{rpclen};
560 2         10 $self->{offset}[1] += $len;
561              
562 2         3 my $obj = $rq->{obj};
563 2 50 33     15 if ($self->{upgrade}) {
    50 33        
564 0         0 $self->{upgrade}(1,[ gap => $len ],$eof,$time);
565             } elsif ($rq->{rpclen}
566             or !defined $rq->{rpclen}
567             or $rq->{rpchunked}) {
568 0 0       0 $obj && $obj->in_response_body([ gap => $len ],$eof,$time);
569             } else {
570             # done with request
571 2 50       5 $DEBUG && $rq->xdebug("response done (last gap)");
572 2         4 pop(@$rqs);
573 2 50       10 $obj && $obj->in_response_body([ gap => $len ],1,$time);
574             }
575 2         15 return $len;
576             }
577              
578              
579             READ_DATA:
580              
581 60 50       146 return $bytes if $self->{error};
582 60 100 100     234 return $bytes if $data eq '' && !$eof;
583              
584 40 100       97 if ($self->{upgrade}) {
585 5         14 $self->{offset}[1] += length($data);
586 5         15 $self->{upgrade}(1,$data,$eof,$time);
587 5         15 return $bytes + length($data);
588             }
589              
590 35 100       81 if ( $data eq '' ) {
591 2 50       5 $DEBUG && $self->xdebug("no more data, eof=$eof bytes=$bytes");
592              
593             # handle EOF
594             # check if we got response body for last request
595 2 50 33     11 if ( @$rqs && $rqs->[-1]{state} & RPBDY_DONE_ON_EOF ) {
    0          
596             # response body done on eof
597 2         3 my $rq = pop(@$rqs);
598 2 50       5 $DEBUG && $rq->xdebug("response done (eof)");
599 2 50       8 $rq->{obj}->in_response_body('',1,$time) if $rq->{obj};
600              
601             } elsif ( @$rqs ) {
602             # response body not done yet
603 0         0 my $rq = pop(@$rqs);
604 0 0       0 $DEBUG && $rq->xdebug("response done (unexpected eof)");
605 0 0       0 if (($rq->{state} & RPHDR_DONE) == 0) {
606 0 0 0     0 if ($data eq '' and $self->{lastreqid}>1) {
    0          
607             # We had already a request and now the server closes while
608             # the client is still sending a new request. This happens
609             # with keep-alive connections and the client needs to handle
610             # this case with retrying the request. Signal this issue by
611             # calling in_request_header with empty header.
612 0   0     0 ($rq->{obj}||$self)->in_request_header('',$time);
613             } elsif ($data eq '') {
614 0   0     0 ($rq->{obj}||$self)->fatal(
615             'eof before receiving first response', 1,$time);
616             } else {
617 0 0 0     0 %TRACE && ($rq->{obj}||$self)->xtrace(
618             "eof within response header: '$data'");
619 0   0     0 ($rq->{obj}||$self)->fatal(
620             'eof within response header', 1,$time);
621             }
622             } else {
623             # eof inside a response or close for first request already.
624 0 0 0     0 %TRACE && ($rq->{obj}||$self)->xtrace("eof within response body");
625 0   0     0 ($rq->{obj}||$self)->fatal('eof within response body', 1,$time);
626             }
627             }
628              
629 2         22 return $bytes; # done
630             }
631              
632 33 50       78 if ( ! @$rqs ) {
633 0 0       0 if ( $data =~s{\A([\r\n]+)}{} ) {
634             # skip newlines after request because newlines at beginning of
635             # new request are allowed, stupid
636 0         0 $bytes += length($1);
637 0         0 goto READ_DATA;
638             }
639              
640 0         0 $self->fatal('data from server w/o request',1,$time);
641 0         0 $self->{error} = 1;
642 0         0 return $bytes;
643             }
644              
645 33         56 my $rq = $rqs->[-1];
646 33         55 my $obj = $rq->{obj};
647              
648             # read response header if not done
649 33 100       84 if ( not $rq->{state} & RPHDR_DONE ) {
650 15 50       37 $DEBUG && $rq->xdebug("response header not read yet");
651              
652             # leading newlines at beginning of response are legally ignored junk
653 15 50       58 if ( $data =~s{\A([\r\n]+)}{} ) {
654 0   0     0 ($obj||$self)->in_junk(1,$1,0,$time);
655             }
656              
657             # no response header yet, check if data contains it
658 15 100       93 if ( $data =~s{\A(.*?\n\r?\n)}{}s ) {
    50          
    50          
    50          
    50          
659 14         38 my $hdr = $1;
660 14         32 my $n = length($hdr);
661 14         24 $bytes += $n;
662 14         32 $self->{offset}[1] += $n;
663              
664 14         28 my %hdr;
665 14         47 my $err = parse_rsphdr($hdr,$rq->{request},\%hdr);
666 14 50 66     55 if ($err and my $sub = $obj->can('fix_rsphdr')) {
667 0         0 $hdr = $sub->($obj,$hdr);
668 0         0 $err = parse_rsphdr($hdr,$rq->{request},\%hdr);
669             }
670              
671 14 100       36 goto error if $err;
672 13 50       36 $DEBUG && $rq->xdebug("got response header");
673              
674 13 0 0     36 %TRACE && $hdr{junk} && ($obj||$self)->xtrace(
      33        
675             "invalid request header data: $hdr{junk}");
676              
677 13 100       33 if ($hdr{preliminary}) {
678             # Preliminary response. Wait for read real response.
679 1 50       6 $obj && $obj->in_response_header($hdr,$time,\%hdr);
680 1         32 goto READ_DATA;
681             }
682              
683 12         25 $rq->{state} |= RPHDR_DONE; # response header done
684              
685 12 100       40 if ($hdr{upgrade}) {
    50          
686             # Reset length to undef since we need to read until eof.
687 2         5 $rq->{rpclen} = undef;
688              
689             # If no object is given we just use a dummy function which
690             # returns the size of the data.
691             # If object has its own upgrade_XXXX method for the protocol we
692             # use this. Support for CONNECT has been traditionally built in
693             # but can be redefined with upgrade_CONNECT.
694              
695 2 50       40 if (!$obj) {
    100          
    50          
    50          
696 0     0   0 $self->{upgrade} = sub {};
697 0         0 @{$self->{gap_upto}} = (-1,-1);
  0         0  
698              
699             } elsif (my $sub = $obj->can('upgrade_'.$hdr{upgrade})) {
700             # $sub might throw an error if it is unwilling to upgrade
701             # the connection based on request and response.
702 1 50       3 unless ($self->{upgrade} = eval {
703 1         6 $sub->($obj,$self,$rq->{request},\%hdr)
704             }) {
705 0         0 $err = "invalid connection upgrade '$hdr{upgrade}': $@";
706 0         0 goto error;
707             }
708              
709             } elsif ($sub = $obj->can('upgrade_ANY')) {
710             # $sub might throw an error if it is unwilling to upgrade
711             # the connection based on request and response.
712 0 0       0 unless ($self->{upgrade} = eval {
713             $sub->($obj,$self,$rq->{request},\%hdr,$hdr{upgrade})
714 0         0 }) {
715 0         0 $err = "invalid connection upgrade '$hdr{upgrade}': $@";
716 0         0 goto error;
717             }
718              
719             } elsif ($hdr{upgrade} eq 'CONNECT') {
720             # Traditionally just calls in_data. If this is not available
721             # call dummy function.
722             $self->{upgrade} = $obj->can('in_data') && do {
723             weaken(my $wobj = $obj);
724 2     2   57 sub { $wobj->in_data(@_) }
725 1   50 0   8 } || sub {};
726 1         4 @{$self->{gap_upto}} = (-1,-1);
  1         4  
727              
728             } else {
729 0         0 $err = "unsupported connection upgrade '$hdr{upgrade}'";
730 0         0 goto error;
731             }
732              
733 2         28 goto done;
734             } elsif ($rq->{request}{upgrade}) {
735             # The client requested an upgrade which the server did not ack.
736             # Thus propagate the empty request body now.
737 0 0       0 $obj && $obj->in_request_body('',1,$time);
738             }
739              
740 10         17 my $body_done;
741 10 100       33 if ($hdr{chunked}) {
    100          
742 2         4 $rq->{rpchunked} = 1;
743             } elsif (defined $hdr{content_length}) {
744 6 100       18 if (($rq->{rpclen} = $hdr{content_length})) {
745             # content_length > 0, can do gaps
746             $self->{gap_upto}[1]= $self->{offset}[1]
747 3         9 + $hdr{content_length};
748             } else {
749 3         7 $body_done = 1;
750             }
751             } else {
752             # no length given but method supports body -> end with eof
753 2         3 $rq->{state} |= RPBDY_DONE_ON_EOF; # body done when eof
754 2         6 $self->{gap_upto}[1] = -1;
755             }
756              
757 12 50       64 done:
758             $obj && $obj->in_response_header($hdr,$time,\%hdr);
759 12 100       63 if ($body_done) {
760 3 50       11 $DEBUG && $rq->xdebug("response done (no body)");
761 3         7 pop(@$rqs);
762 3 50       15 $obj && $obj->in_response_body('',1,$time);
763             }
764 12         324 goto READ_DATA;
765              
766             error:
767 1         3 $self->{error} = 1;
768 1   33     8 ($obj||$self)->fatal($err,1,$time);
769 1         9 return $bytes;
770              
771             } elsif ($data =~m{[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]}) {
772 0   0     0 ($obj||$self)->fatal( sprintf(
773             "junk data instead of response header '%s...'",
774             substr($data,0,10)) ,1,$time);
775 0         0 $self->{error} = 1;
776 0         0 return $bytes;
777             } elsif ( $data =~m{[^\n]\r?\n\r?\n}g ) {
778 0   0     0 ($obj||$self)->fatal( sprintf("invalid response header syntax '%s'",
779             substr($data,0,pos($data))),1,$time);
780 0         0 $self->{error} = 1;
781 0         0 return $bytes;
782             } elsif ( length($data) > $self->{hdr_maxsz}[1] ) {
783 0   0     0 ($obj||$self)->fatal('response header too big',1,$time);
784 0         0 $self->{error} = 1;
785 0         0 return $bytes;
786             } elsif ( $eof ) {
787 0   0     0 ($obj||$self)->fatal('eof in response header',1,$time);
788 0         0 $self->{error} = 1;
789 0         0 return $bytes;
790             } else {
791             # will be called on new data from upper flow
792 1 50       4 $DEBUG && $rq->xdebug("need more data for response header");
793 1         3 return $bytes;
794             }
795             }
796              
797             # read response body
798 18 50       44 if ( $data ne '' ) {
799             # response body
800 18 50       39 $DEBUG && $rq->xdebug("response body data");
801              
802             # have content-length or within chunk
803 18 100       53 if ( my $want = $rq->{rpclen} ) {
    100          
    50          
804             # called for content-length or to read content from chunk
805             # with known length
806 6         11 my $l = length($data);
807 6 100       15 if ( $l >= $want ) {
808 5 50       12 $DEBUG && $rq->xdebug("need $want bytes, got all($l)");
809             # got all response body
810 5         13 my $body = substr($data,0,$want,'');
811 5         11 $self->{offset}[1] += $want;
812 5         10 $bytes += $want;
813 5         10 $rq->{rpclen} = 0;
814 5 100       11 if ( ! $rq->{rpchunked} ) {
815             # response done
816 1         2 pop(@$rqs);
817 1 50       4 $DEBUG && $rq->xdebug("response done (full clen received)");
818 1 50       6 $obj && $obj->in_response_body($body,1,$time);
819             } else {
820 4 50       17 $obj->in_response_body($body,0,$time) if $obj;
821 4         18 $rq->{rpchunked} = 2; # get CRLF after chunk
822             }
823             } else {
824             # only part
825 1 50       4 $DEBUG && $rq->xdebug("need $want bytes, got only $l");
826 1         3 my $body = substr($data,0,$l,'');
827 1         3 $self->{offset}[1] += $l;
828 1         2 $bytes += $l;
829 1         3 $rq->{rpclen} -= $l;
830 1 50       5 $obj->in_response_body($body,0,$time) if $obj;
831             }
832              
833             # no content-length, no chunk: must read until eof
834             } elsif ( $rq->{state} & RPBDY_DONE_ON_EOF ) {
835 2 50       5 $DEBUG && $rq->xdebug("read until eof");
836 2         5 $self->{offset}[1] += length($data);
837 2         4 $bytes += length($data);
838 2 50       5 if ($eof) {
839             # response done
840 0         0 pop(@$rqs);
841 0 0       0 $DEBUG && $rq->xdebug("response done (eof)");
842             }
843 2 50       10 $obj->in_response_body($data,$eof,$time) if $obj;
844 2         10 $data = '';
845 2         5 return $bytes;
846              
847             # Chunking: rfc2616, 3.6.1
848             } elsif ( ! $rq->{rpchunked} ) {
849             # should not happen
850 0         0 die "no content-length and no chunked - why we are here?";
851             } else {
852             # [2] must get CRLF after chunk
853 10 100       23 if ( $rq->{rpchunked} == 2 ) {
854 4 50       11 $DEBUG && $rq->xdebug("want CRLF after chunk");
855 4 50       18 if ( $data =~m{\A\r?\n}g ) {
    0          
856 4         9 my $n = pos($data);
857 4         8 $self->{offset}[1] += $n;
858 4         9 $bytes += $n;
859 4         10 substr($data,0,$n,'');
860 4         6 $rq->{rpchunked} = 1; # get next chunk header
861 4 50       11 $DEBUG && $rq->xdebug("got CRLF after chunk");
862             } elsif ( length($data)>=2 ) {
863 0   0     0 ($obj||$self)->fatal("no CRLF after chunk",1,$time);
864 0         0 $self->{error} = 1;
865 0         0 return $bytes;
866             } else {
867             # need more
868 0         0 return $bytes;
869             }
870             }
871              
872             # [1] must read chunk header
873 10 50       24 if ( $rq->{rpchunked} == 1 ) {
874 10 50       21 $DEBUG && $rq->xdebug("want chunk header");
875 10 100 33     52 if ( $data =~m{\A([\da-fA-F]+)[ \t]*(?:;.*)?\r?\n}g ) {
    50          
876 6         47 $rq->{rpclen} = hex($1);
877 6         18 my $chdr = substr($data,0,pos($data),'');
878 6         12 $self->{offset}[1] += length($chdr);
879 6         10 $bytes += length($chdr);
880             $self->{gap_upto}[1] = $self->{offset}[1] + $rq->{rpclen}
881 6 100       19 if $rq->{rpclen};
882              
883 6 50       24 $obj->in_chunk_header(1,$chdr,$time) if $obj;
884 6 50       32 $DEBUG && $rq->xdebug(
885             "got chunk header - want $rq->{rpclen} bytes");
886 6 100       16 if ( ! $rq->{rpclen} ) {
887             # last chunk
888 2         5 $rq->{rpchunked} = 3;
889 2 50       8 $obj && $obj->in_response_body('',1,$time);
890             }
891             } elsif ( $data =~m{\n} or length($data)>8192 ) {
892 0   0     0 ($obj||$self)->fatal("invalid chunk header",1,$time);
893 0         0 $self->{error} = 1;
894 0         0 return $bytes;
895             } else {
896             # need more data
897 4         12 return $bytes;
898             }
899             }
900              
901             # [3] must read chunk trailer
902 6 100       22 if ( $rq->{rpchunked} == 3 ) {
903 2 50       5 $DEBUG && $rq->xdebug("want chunk trailer");
904 2 50 0     9 if ( $data =~m{\A
    0          
905             (?:\w[\w\-]*:.*\r?\n(?:[\t\040].*\r?\n)* )* # field:..+cont
906             \r?\n
907             }xg) {
908 2 50       11 $DEBUG && $rq->xdebug("response done (chunk trailer)");
909 2         5 my $trailer = substr($data,0,pos($data),'');
910 2         5 $self->{offset}[1] += length($trailer);
911 2         5 $bytes += length($trailer);
912 2 50       16 $obj->in_chunk_trailer(1,$trailer,$time) if $obj;
913 2         10 pop(@$rqs); # done
914             } elsif ( $data =~m{\n\r?\n} or
915             length($data)>$self->{hdr_maxsz}[2] ) {
916 0   0     0 ($obj||$self)->fatal("invalid chunk trailer",1,$time);
917 0         0 $self->{error} = 1;
918 0         0 return $bytes;
919             } else {
920             # need more
921 0 0       0 $DEBUG && $rq->xdebug("need more bytes for chunk trailer");
922 0         0 return $bytes
923             }
924             }
925             }
926             }
927              
928 12         25 goto READ_DATA;
929             }
930              
931             # parse and normalize header
932             sub parse_hdrfields {
933 31     31 1 82 my ($hdr,$fields) = @_;
934 31 100       98 return '' if ! defined $hdr;
935 18         33 my $bad = '';
936             parse:
937 36         449 while ( $hdr =~m{\G$token_value_cont}gc ) {
938 24 50       75 if ($3 eq '') {
939             # no continuation line
940 24         46 push @{$fields->{ lc($1) }},$2;
  24         201  
941             } else {
942             # with continuation line
943 0         0 my ($k,$v) = ($1,$2.$3);
944             # value-part -> ' ' + value-part
945 0         0 $v =~s{[\r\n]+[ \t](.*?)[ \t]*}{ $1}g;
946 0         0 push @{$fields->{ lc($k) }},$v;
  0         0  
947             }
948             }
949 36 100 66     158 if (pos($hdr)//0 != length($hdr)) {
950             # bad line inside
951 18   50     68 substr($hdr,0,pos($hdr)//0,'');
952 18 50       52 $bad .= $1 if $hdr =~s{\A([^\n]*)\n}{};
953 18         63 goto parse;
954             }
955 18         52 return $bad;
956             }
957              
958             sub parse_reqhdr {
959 17     17 1 43 my ($data,$hdr,$external_length) = @_;
960 17 50       123 $data =~m{\A
961             ([A-Z]{2,20})[\040\t]+ # $1: method
962             (\S+)[\040\t]+ # $2: path/URI
963             HTTP/(1\.[01])[\40\t]* # $3: version
964             \r?\n # (CR)LF
965             ([^\r\n].*?\n)? # $4: fields
966             \r?\n # final (CR)LF
967             \Z}sx or return "invalid request header";
968              
969 17         47 my $version = $3;
970 17         53 my $method = $1;
971 17         111 %$hdr = (
972             method => $method,
973             url => $2,
974             version => $version,
975             info => "$method $2 HTTP/$version",
976             # fields - hash of fields
977             # junk - bad header fields
978             # expect - expectations from expect header
979             # upgrade - { websocket => key }
980             # content_length
981             # chunked
982             );
983              
984 17         35 my %kv;
985 17         43 my $bad = parse_hdrfields($4,\%kv);
986 17 50       51 $hdr->{junk} = $bad if $bad ne '';
987 17         44 $hdr->{fields} = \%kv;
988              
989 17 100 66     105 if ($version>=1.1 and $kv{expect}) {
990 1         3 for(@{$kv{expect}}) {
  1         4  
991             # ignore all but 100-continue
992 1 50       19 $hdr->{expect}{lc($1)} = 1 if m{\b(100-continue)\b}i
993             }
994             }
995              
996             # RFC2616 4.4.3:
997             # chunked transfer-encoding takes preferece before content-length
998 17 100 100     59 if ( $version >= 1.1 and
    100          
999 4         26 grep { m{(?:^|[ \t,])chunked(?:$|[ \t,;])}i }
1000 12 100       79 @{ $kv{'transfer-encoding'} || [] }
1001             ) {
1002 4         11 $hdr->{chunked} = 1;
1003              
1004             } elsif ( my $cl = $kv{'content-length'} ) {
1005             return "multiple different content-length header in request"
1006 5 50 33     21 if @$cl>1 and do { my %x; @x{@$cl} = (); keys(%x) } > 1;
  0         0  
  0         0  
  0         0  
1007 5 100       45 return "invalid content-length '$cl->[0]' in request"
1008             if $cl->[0] !~m{^(\d+)$};
1009 4         15 $hdr->{content_length} = $cl->[0];
1010             }
1011              
1012 16 100       60 if ( $METHODS_WITHOUT_RQBODY{$method} ) {
    50          
    0          
1013             # Complain if the client announced a body.
1014             return "no body allowed with $method"
1015 8 50 33     36 if $hdr->{content_length} or $hdr->{chunked};
1016              
1017             } elsif ( $METHODS_WITH_RQBODY{$method} ) {
1018             return "content-length or transfer-encoding chunked must be given with method $method"
1019             if ! $hdr->{chunked}
1020             and ! defined $hdr->{content_length}
1021 8 0 66     31 and ! $external_length;
      33        
1022              
1023             } elsif ( ! $hdr->{chunked} ) {
1024             # if not given content-length is considered 0
1025 0   0     0 $hdr->{content_length} ||= 0;
1026             }
1027              
1028             # Connection upgrade
1029 16 100 66     66 if ($version >= 1.1 and $kv{upgrade} and my %upgrade
      66        
1030 1         13 = map { lc($_) => 1 } map { m{($token)}g } @{$kv{upgrade}}) {
  1         50  
  1         2  
1031 1         3 $hdr->{upgrade} = \%upgrade;
1032             }
1033              
1034 16         43 return; # no error
1035             }
1036              
1037             sub parse_rsphdr {
1038 14     14 1 34 my ($data,$request,$hdr,$warn) = @_;
1039 14 50       86 $data =~ m{\A
1040             HTTP/(1\.[01])[\040\t]+ # $1: version
1041             (\d\d\d) # $2: code
1042             (?:[\040\t]+([^\r\n]*))? # $3: reason
1043             \r?\n
1044             ([^\r\n].*?\n)? # $4: fields
1045             \r?\n # empty line
1046             \Z}sx or return "invalid response header";
1047              
1048 14         37 my $version = $1;
1049 14         31 my $code = $2;
1050 14         61 %$hdr = (
1051             version => $version,
1052             code => $code,
1053             reason => $3,
1054             # fields
1055             # junk
1056             # content_length
1057             # chunked
1058             # upgrade
1059             # preliminary
1060             );
1061              
1062 14         25 my %kv;
1063 14         36 my $bad = parse_hdrfields($4,\%kv);
1064 14         35 $hdr->{fields} = \%kv;
1065 14 50       44 $hdr->{junk} = $bad if $bad ne '';
1066              
1067 14 100       45 if ($code<=199) {
1068             # Preliminary responses do not contain any body.
1069 2         6 $hdr->{preliminary} = 1;
1070 2         5 $hdr->{content_length} = 0;
1071 2 50 66     25 if ($code == 100 and $request->{expect}{'100-continue'}
      66        
      66        
1072             or $code == 102 or $code == 101) {
1073             # 100 should only happen with Expect: 100-continue from client
1074             } else {
1075 0 0       0 push @$warn,"unexpected intermediate status code $code" if $warn;
1076             }
1077             }
1078              
1079             # Switching Protocols
1080             # Any upgrade must have both a "Connection: upgrade" and a
1081             # "Upgrade: newprotocol" header.
1082 14 100       43 if ($code == 101) {
1083 1         2 my %proto;
1084 1 50 33     5 if ($request->{upgrade}
1085 1 50       7 and grep { m{\bUPGRADE\b}i } @{$kv{connection} || []}) {
  1         4  
1086 1 50       3 for(@{$kv{upgrade} || []}) {
  1         8  
1087 1         6 $proto{lc($_)} = 1 for split(m{\s*[,;]\s*});
1088             }
1089             }
1090              
1091 1 50       5 if (keys(%proto) == 1) {
1092 1         3 $hdr->{upgrade} = (keys %proto)[0];
1093 1         3 $hdr->{preliminary} = 0;
1094 1         2 $hdr->{content_length} = undef;
1095             } else {
1096 0         0 return "invalid or unsupported connection upgrade";
1097             }
1098             }
1099              
1100             # successful response to CONNECT
1101 14 50 66     57 if ($request->{method} eq 'CONNECT' and $code >= 200 and $code < 300) {
      66        
1102 1         4 $hdr->{upgrade} = 'CONNECT';
1103 1         3 $hdr->{content_length} = 0;
1104 1         3 delete $hdr->{chunked};
1105 1         4 return;
1106             }
1107              
1108             # RFC2616 4.4.3:
1109             # chunked transfer-encoding takes preferece before content-length
1110 13 100 100     57 if ( $version >= 1.1 and
    100          
1111 2         13 grep { m{(?:^|[ \t,])chunked(?:$|[ \t,;])}i }
1112 9 100       69 @{ $kv{'transfer-encoding'} || [] }
1113             ) {
1114 2         6 $hdr->{chunked} = 1;
1115              
1116             } elsif ( my $cl = $kv{'content-length'} ) {
1117             return "multiple different content-length header in response"
1118 5 50 33     22 if @$cl>1 and do { my %x; @x{@$cl} = (); keys(%x) } > 1;
  0         0  
  0         0  
  0         0  
1119 5 100       35 return "invalid content-length '$cl->[0]' in response"
1120             if $cl->[0] !~m{^(\d+)$};
1121 4         12 $hdr->{content_length} = $cl->[0];
1122             }
1123              
1124 12 100 66     57 if ($CODE_WITHOUT_RPBODY{$code}
1125             or $METHODS_WITHOUT_RPBODY{$request->{method}}) {
1126             # no content, even if specified
1127 4         11 $hdr->{content_length} = 0;
1128 4         9 delete $hdr->{chunked};
1129 4         14 return;
1130             }
1131              
1132 8         22 return;
1133             }
1134              
1135              
1136             sub new_request {
1137 17     17 1 45 my $self = shift;
1138 17         58 return $self->{upper_flow}->new_request(@_,$self)
1139             }
1140              
1141             # return open requests
1142             sub open_requests {
1143 0     0 1   my $self = shift;
1144 0 0         my @rq = @_ ? @{$self->{requests}}[@_] : @{$self->{requests}};
  0            
  0            
1145             return wantarray
1146 0 0         ? map { $_->{obj} ? ($_->{obj}):() } @rq
  0 0          
1147             : 0 + @rq;
1148             }
1149              
1150             sub fatal {
1151 0     0 1   my ($self,$reason,$dir,$time) = @_;
1152 0 0         %TRACE && $self->xtrace($reason);
1153             }
1154              
1155             sub xtrace {
1156 0     0 0   my $self = shift;
1157 0           my $msg = shift;
1158 0           $msg = "$$.$self->{connid} $msg";
1159 0           unshift @_,$msg;
1160 0           goto &trace;
1161             }
1162              
1163             sub xdebug {
1164 0 0   0 0   $DEBUG or return;
1165 0           my $self = shift;
1166 0           my $msg = shift;
1167 0           $msg = "$$.$self->{connid} $msg";
1168 0           unshift @_,$msg;
1169 0           goto &debug;
1170             }
1171              
1172             sub dump_state {
1173 0 0 0 0 1   $DEBUG or defined wantarray or return;
1174 0           my $self = shift;
1175 0           my $m = $self->{meta};
1176             my $msg = sprintf("%s.%d -> %s.%d ",
1177 0           $m->{saddr},$m->{sport},$m->{daddr},$m->{dport});
1178 0           my $rqs = $self->{requests};
1179 0           for( my $i=0;$i<@$rqs;$i++) {
1180             $msg .= sprintf("request#$i state=%05b %s",
1181 0           $rqs->[$i]{state},$rqs->[$i]{request}{info});
1182             }
1183 0 0         return $msg if defined wantarray;
1184 0           $self->xdebug($msg);
1185             }
1186              
1187              
1188             {
1189             package Net::Inspect::L7::HTTP::_DebugRequest;
1190             sub xdebug {
1191 0     0     my $rq = shift;
1192 0           my $msg = shift;
1193 0           unshift @_, $rq->{conn}, "#$rq->{reqid} $msg";
1194 0           goto &Net::Inspect::L7::HTTP::xdebug;
1195             }
1196             }
1197              
1198             1;
1199              
1200             __END__