Skip to content

Commit

Permalink
copy from HTTP::Engine::Middleware::ReverseProxy
Browse files Browse the repository at this point in the history
  • Loading branch information
nobuo-danjou committed Nov 29, 2009
1 parent 9373efd commit 3cbf5f3
Show file tree
Hide file tree
Showing 2 changed files with 225 additions and 26 deletions.
74 changes: 48 additions & 26 deletions lib/Plack/Middleware/ReverseProxy.pm
Expand Up @@ -2,32 +2,54 @@ package Plack::Middleware::ReverseProxy;

use strict;
use warnings;
our $VERSION = '0.01';
use Carp;
use parent qw(Plack::Middleware);

sub call {
my $self = shift;
my $env = shift;

# in apache httpd.conf (RequestHeader set X-Forwarded-HTTPS %{HTTPS}s)
$env->{HTTPS} = $env->{'HTTP_X_FORWARDED_HTTPS'}
if $env->{'HTTP_X_FORWARDED_HTTPS'};
$env->{HTTPS} = 'ON' if $env->{'HTTP_X_FORWARDED_PROTO'}; # Pound
$env->{'psgi.url_scheme'} = 'https' if $env->{HTTPS} && uc $env->{HTTPS} eq 'ON';
my $default_port = $env->{'psgi.url_scheme'} eq 'https' ? 443 : 80;

# If we are running as a backend server, the user will always appear
# as 127.0.0.1. Select the most recent upstream IP (last in the list)
if ( $env->{'HTTP_X_FORWARDED_FOR'} ) {
my ( $ip, ) = $env->{HTTP_X_FORWARDED_FOR} =~ /([^,\s]+)$/;
$env->{REMOTE_ADDR} = $ip;
}

if ( $env->{HTTP_X_FORWARDED_HOST} ) {
my $host = $env->{HTTP_X_FORWARDED_HOST};
if ( $host =~ /^(.+):(\d+)$/ ) {
$host = $1;
$env->{SERVER_PORT} = $2;
} elsif ( $env->{HTTP_X_FORWARDED_PORT} ) {
# in apache httpd.conf (RequestHeader set X-Forwarded-Port 8443)
$env->{SERVER_PORT} = $env->{HTTP_X_FORWARDED_PORT};
} else {
$env->{SERVER_PORT} = $default_port;
}
$env->{HTTP_HOST} = $host;

} elsif ( $env->{HTTP_HOST} ) {
my $host = $env->{HTTP_HOST};
if ($host =~ /^(.+):(\d+)$/ ) {
$env->{HTTP_HOST} = $1;
$env->{SERVER_PORT} = $2;
} elsif ($host =~ /^(.+)$/ ) {
$env->{HTTP_HOST} = $1;
$env->{SERVER_PORT} = $default_port;
}
}

$self->app->($env);
}

1;
__END__
=head1 NAME
Plack::Middleware::ReverseProxy -
=head1 SYNOPSIS
use Plack::Middleware::ReverseProxy;
=head1 DESCRIPTION

Plack::Middleware::ReverseProxy is
=head1 AUTHOR
Noubo Danjou E<lt>nobuo.danjou@gmail.comE<gt>
=head1 SEE ALSO
=head1 LICENSE
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
1;

=cut
177 changes: 177 additions & 0 deletions t/reverseproxy.t
@@ -0,0 +1,177 @@
use strict;
use warnings;
use Test::Base;
plan tests => 25*2;

use Plack::Builder;
use Plack::Test;
use Plack::Request;
use HTTP::Headers;
use HTTP::Request;

filters { input => [qw/yaml/] };

