1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
package Plack::Middleware::Proxy::RewriteLocation;
use strict;
use parent 'Plack::Middleware';
use Plack::Util;
use Plack::Util::Accessor 'url_map';
use URI;
sub _different_part($$) {
my ($from, $to) = @_;
while ($from =~ m{[^/]+(?:\://$|/$|$)}g) {
my $last_part = $&;
last unless $to =~ /\Q$last_part\E$/;
$from =~ s!\Q$last_part\E$!!;
$to =~ s!\Q$last_part\E$!!;
}
$from => $to;
}
sub new {
my $self = shift->SUPER::new( @_ );
# regularize the remote URLs in the URL map
if( my $m = $self->url_map ) {
for( my $i = 1; $i < @$m; $i += 2 ) {
$m->[$i] = $self->_regularize_url( $m->[$i] );
}
}
return $self;
}
sub call {
my($self, $env) = @_;
return sub {
my $respond = shift;
my $cb = $self->app->($env);
return $respond->( $cb ) unless ref $cb eq 'CODE';
$cb->(sub {
my $res = shift;
if ( $env->{HTTP_HOST} and my $location = Plack::Util::header_get($res->[1], 'Location') ) {
my @map;
if ($self->url_map) {
# regularize the format of the location so we can
# compare it correctly (some apps print this
# non-canonically)
$location = $self->_regularize_url( $location );
my $proxy = "$env->{'psgi.url_scheme'}://$env->{HTTP_HOST}";
my @url_map = @{$self->url_map};
while(my ($proxy_path, $remote) = splice @url_map, 0, 2) {
push @map, "$proxy$proxy_path" => $remote;
}
} else {
# Auto-guessing url_map
my $original_url = "$env->{'psgi.url_scheme'}://" .
$env->{HTTP_HOST} .
$env->{SCRIPT_NAME} .
$env->{PATH_INFO};
$original_url .= '?' . $env->{QUERY_STRING}
if defined $env->{QUERY_STRING} && $env->{QUERY_STRING};
@map = _different_part(
$original_url => $env->{'plack.proxy.last_url'}
);
}
while(my ($proxy_url, $remote) = splice @map, 0, 2) {
last if $location =~ s!^$remote!$proxy_url!;
}
$location =~ s!//$!/!; #< avoid double slashes
Plack::Util::header_set( $res->[1], 'Location' => $location );
}
return $respond->( $res );
});
};
}
sub _regularize_url {
'' . URI->new( $_[1] )->canonical
}
1;
__END__
=head1 NAME
Plack::Middleware::Proxy::RewriteLocation - Rewrites redirect headers
=head1 SYNOPSIS
use Plack::Builder;
use Plack::App::Proxy;
builder {
enable "Proxy::RewriteLocation";
Plack::App::Proxy->new(remote => "http://10.0.1.2:8080/")->to_app;
};
### or, if mounting (i.e. URLMap) the proxied site at /foo
builder {
enable "Proxy::RewriteLocation", url_map => [ '/foo' => http://10.0.1.2:8080' ];
mount '/foo' => Plack::App::Proxy->new(remote => "http://10.0.1.2:8080/")->to_app;
};
=head1 DESCRIPTION
Plack::Middleware::Proxy::RewriteLocation rewrites the C<Location>
header in the response when the remote host redirects using its own
headers, like mod_proxy's C<ProxyPassReverse> option.
=head1 OPTIONS
=over 4
=item url_map (arrayref)
If given, will account for mounted (URLMapped) Proxy apps when
rewriting C<Location> headers. Will be applied in order, stopping at the
first successful match with the remote C<Location>.
=back
=head1 AUTHOR
Tatsuhiko Miyagawa
Robert Buels
=head1 SEE ALSO
L<Plack::App::Proxy>
=cut
|