File Coverage

blib/lib/App/HTTP_Proxy_IMP/Request.pm
Criterion Covered Total %
statement 36 339 10.6
branch 0 254 0.0
condition 0 68 0.0
subroutine 12 38 31.5
pod 0 14 0.0
total 48 713 6.7


line stmt bran cond sub pod time code
1              
2             ############################################################################
3             # Request
4             ############################################################################
5              
6 1     1   7 use strict;
  1         2  
  1         30  
7 1     1   5 use warnings;
  1         2  
  1         36  
8              
9             package App::HTTP_Proxy_IMP::Request;
10 1     1   5 use base 'Net::Inspect::Flow';
  1         2  
  1         128  
11             use fields (
12 1         5 'conn', # App::HTTP_Proxy_IMP::Connection object
13             'meta', # meta data
14             'me_proxy', # defined if I'm proxy, if true will be used for Via:
15             'up_proxy', # address of upstream proxy if any
16             'acct', # some accounting data
17             'connected', # false|CONN_HOST|CONN_INTERNAL
18              
19             'imp_analyzer', # App::HTTP_Proxy_IMP::IMP object
20             'defer_rqhdr', # deferred request header (wait until body length known)
21             'defer_rqbody', # deferred request body (wait until header can be sent)
22              
23             'method', # request method
24             'rqhost', # hostname from request
25             'rq_version', # version of request
26             'rp_encoder', # sub to encode response body (chunked)
27             'keep_alive', # do we use keep_alive in response
28 1     1   6 );
  1         2  
29              
30 1     1   145 use App::HTTP_Proxy_IMP::Debug qw(debug $DEBUG debug_context);
  1         2  
  1         121  
31 1     1   7 use Scalar::Util 'weaken';
  1         1  
  1         45  
32 1     1   5 use Net::Inspect::Debug 'trace';
  1         2  
  1         6  
33 1     1   77 use Net::IMP qw(:DEFAULT :log);
  1         2  
  1         181  
34 1     1   7 use Net::IMP::HTTP; # constants
  1         3  
  1         87  
35 1     1   519 use Sys::Hostname 'hostname';
  1         1077  
  1         69  
36              
37             my $HOSTNAME = hostname();
38              
39             # connected to host or do we fake the response internally
40 1     1   8 use constant CONN_HOST => 1;
  1         2  
  1         56  
41 1     1   6 use constant CONN_INTERNAL => 2;
  1         2  
  1         4602  