run {
my $block = shift;
local %ENV = ();
$ENV{REQUEST_METHOD} = 'GET';
$ENV{SERVER_PORT} = 80;
$ENV{HTTP_HOST} = 'example.com';
$ENV{QUERY_STRING} = 'foo=bar';
$ENV{HTTPS} = delete $block->input->{https} if $block->input->{https};

my $headers = HTTP::Headers->new;
$headers->header( %{ $block->input } );

my %args;
my $code = sub {
my $error = shift;
my $handler = builder {
enable 'Plack::Middleware::ReverseProxy';
sub {
my $req = Plack::Request->new(shift);
for my $attr (qw/url_scheme address/) {
if ( $block->$attr ) {
if ($error && $block->is_secure_error) {
isnt( $req->$attr, $block->$attr, $block->name . " of $attr isnt" );
} else {
is( $req->$attr, $block->$attr, $block->name . " of $attr" );
}
}
}
for my $url (qw/uri base /) {
if ( $block->$url ) {
if ($error && $block->is_url_error) {
isnt( $req->$url->as_string, $block->$url, $block->name . " of $url isnt" );
} else {
is( $req->$url->as_string, $block->$url, $block->name . " of $url" );
}
}
}
[200, ['Content-Type' => 'text/plain'], [ 'OK' ]];
}
};

my %test = (
client => sub {
my $cb = shift;
my $res = $cb->(
HTTP::Request->new(
GET => 'http://example.com/?foo=bar', $headers,
%args,
)
);
},
app => $handler,
);

test_psgi %test;
};

# allow host
$args{address} = $ENV{REMOTE_ADDR} = $block->allow_host || '127.0.0.1';
$code->();
# deny host
$args{address} = $ENV{REMOTE_ADDR} = $block->deny_host || '0.0.0.0';
$code->(1);
};

__END__
=== with https
--- input
x-forwarded-https: on
--- url_scheme: https
--- base: https://example.com:80/
--- uri: https://example.com:80/?foo=bar
--- deny_host: 10.0.0.1
--- is_secure_error: 1
--- is_url_error: 1
=== without https
--- input
x-forwarded-https: off
--- url_scheme: http
--- base: http://example.com/
--- uri: http://example.com/?foo=bar
--- allowed_remote: 192.168.0.1
--- allow_host: 192.168.0.1
--- deny_host: 10.0.0.1
--- is_secure_error: 1
===
--- input
dummy: 1
--- url_scheme: http
--- base: http://example.com/
--- uri: http://example.com/?foo=bar
--- allowed_remote: 192.168.0.\d+
--- allow_host: 192.168.0.99
--- deny_host: 10.0.0.1
=== https with HTTP_X_FORWARDED_PROTO
--- input
x-forwarded-proto: https
--- secure: 1
--- base: https://example.com:80/
--- uri: https://example.com:80/?foo=bar
--- allowed_remote: 192.168.0.\d
--- allow_host: 192.168.0.1
--- deny_host: 192.168.0.11
--- is_secure_error: 1
--- is_url_error: 1
=== with HTTP_X_FORWARDED_FOR
--- input
x-forwarded-for: 192.168.3.2
--- address: 192.168.3.2
--- base: http://example.com/
--- uri: http://example.com/?foo=bar
--- is_secure_error: 1
=== with HTTP_X_FORWARDED_HOST
--- input
x-forwarded-host: 192.168.1.2:5235
--- base: http://192.168.1.2:5235/
--- uri: http://192.168.1.2:5235/?foo=bar
--- is_url_error: 1
=== default port with HTTP_X_FORWARDED_HOST
--- input
x-forwarded-host: 192.168.1.2
--- base: http://192.168.1.2/
--- uri: http://192.168.1.2/?foo=bar
--- is_url_error: 1
=== default https port with HTTP_X_FORWARDED_HOST
--- input
x-forwarded-https: on
x-forwarded-host: 192.168.1.2
--- base: https://192.168.1.2/
--- uri: https://192.168.1.2/?foo=bar
--- is_secure_error: 1
--- is_url_error: 1
=== default port with HOST
--- input
host: 192.168.1.2
--- base: http://192.168.1.2/
--- uri: http://192.168.1.2/?foo=bar
--- is_url_error: 1
=== default https port with HOST
--- input
host: 192.168.1.2
https: ON
--- base: https://192.168.1.2/
--- uri: https://192.168.1.2/?foo=bar
--- is_secure_error: 1
--- is_url_error: 1
=== with HTTP_X_FORWARDED_HOST and HTTP_X_FORWARDED_PORT
--- input
x-forwarded-host: 192.168.1.5
x-forwarded-port: 1984
--- base: http://192.168.1.5:1984/
--- uri: http://192.168.1.5:1984/?foo=bar
--- is_url_error: 1

0 comments on commit 3cbf5f3

Please sign in to comment.