App-HTTP_Proxy_IMP

 view release on metacpan or  search on metacpan

lib/App/HTTP_Proxy_IMP/IMP.pm  view on Meta::CPAN

    # reset infos about content-length
    $xhdr->{content_length} = $xhdr->{chunked} = undef;
    delete @{ $xhdr->{fields} }{ qw/ content-length transfer-encoding / };

    # if we have read the whole body already or at least know, that we will
    # not change anymore data, we could compute the new content-length
    my $clen;
    my $nochange;
    while ( defined $orig_clen ) {
	my $rpsize = $orig_hlen + $orig_clen;

	if ( my $pass = $self->{pass}[1] ) {
	    if ( $pass == IMP_MAXOFFSET or $pass >= $rpsize ) {
		# will not look at and not change body
		$nochange = 1;
		goto compute_clen;
	    }
	}
	if ( my $prepass = $self->{prepass}[1] ) {
	    if ( $prepass == IMP_MAXOFFSET or $prepass >= $rpsize ) {
		# will not change body
		$nochange = 1;
		goto compute_clen;
	    }
	}
	if ($self->{ibuf}[1][0][0] >= $rpsize) { # ibuf[server].base
	    # everything passed thru ibuf
	    goto compute_clen;
	}

	# we still don't know final size
	last;

	compute_clen:
	# bytes in ibuf and outstanding bytes will not be changed, so:
	# new_content_length = 
	#  ( total_size - received )             # not yet received
	#  + ( received - ibuf.base )            # still in ibuf
	#  --->
	#  total_size - ibuf.base
	$clen = $rpsize - $self->{ibuf}[1][0][0];

	last;
    }

    if ( $self->{decode}{IMP_DATA_HTTPRQ_CONTENT+0}[1] ) {
	if ( $nochange ) {
	    # we will pass encoded stuff, either no decoding needs to
	    # be done (pass) or we will decode only for the analyzer (prepass)
	    # which will only watch at the content, but not change it
	    $self->{pass_encoded}[1] = 1;

	    my $pass = $self->{pass}[1];
	    if ( $pass and defined $orig_clen and ( 
		$pass == IMP_MAXOFFSET or 
		$pass >= $orig_clen + $orig_hlen )) {
		# no need to decode body
		$self->{decode}{IMP_DATA_HTTPRQ_CONTENT+0}[1] = undef;
	    }
	} else {
	    # content is encoded and inspection wants to see decoded stuff,
	    # which we then will forward too 
	    # but decoding might change length
	    $clen = undef;
	    # the content will be delivered decoded
	    delete $xhdr->{fields}{'content-encoding'}
	}
    }
    if ( defined $clen ) {
	$xhdr->{fields}{'content-length'} = [ $clen ];
	$xhdr->{content_length} = $clen;
    }

    callback:
    $callback->(@cb_args,$hdr,$xhdr);
}



############################################################################
# handle response body data
############################################################################
sub response_body {
    my ($self,$data,@callback) = @_;

    # forward to IMP analyzer
    $self->{eof}[1] = 1 if $data eq '';
    _imp_data($self,1,$data,0,IMP_DATA_HTTPRQ_CONTENT,
	\&_response_body_imp,\@callback);
}

sub _response_body_imp {
    my ($self,$data,$changed,$args) = @_;
    my ($callback,@cb_args) = @$args;
    my $eof = _check_eof($self,1);
    $callback->(@cb_args,$data,$eof) if $data ne '' || $eof;
}


sub _check_eof {
    my ($self,$dir) = @_;
    $DEBUG && $self->{request}->xdebug(
	"check eof[%d]  - eof=%d - %s - (pre)pass=%d/%d",
	$dir,$self->{eof}[$dir], _show_buf($self,$dir),
	$self->{prepass}[$dir],
	$self->{pass}[$dir]
    );
    return $self->{eof}[$dir]                    # received eof
	&& ! defined $self->{ibuf}[$dir][0][2]   # no more data in buf
	&& (                                     # (pre)pass til end ok
	    $self->{prepass}[$dir] == IMP_MAXOFFSET
	    || $self->{pass}[$dir] == IMP_MAXOFFSET
	);
}

sub _show_buf {
    my ($self,$dir) = @_;
    return join('|',
	map { ($_->[2]||'none')."($_->[0],+".length($_->[1]).")" } 
	@{ $self->{ibuf}[$dir] }
    );
}



