Reverse-Proxy
view release on metacpan or search on metacpan
t/01-basic.t view on Meta::CPAN
. "Connection: close\r\n\r\n"
. $out;
close $c;
}
exit 0;
}
return ($pid, $port);
}
sub make_env {
my ($method, $path, $qs, $hdrs, $body) = @_;
open my $in, '<', \(my $b = defined $body ? $body : '');
my %env = (
REQUEST_METHOD => $method,
PATH_INFO => $path,
QUERY_STRING => defined $qs ? $qs : '',
SERVER_PROTOCOL => 'HTTP/1.1',
REMOTE_ADDR => '203.0.113.7',
'psgi.version' => [1, 1],
'psgi.url_scheme' => 'http',
'psgi.input' => $in,
'psgi.errors' => \*STDERR,
);
$env{CONTENT_LENGTH} = length $body if defined $body;
for my $k (keys %{ $hdrs || {} }) {
(my $n = uc $k) =~ tr/-/_/;
$env{"HTTP_$n"} = $hdrs->{$k};
}
return \%env;
}
sub header_val { # first value for a name in a PSGI header arrayref
my ($h, $name) = @_;
for (my $i = 0; $i + 1 < @$h; $i += 2) {
return $h->[$i + 1] if lc $h->[$i] eq lc $name;
}
return undef;
}
sub header_all {
my ($h, $name) = @_;
my @v;
for (my $i = 0; $i + 1 < @$h; $i += 2) {
push @v, $h->[$i + 1] if lc $h->[$i] eq lc $name;
}
return @v;
}
my ($pid, $port) = spawn_backend();
select undef, undef, undef, 0.2;
my $app = Reverse::Proxy->new(upstream => "http://127.0.0.1:$port")->to_app;
# ---- GET with query + a custom header ------------------------------------
{
my $res = $app->(make_env(GET => '/foo/bar', 'baz=1', { 'X-Custom' => 'hi' }));
is($res->[0], 200, 'GET proxied, 200');
my $body = $res->[2][0];
like($body, qr/\bM=GET\b/, 'method forwarded');
like($body, qr{\bU=/foo/bar\?baz=1\b}, 'path + query forwarded');
like($body, qr/\bXC=hi\b/, 'custom request header forwarded');
like($body, qr/\bXFF=203\.0\.113\.7\b/, 'X-Forwarded-For added from REMOTE_ADDR');
like($body, qr/\bXFP=http\b/, 'X-Forwarded-Proto added');
like($body, qr/\bHOST=127\.0\.0\.1:$port\b/, 'Host set from upstream (no preserve_host)');
is(header_val($res->[1], 'Content-Type'), 'text/plain', 'response Content-Type passed through');
is(header_val($res->[1], 'Keep-Alive'), undef, 'hop-by-hop Keep-Alive stripped from response');
is(header_val($res->[1], 'Connection'), undef, 'hop-by-hop Connection stripped from response');
is_deeply([ header_all($res->[1], 'Set-Cookie') ], [ 'a=1', 'b=2' ],
'multi-value Set-Cookie preserved in order');
}
# ---- POST with a body -----------------------------------------------------
{
my $res = $app->(make_env(POST => '/submit', '', {}, 'hello=world'));
is($res->[0], 200, 'POST proxied, 200');
like($res->[2][0], qr/\bM=POST\b/, 'POST method forwarded');
like($res->[2][0], qr/\bBODY=hello=world\b/, 'request body forwarded');
}
# ---- preserve_host --------------------------------------------------------
{
my $keep = Reverse::Proxy->new(
upstream => "http://127.0.0.1:$port", preserve_host => 1,
)->to_app;
my $res = $keep->(make_env(GET => '/', '', { Host => 'client.example' }));
like($res->[2][0], qr/\bHOST=client\.example\b/, 'preserve_host forwards client Host');
}
END { if ($pid) { local $?; kill 'TERM', $pid; waitpid $pid, 0 } }
done_testing();
( run in 1.742 second using v1.01-cache-2.11-cpan-54e63673c56 )