42              
43             sub DESTROY {
44 0 0   0     $DEBUG && debug("destroy request");
45             #Devel::TrackObjects->show_tracked;
46             }
47             sub new_request {
48 0     0 0   my ($factory,$meta,$conn) = @_;
49 0           my $self = $factory->new;
50 0 0         $DEBUG && $conn->xdebug("new request $self");
51              
52 0           $self->{meta} = $meta;
53 0           weaken($self->{conn} = $conn);
54 0           $self->{defer_rqhdr} = $self->{defer_rqbody} = '';
55              
56 0           $self->{acct} = { %$meta, Id => $self->id };
57 0 0         if ( my $f = $conn->{imp_factory} ) {
58 0           $self->{imp_analyzer} = $f->new_analyzer($self,$meta);
59             }
60              
61 0           $self->{me_proxy} = $HOSTNAME;
62 0           $self->{up_proxy} = $meta->{upstream};
63              
64 0           return $self;
65             }
66              
67             sub xdebug {
68 0     0 0   my $self = shift;
69 0           my $ctx = debug_context( id => $self->id );
70 0           goto &debug;
71             }
72              
73             sub id {
74 0     0 0   my $self = shift;
75 0 0         $self->{conn} or return '';
76             return $$.'.'.$self->{conn}{connid}.'.'.$self->{meta}{reqid}
77 0           }
78              
79             sub fatal {
80 0     0 0   my ($self,$reason) = @_;
81 0           warn "[fatal] ".$self->id." $reason\n";
82 0 0         if ( my $conn = $self->{conn} ) {
83 0           my $relay = $conn->{relay};
84 0           $relay->account('fatal');
85 0           $relay->close;
86             }
87             }
88              
89             sub deny {
90 0     0 0   my ($self,$reason) = @_;
91 0           warn "[deny] ".$self->id." $reason\n";
92 0 0 0       if ( my $relay = $self->{conn} && $self->{conn}{relay} ) {
93 0           $relay->account('deny', status => 'DENIED', reason => $reason );
94             $relay->forward(1,0,"HTTP/1.0 403 $reason\r\n\r\n")
95 0 0         if ! $self->{acct}{code};
96 0           $relay->close;
97             }
98             }
99              
100             sub xtrace {
101 0     0 0   my $self = shift;
102 0           my $msg = shift;
103 0           $msg = "$$.$self->{conn}{connid}.$self->{meta}{reqid} $msg";
104 0           unshift @_,$msg;
105 0           goto &trace;
106             }
107              
108              
109             ############################################################################
110             # process HTTP request header
111             # called from HTTP connection object
112             # if IMP plugin is configured it will send the received header to the plugin
113             # and continue from the IMP callback to _request_header_after_imp.
114             # if no IMP is configured it will immediatly go there
115             ############################################################################
116              
117             my %default_port = ( http => 80, ftp => 21, https => 443 );
118             sub in_request_header {
119 0     0 0   my ($self,$hdr,$time,$xhdr) = @_;
120 0 0         my $conn = $self->{conn} or return;
121 0 0         if ( $conn->{spool} ) {
122             # we have an active request, spool this new one (pipelining)
123 0 0         $DEBUG && $self->xdebug("spool new request");
124 0           push @{$conn->{spool}}, [ \&in_request_header, @_ ];
  0            
125 0           return;
126             }
127              
128 0 0         my $relay = $conn->{relay} or return;
129 0           $relay->acctinfo($self->{acct});
130 0           $conn->{spool} = []; # mark connection as processing request
131              
132 0 0         $DEBUG && $self->xdebug("incoming request header ".$hdr);
133              
134 0           $self->{method} = $xhdr->{method};
135 0           $self->{rq_version} = $xhdr->{version};
136              
137 0 0         if ( my $imp = $self->{imp_analyzer} ) {
138             # pass thru IMP
139 0   0       my $debug = $DEBUG && debug_context( id => $self->id);
140 0           $imp->request_header($hdr,$xhdr,
141             \&_request_header_after_imp,$self);
142             } else {
143             # pass directly
144 0           _request_header_after_imp($self,$hdr,$xhdr);
145             }
146             }
147              
148              
149             ############################################################################
150             # process HTTP request header, which might have been modified by IMP
151             # if not IMP is used this is called directly from in_request_header, else
152             # via callback from IMP
153             ############################################################################
154             sub _request_header_after_imp {
155 0     0     my ($self,$hdr,$xhdr) = @_;
156 0 0         my $conn = $self->{conn} or return;
157 0 0         my $relay = $conn->{relay} or return;
158              
159             # with IMP method should not change
160 0           my $met = $self->{method};
161             die "method should not change in IMP plugin"
162 0 0         if $met ne $xhdr->{method};
163              
164             # work with original client version
165 0           my $version = $self->{rq_version};
166 0           my $url = $xhdr->{url};
167              
168 0           my $head = $xhdr->{fields};
169 0 0         $xhdr->{junk} and $relay->error(
170             "Bad request header lines: $xhdr->{junk}");
171              
172 0           my ($proto,$host,$port,$path);
173 0 0         if ( $met eq 'CONNECT' ) {
174             # only possible if we work as proxy
175             return $self->fatal("connect request only allowed on proxy")
176 0 0         if ! defined $self->{me_proxy};
177             return $self->fatal("connect request not allowed inside ssl tunnel")
178 0 0         if $conn->{intunnel};
179              
180             # url should be host[:port]
181 0 0         $url =~m{^(?:\[([\w\-.:]+)\]|([\w\-.]+))(?::(\d+))$} or
182             return $self->fatal("invalid host[:port] in connect: $url");
183 0           $proto = 'https';
184 0   0       $host = lc($1||$2);
185 0   0       $port = $3 || $default_port{$proto};
186 0           $path = '';
187 0 0         $url = ( $host =~m{:} ? "[$host]":$host ) . ":$port";
188              
189             } else {
190 0 0         if ( $url =~m{^(\w+)://(?:\[([\w\-.:]+)\]|([\w\-.]+))(?::(\d+))?(.+)?} ) {
191             # absolute url, valid for HTTP/1.1 or proxy requests
192 0           $proto = lc($1);
193 0   0       $host = lc($2||$3);
194 0           $port = $4;
195 0   0       $path = $5 // '/';
196              
197             } else {
198             # relativ url, needs Host header if we want to get target
199             # from request
200 0           $proto = 'http';
201 0           $path = $url;
202 0 0         if ( my $h = $head->{host} ) {
203 0 0         $relay->error("Ignoring multiple host headers") if @$h>1;
204 0 0         $h->[0] =~m{^(?:\[([\w\-.:]+)\]|([\w\-.]+))(?::(\d+))?$} or
205             return $self->fatal("bad host line '$h->[0]'");
206 0   0       $host = $1||$2;
207 0           $port = $3;
208             } else {
209 0           return $self->fatal("cannot determine target host");
210             }
211             }
212              
213 0   0       $port //= $default_port{$proto};
214 0 0 0       return $self->fatal("invalid port $port")
215             if ! $port or $port > 2**16-1;
216              
217 0 0         $path !~m{^/} and return $self->fatal("invalid path $path ($url)");
218              
219             # set/replace host header with target from URL and normalize URL
220 0           $host =~s{\.\.+}{.}g;
221 0 0         my $hp = $host =~m{:} ? "[$host]":$host;
222 0 0         $hp .= ":$port" if $default_port{$proto} != $port;
223 0           $head->{host} = [ $hp ];
224 0           $url = "$proto://$hp$path";
225             }
226              
227 0           $self->{acct}{url} = $url;
228 0 0         $self->{acct}{url} =~s{://}{s://} if $conn->{intunnel};
229 0           $self->{acct}{method} = $met;
230 0           $self->{acct}{reqid} = $self->{meta}{reqid};
231 0           $self->{rqhost} = $host;
232              
233 0 0 0       if ( $met eq 'CONNECT' and ! $self->{up_proxy} ) {
234             # just skip all the header manipulation and normalization, we don't
235             # need your stinkin header!
236 0           $hdr = '';
237 0           goto SRVCON;
238             }
239              
240             # do we want/support persistence?
241 0           my %conn = map { lc($_) => 1 } grep { m{\b(close|keep-alive)\b}i } (
  0            
242 0 0         @{ delete $head->{connection} || [] },
243             defined($self->{me_proxy})
244 0 0         ? @{ delete $head->{'proxy-connection'} || [] } : ()
  0 0          
245             );
246 0 0         if ( keys %conn > 1 ) {
    0          
    0          
247             # fall back to close
248 0           $self->{keep_alive} = 0;
249 0           $head->{connection} = [ 'close' ];
250             } elsif ( $conn{close} ) {
251 0           $self->{keep_alive} = 0;
252             # default in 1.1 is keep-alive
253 0 0         $head->{connection} = [ 'close' ] if $version eq '1.1';
254             } elsif ( $conn{'keep-alive'} ) {
255 0           $self->{keep_alive} = 1;
256             # default in 1.0 is close
257 0 0         $head->{connection} = [ 'keep-alive' ] if $version eq '1.0';
258             } else {
259             # use default of version
260 0           $self->{keep_alive} = $version eq '1.1';
261             }
262              
263             # if we are a proxy set a via tag
264 0 0         if ( my $via = $self->{me_proxy} ) {
265 0           push @{$head->{via}}, "$version $via";
  0            
266             }
267              
268             # normalize header before forwarding it
269             # sort keys, normalize case of keys etc
270 0 0         $hdr = "$met ".( $self->{up_proxy} ? $url : $path )." HTTP/$version\r\n";
271 0           for my $k ( sort keys %$head) {
272 0           $hdr .= "\u$k: $_\r\n" for @{$head->{$k}};
  0            
273             }
274 0           $hdr .= "\r\n";
275              
276             SRVCON:
277              
278 0 0         if ( $xhdr->{internal_url} ) {
279             # the IMP plugin rewrote the url to internal://smthg,
280             # meaning, that the plugin will provide us with the real response
281 0           $self->{acct}{internal} = 1;
282 0           $self->{connected} = CONN_INTERNAL;
283 0           $self->{keep_alive} = 0;
284              
285             # accept more body data
286 0           _call_spooled_this($conn);
287 0           $relay->mask(0,r=>1);
288              
289             # inject minimal response into Net::Inspect, which than can modify
290             # it at will
291             # IMP let us not change nothing (e.g. empty body) into something, so
292             # we need to provide minimal content where content is expected
293 0 0         $conn->in(1,
294             $met eq 'HEAD'
295             ? "HTTP/$version 200 Ok\r\n\r\n"
296             : "HTTP/$version 200 Ok\r\nContent-length: 1\r\n\r\n%",
297             1, # eof
298             0, # time
299             );
300 0           return;
301             }
302              
303 0 0         if ( my $imp = $self->{imp_analyzer} ) {
304 0 0         if ( defined( my $len = $xhdr->{content_length} )) {
305             # length is given, fix header
306 0   0       my $debug = $DEBUG && debug_context( id => $self->id);
307 0           $imp->fixup_request_header(\$hdr, content => $len);
308             } else {
309 0           $self->{defer_rqhdr} = $hdr;
310             }
311             }
312              
313 0 0         if ( $conn->{intunnel} ) {
314 0           _fwd_request_after_connect($self,$hdr);
315             } else {
316             $relay->connect( 1,
317 0 0         @{ $self->{up_proxy} || [ $host,$port ] },
318 0     0     sub { _fwd_request_after_connect($self,$hdr) }
319 0           );
320             }
321             }
322              
323             sub _fwd_request_after_connect {
324 0     0     my ($self,$hdr) = @_;
325 0           $self->{connected} = CONN_HOST;
326              
327 0 0         if ($hdr eq '') {
328             # no header, e.g we have a CONNECT to a non-proxy
329             # put a fake response into Net::Inspect to keep state
330 0           $self->{conn}->in(1,"HTTP/1.0 200 Connection established\r\n\r\n");
331 0           return _call_spooled_this($self->{conn});
332             }
333              
334 0 0         if ( my $imp = $self->{imp_analyzer} ) {
335 0   0       my $debug = $DEBUG && debug_context( id => $self->id);
336 0 0         if ( $imp->fixup_request_header(\$hdr, defered => 0) ) {
337 0           $self->{defer_rqhdr} = '';
338             } else {
339             # keep deferring sending header, length not known
340 0           _call_spooled_this($self->{conn}); # any body already ?
341 0           return;
342             }
343             }
344              
345 0 0         my $relay = $self->{conn}{relay} or return;
346 0 0         $relay->forward(0,1,$hdr) if $self->{connected} == CONN_HOST;
347 0           _call_spooled_this($self->{conn}); # any body already ?
348             }
349              
350             sub _call_spooled_this {
351 0     0     my $conn = shift;
352              
353             # call spooled request_bodies, e.g. until we see a new request
354 0           debug("check for spooled subs in this request");
355 0 0         my $spool = $conn->{spool} or return;
356 0           $conn->{spool} = undef;
357 0   0       while (@$spool && ! $conn->{spool} ) {
358 0           my ($sub,@arg) = @{ $spool->[0] };
  0            
359 0 0         last if $sub == \&in_request_header;
360 0           shift(@$spool);
361 0 0         $DEBUG && debug("handle spooled event $sub");
362 0           $sub->(@arg);
363             }
364 0 0         push @{ $conn->{spool}}, @$spool if @$spool; # put back
  0            
365             }
366              
367             sub _call_spooled_next {
368 0     0     my $conn = shift;
369              
370             # skip until we have a next request, then continue
371 0           debug("check for spooled requests, ignoring subs for this");
372 0 0         my $spool = $conn->{spool} or return;
373 0           $conn->{spool} = undef;
374 0           while (@$spool) {
375 0           my ($sub,@arg) = @{ $spool->[0] };
  0            
376 0 0         last if $sub == \&in_request_header;
377 0 0         $DEBUG && debug("skip spooled event $sub");
378 0           shift(@$spool);
379             }
380 0   0       while (@$spool && ! $conn->{spool} ) {
381 0           my ($sub,@arg) = @{ $spool->[0] };
  0            
382 0 0         $DEBUG && debug("handle spooled event $sub");
383 0           $sub->(@arg);
384             }
385 0 0         push @{ $conn->{spool}}, @$spool if @$spool; # put back
  0            
386             }
387              
388             ############################################################################
389             # process request body data
390             # if IMP, we might need to wait for a callback to decide what to do with
391             # the data, otherwise the data are further send directly
392             # if IMP might modify the data, we need to defer sending the header to get
393             # the final content-length and fixup the header accordingly
394             ############################################################################
395             sub in_request_body {
396 0     0 0   my ($self,$data,$eof) = @_;
397 0 0         my $conn = $self->{conn} or return;
398 0 0         my $relay = $conn->{relay} or return;
399 0 0         if ( ! $self->{connected} ) {
400             # not connected yet
401 0 0         $DEBUG && $self->xdebug("spool request body data");
402 0           push @{$conn->{spool}}, [ \&in_request_body, @_ ];
  0            
403 0           return;
404             }
405            
406 0 0         $DEBUG && $self->xdebug("got request body data len=%d eof=%d",length($data),$eof);
407 0           my $imp = $self->{imp_analyzer};
408 0 0         if ( ! $imp ) {
409             # fast path w/o imp
410             $relay->forward(0,1,$data) if $data ne ''
411 0 0 0       and $self->{connected} == CONN_HOST;
412 0           return;
413             }
414              
415             # feed data into IMP
416 0 0         $DEBUG && $self->xdebug("fwd request body to IMP");
417 0   0       my $debug = $DEBUG && debug_context( id => $self->id);
418 0 0         $imp->request_body($data,\&_request_body_after_imp,$self) if $data ne '';
419 0 0         $imp->request_body('',\&_request_body_after_imp,$self) if $eof;
420             }
421              
422             ############################################################################
423             # process request body data in case of IMP
424             # called from IMP callback working on request body data
425             ############################################################################
426             sub _request_body_after_imp {
427 0     0     my ($self,$data,$eof) = @_;
428 0 0         my $conn = $self->{conn} or return;
429 0 0         my $relay = $conn->{relay} or return;
430              
431 0   0       my $debug = $DEBUG && debug_context( id => $self->id);
432              
433 0 0         if ( $self->{defer_rqhdr} ne '') {
434 0           $self->{defer_rqbody} .= $data;
435 0 0         if ( not $self->{imp_analyzer}->fixup_request_header(
436             \$self->{defer_rqhdr},
437             defered => length($self->{defer_rqbody})
438             )) {
439             # body length still not known
440 0 0         $DEBUG && debug("request body length still unknown");
441 0           $self->{defer_rqbody} .= $data;
442 0 0         $eof or return;
443             }
444              
445             $DEBUG && debug("forward %d bytes header + %d bytes body",
446             length($self->{defer_rqhdr}),
447 0 0         length($self->{defer_rqbody}));
448              
449             $relay->forward(0,1,$self->{defer_rqhdr}.$self->{defer_rqbody} )
450 0 0         if $self->{connected} == CONN_HOST;
451 0           $self->{defer_rqhdr} = $self->{defer_rqbody} = '';
452              
453             } else {
454 0 0         $DEBUG && debug("forward %d bytes body",length($data));
455 0 0         $relay->forward( 0,1,$data ) if $self->{connected} == CONN_HOST;
456             }
457             }
458              
459             ############################################################################
460             # process response header
461             # jumps to _response_header_after_imp, directly or from IMP
462             ############################################################################
463             sub in_response_header {
464 0     0 0   my ($self,$hdr,$time,$xhdr) = @_;
465 0 0         return if $xhdr->{code} == 100; # ignore preliminary response
466              
467 0 0         if ( my $imp = $self->{imp_analyzer} ) {
468 0   0       my $debug = $DEBUG && debug_context( id => $self->id);
469 0           $imp->response_header($hdr,$xhdr,
470             \&_response_header_after_imp,$self);
471             } else {
472 0           _response_header_after_imp($self,$hdr,$xhdr);
473             }
474             }
475              
476              
477             ############################################################################
478             # process response header, maybe it got manipulated by IMP
479             ############################################################################
480             sub _response_header_after_imp {
481 0     0     my ($self,$hdr,$xhdr) = @_;
482 0 0         my $relay = $self->{conn}{relay} or return;
483              
484 0           my $version = $xhdr->{version};
485 0           my $code = $self->{acct}{code} = $xhdr->{code};
486 0           my $clen = $xhdr->{content_length};
487              
488 0 0         $DEBUG && $self->xdebug("input header: $hdr");
489 0           my $status_line = "HTTP/$version $code $xhdr->{reason}\r\n"; # normalized
490              
491 0           my $head = $xhdr->{fields};
492             #warn Dumper($head); use Data::Dumper;
493 0 0         $xhdr->{junk} and $relay->error(
494             "Bad response header lines: $xhdr->{junk}");
495              
496             # check if the response is chunked and strip any transfer-encoding header
497             # it will be added, when we know, how we talk to the client
498 0 0         if ( $xhdr->{chunked} ) {
499 0           delete $head->{'transfer-encoding'};
500             # if chunked is given content-length should be ignored
501             # better strip, so that client will parse it correctly
502 0           delete $head->{'content-length'};
503             }
504              
505             # if we don't know the content_length we try chunked, but only if client
506             # and server used version 1.1. Otherwise we will close connection
507             # at request end.
508             # if only client supports chunking we better don't change response header
509             # to 1.1, because in the 1.0 response might contain 1.0 specific headers
510             # (Pragma...) which we don't know how to translate
511 0 0         if ( defined $clen ) {
    0          
512 0 0         $DEBUG && $self->xdebug("have content-length $clen");
513             } elsif ( $self->{method} eq 'CONNECT' ) {
514 0 0         $DEBUG && $self->xdebug("have connect request");
515             } else {
516 0 0 0       if ( $version eq '1.1' and $self->{rq_version} eq '1.1' ) {
517 0           $head->{'transfer-encoding'} = [ 'chunked' ];
518 0           delete $head->{'content-length'};
519 0 0         $DEBUG && $self->xdebug("no clen known - use chunked encoding");
520             $self->{rp_encoder} = sub {
521 0     0     my $data = shift;
522 0           sprintf("%x\r\n%s\r\n", length($data),$data)
523 0           };
524             } else {
525             # disable persistance, we will end with EOF
526 0 0         $DEBUG && $self->xdebug("no clen known - use eof to end response");
527 0           $self->{keep_alive} = 0;
528             }
529             }
530              
531             # set connection header if behavior is not default
532 0 0 0       if ( $version eq '1.1' and ! $self->{keep_alive} ) {
    0 0        
533 0           $head->{connection} = [ 'close' ];
534             } elsif ( $version eq '1.0' and $self->{keep_alive} ) {
535 0           $head->{connection} = [ 'keep-alive' ];
536             } else {
537             delete $head->{connection}
538 0           }
539              
540              
541             # create normalized header
542 0           $hdr = $status_line;
543 0           for my $k ( sort keys %$head) {
544 0           $hdr .= "\u$k: $_\r\n" for @{$head->{$k}};
  0            
545             }
546 0           $hdr .= "\r\n";
547              
548             # forward header
549 0 0         $DEBUG && $self->xdebug("output hdr: $hdr");
550 0           $relay->forward(1,0,$hdr);
551              
552 0 0         if ( $self->{method} eq 'CONNECT' ) {
553             # upgrade server side and client side with SSL, but intercept traffic.
554             # need to be called outside the current event handler, because $hdr
555             # will only be removed from rbuf after the current handler is done
556             App::HTTP_Proxy_IMP->once( sub {
557 0     0     $relay->sslify(1,0,$self->{rqhost});
558 0           });
559             }
560             }
561              
562              
563             ############################################################################
564             # handle response body data
565             # will be forwarded to _response_body_after_imp with data or '' (eof)
566             # maybe it will forwarded before to IMP analyzer
567             ############################################################################
568             sub in_response_body {
569 0     0 0   my ($self,$data,$eof) = @_;
570              
571 0           $self->xdebug("len=".length($data)." eof=$eof");
572 0 0         if ( my $imp = $self->{imp_analyzer} ) {
573 0   0       my $debug = $DEBUG && debug_context( id => $self->id);
574 0 0         $data ne '' && $imp->response_body($data,
575             \&_response_body_after_imp,$self);
576 0 0         $eof && $imp->response_body('',
577             \&_response_body_after_imp,$self);
578             } else {
579 0           _response_body_after_imp($self,$data,$eof);
580             }
581             }
582              
583             sub _response_body_after_imp {
584 0     0     my ($self,$data,$eof) = @_;
585 0           $self->xdebug("len=".length($data)." eof=$eof");
586 0 0         my $relay = $self->{conn}{relay} or return;
587              
588             # chunking, compression ...
589 0 0         if ( my $encode = $self->{rp_encoder} ) {
590 0 0         $data = $encode->($data) if $data ne '';
591 0 0         $data.= $encode->('') if $eof;
592             }
593 0 0         if ( $data ne '' ) {
594 0 0         $DEBUG && $self->xdebug("send ".length($data)." bytes to c");
595 0           $relay->forward(1,0,$data);
596             }
597              
598 0 0         if ($eof) {
599 0           $relay->account('request');
600 0 0         if ( ! $self->{keep_alive} ) {
601             # close connection
602 0 0         $DEBUG && $self->xdebug("end of request: close");
603 0           return $relay->close;
604             }
605              
606             # keep connection open
607             # and continue with next request if we have one
608 0 0         $DEBUG && $self->xdebug("end of request: keep-alive");
609 0           _call_spooled_next( $self->{conn} );
610             }
611             }
612              
613             ############################################################################
614             # Websockets, TLS upgrades etc
615             # if not IMP the forwarding will be done inside this function, otherwise it
616             # will be done in _in_data_imp, which gets called by IMP callback
617             ############################################################################
618             sub in_data {
619 0     0 0   my ($self,$dir,$data,$eof) = @_;
620              
621 0 0         if ( my $imp = $self->{imp_analyzer} ) {
622 0   0       my $debug = $DEBUG && debug_context( id => $self->id);
623 0 0         $data ne '' and $imp->data($dir,$data,\&_in_data_imp,$self);
624 0 0         $eof and $imp->data($dir,'',\&_in_data_imp,$self);
625             } else {
626 0 0         my $relay = $self->{conn}{relay} or return;
627 0 0         $DEBUG && $self->xdebug("got %d bytes from %d, eof=%d",length($data),$dir,$eof);
628 0 0         if ( $data ne '' ) {
629 0 0         if ( $dir == 1 ) {
630 0           $relay->forward(1,0,$data)
631             } else {
632 0 0         $relay->forward(0,1,$data) if $self->{connected} == CONN_HOST;
633             }
634             }
635 0 0         $relay->account('upgrade') if $eof;
636             }
637             }
638             sub _in_data_imp {
639 0     0     my ($self,$dir,$data,$eof) = @_;
640 0 0         my $relay = $self->{conn}{relay} or return;
641 0 0         $DEBUG && $self->xdebug("imp got %d bytes from %d, eof=%d",length($data),$dir,$eof);
642 0 0         if ( $data ne '' ) {
643 0 0         if ( $dir == 1 ) {
644 0           $relay->forward(1,0,$data)
645             } else {
646 0 0         $relay->forward(0,1,$data) if $self->{connected} == CONN_HOST;
647             }
648             }
649              
650 0 0         $relay->account('upgrade') if $eof;
651             }
652              
653             ############################################################################
654             # chunks and junk gets ignored
655             # - we decide ourself, when we will forward data chunked and do the
656             # chunking ourself
657             # - junk data will not be forwarded
658             ############################################################################
659              
660       0 0   sub in_chunk_header {}
661       0 0   sub in_chunk_trailer {}
662       0 0   sub in_junk {}
663              
664              
665             1;
666