From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

use Any::Moose '::Util::TypeConstraints';
subtype 'HTTP::Engine::Middleware::ReverseProxy::Regexp'
=> as 'Regexp';
coerce 'HTTP::Engine::Middleware::ReverseProxy::Regexp'
=> from 'Str' => via {
qr/\A$_[0]\z/;
};
has 'allowed_remote' => (
is => 'rw',
isa => 'HTTP::Engine::Middleware::ReverseProxy::Regexp',
default => '127\.0\.0\.1',
coerce => 1,
);
before_handle {
my ( $c, $self, $req ) = @_;
return $req unless $req->address =~ $self->allowed_remote;
my $env = $req->_connection->{env} || {};
# in apache httpd.conf (RequestHeader set X-Forwarded-HTTPS %{HTTPS}s)
$env->{HTTPS} = $req->headers->{'x-forwarded-https'}
if $req->headers->{'x-forwarded-https'};
$env->{HTTPS} = 'ON' if $req->headers->{'x-forwarded-proto'}; # Pound
my $secure = 0;
if ( my $https = $env->{HTTPS} ) {
$secure = 1 if $https =~ /\AON\z/i;
}
$req->secure($secure);
my $default_port = $req->secure ? 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 ( $req->headers->{'x-forwarded-for'} ) {
my ( $ip, ) = $req->headers->{'x-forwarded-for'} =~ /([^,\s]+)$/;
$req->address($ip);
}
if ( $req->headers->{'x-forwarded-host'} ) {
my ( $host, ) = $req->headers->{'x-forwarded-host'} =~ /([^,\s]+)$/;
if ( $host =~ /^(.+):(\d+)$/ ) {
$host = $1;
$env->{SERVER_PORT} = $2;
} elsif ( $req->headers->{'x-forwarded-port'} ) {
# in apache httpd.conf (RequestHeader set X-Forwarded-Port 8443)
$env->{SERVER_PORT} = $req->headers->{'x-forwarded-port'};
} else {
$env->{SERVER_PORT} = $default_port;
}
$env->{HTTP_HOST} = $host;
$req->headers->header( 'Host' => $env->{HTTP_HOST} );
} elsif ($req->headers->{'host'}) {
my $host = $req->headers->{'host'};
if ($host =~ /^(.+):(\d+)$/ ) {
$env->{HTTP_HOST} = $1;
$env->{SERVER_PORT} = $2;
} elsif ($host =~ /^(.+)$/ ) {
$env->{HTTP_HOST} = $1;
$env->{SERVER_PORT} = $default_port;
}
} else {
$env->{HTTP_HOST} = $req->uri->host;
$env->{SERVER_PORT} = $req->uri->port || $default_port;
}
$req->_connection->{env} = $env;
for my $attr (qw/uri base/) {
my $scheme = $secure ? 'https' : 'http';
my $host = $env->{HTTP_HOST} || $env->{SERVER_NAME};
my $port = $env->{SERVER_PORT} || undef;
# my $path_info = $env->{PATH_INFO} || '/';
$req->$attr->scheme($scheme);
$req->$attr->host($host);
if (($port || '') eq $default_port) {
$req->$attr->port(undef);
} else {
$req->$attr->port($port);
}
# $req->$attr->path($path_info);
# $req->$attr( $req->$attr->canonical );
}
$req;
};
__MIDDLEWARE__
__END__
=head1 NAME
HTTP::Engine::Middleware::ReverseProxy - reverse-proxy support
=head1 SYNOPSIS
# default proxy server is 127.0.0.1
my $mw = HTTP::Engine::Middleware->new;
$mw->install(qw/ HTTP::Engine::Middleware::ReverseProxy /);
HTTP::Engine->new(
interface => {
module => 'YourFavoriteInterfaceHere',
request_handler => $mw->handler( \&handler ),
}
)->run();
# allowd proxy server is 192.168.0.0/24
my $mw = HTTP::Engine::Middleware->new;
$mw->install( 'HTTP::Engine::Middleware::ReverseProxy', { allowed_remote => qr/\A192\.168\.0\.\d+\z/ } );
# or $mw->install( 'HTTP::Engine::Middleware::ReverseProxy', { allowed_remote => '192\.168\.0\.\d+' } );
HTTP::Engine->new(
interface => {
module => 'YourFavoriteInterfaceHere',
request_handler => $mw->handler( \&handler ),
}
)->run();
=head1 DESCRIPTION
This module resets some HTTP headers, which changed by reverse-proxy.
=head1 AUTHORS
yappo
=cut