lib/App/HTTP_Proxy_IMP/IMP.pm  view on Meta::CPAN


	    push @{$fwd{$dir}}, [
		1,          # changed
		$newdata,   # new data
		$ib0->[3],  # callback
		$ib0->[4],  # cbargs
	    ];

	    next;
	}
        if ( $rtype ~~ [ IMP_PAUSE, IMP_CONTINUE ] ) {
	    my $dir = shift(@$rv);
	    my $relay = $self->{request}{conn}{relay};
	    if ( $relay and my $fo = $relay->fd($dir)) {
		$fo->mask( r => ($rtype == IMP_PAUSE ? 0:1));
	    }
	    next;
	}

	if ( $rtype == IMP_FATAL ) {
	    $request->fatal(shift(@$rv));
	    next;
	}

	return $request->fatal("unsupported IMP return type: $rtype");
    }

    %fwd or return; # no passes/replacements...

    while ( my ($dir,$fwd) = each %fwd ) {
	while ( my $fw = shift(@$fwd)) {
	    #warn Dumper($fw); use Data::Dumper;
	    my ($changed,$data,$callback,$args) = @$fw;
	    $callback->($self,$data,$changed,$args);
	}
    }
}

############################################################################
# send data to IMP analyzer
# if we had a previous (pre)pass some data can be forwarded immediatly, for
# others we have to wait for the analyzer callback
# returns how many bytes of data are waiting for callback, e.g. 0 if we
# we can pass everything immediately
############################################################################
sub _imp_data {
    my ($self,$dir,$data,$offset,$type,$callback,$args) = @_;
    my $ibuf = $self->{ibuf}[$dir];
    my $eobuf = $ibuf->[-1][0] + length($ibuf->[-1][1]);

    my $encoded_data;
    if ( my $decode = $self->{decode}{$type+0}[$dir] ) {
	# set up decoder if not set up yet
	if ( ! ref($decode)) {
	    # create function to decode content
	    $self->{decode}{$type+0}[$dir] = $decode = _create_decoder($decode)
		|| return $self->{request}->fatal(
		"cannot decode content-encoding $decode");
	}

	# offsets relates to original stream, but we put the decoded stream
	# into ibuf. And offset>0 means, that we have a gap in the input,
	# which is not allowed, when decoding a stream.
	die "cannot use content decoder with gap in data" if $offset;

	$encoded_data = $data if $self->{pass_encoded}[$dir];
	defined( $data = $decode->($data) )
	    or return $self->{request}->fatal("decoding content failed");
    }

    if ( $offset ) {
	die "offset($offset)<eobuf($eobuf)" if $offset < $eobuf;
	$offset = 0 if $offset == $eobuf;
    }

    my $fwd; # what gets send to analyzer

    my $dlen = length($data);
    my $pass =  $self->{pass}[$dir];
    if ( $pass ) {
	# if pass is set there should be no data in ibuf, e.g. everything
	# before should have been passed
	! $ibuf->[0][2] or die "unexpected data in ibuf";

	if ( $pass == IMP_MAXOFFSET ) {
	    # pass thru w/o analyzing
	    $ibuf->[0][0] += $dlen;
	    $DEBUG && $self->{request}->xdebug("can pass($dir) infinite");
	    return $callback->($self,$encoded_data // $data,0,$args);
	}

	my $canpass = $pass - ( $offset||$eobuf );
	if ( $canpass <= 0 ) {
	    # cannot pass anything, pass should have been reset already
	    die "pass($dir,$pass) must be point into future ($canpass)";
	} elsif ( $canpass >= $dlen) {
	    # can pass everything
	    $ibuf->[0][0] += $dlen;
	    if ( $data eq '' ) {
		# forward eof to analyzer
		$fwd = $data;
		$DEBUG && $self->{request}->xdebug("pass($dir) eof");
		goto SEND2IMP;
	    }
	    $DEBUG && $self->{request}->xdebug(
		"can pass($dir) all: pass($canpass)>=data.len($dlen)");
	    return $callback->($self,$encoded_data // $data,0,$args);
	} elsif ( $type < 0 ) {
	    # can pass part of data, only for streaming types
	    # remove from data what can be passed 
	    die "body might change" if $self->{pass_encoded}[$dir];
	    $ibuf->[0][0] += $canpass;
	    my $passed_data = substr($data,0,$canpass,'');
	    $eobuf += $canpass;
	    $dlen = length($data);
	    $DEBUG && $self->{request}->xdebug(
		"can pass($dir) part: pass($canpass)<data.len($dlen)");
	    $callback->($self,$passed_data,0,$args); # callback but continue
	}
    }



( run in 0.508 second using v1.01-cache-2.11-cpan-e93a5daba3e )