Use case: reverse proxying /sub/path/ to http://0:5000/other/path/. This middleware sits on the back-end and uses headers sent by the proxy to hide the proxy plumbing from the back-end app.
Plack::Middleware::ReverseProxy is a Perl module that does the host, port and scheme.
Plack::Middleware::ReverseProxyPath adds handling of paths.
The goal is to allow proxied back-end apps to reconstruct and use the client-facing url. ReverseProxy does most of the work and ReverseProxyPath does the paths. The inner app can simply use $req->base to redirect, set cookies and the like.
I find the term reverse proxy leads to confusion, so I'll use front-end to refer to the reverse proxy (eg. squid) which the client hits first, and back-end to refer to the server that runs your PSGI application (eg. starman).
Plack::Middleware::ReverseProxyPath adjusts SCRIPT_NAME and PATH_INFO based on headers from a front-end so that it's inner app can pretend there is no proxy there. This is useful when you aren't proxying and entire server, but only a deeper path. In Apache terms:
ProxyPass /mirror/foo/ http://localhost:5000/bar/
It should be used with Plack::Middleware::ReverseProxy which does equivalent adjustments to the scheme, host and port environment attributes.
SYNOPSIS
Generally you'll simple use the middle-ware:
enable "ReverseProxy";
enable "ReverseProxyPath";
Below is an elaborate example that includes both a dummy reverse proxy front-end and the back-end using ReverseProxyPath. Run with something like:
PLACK_SERVER=Starman perl -x -Ilib ./lib/Plack/Middleware/ReverseProxyPath.pm
(Sample output below)
#!perl -MPlack::Runner #line 85 sub mw(&);
use Plack::Builder;
# Configure your reverse proxy (perlbal, varnish, apache, squid)
# to send X-Forwarded-Script-Name and X-Traversal-Path headers.
# This example just uses Plack::App::Proxy to demonstrate:
sub proxy_builder {
require Plack::App::Proxy;
# imagine this is https://somehost/fepath/from
mount "http://localhost/fepath/from" => builder {
enable mw {
my ($app, $env) = @_;
# Headers for ReverseProxyPath
$env->{'HTTP_X_FORWARDED_SCRIPT_NAME'} = '/fepath/from';
$env->{'HTTP_X_TRAVERSAL_PATH'} = '/bepath/to';
# Headers for ReverseProxy (often already sent)
$env->{'HTTP_X_FORWARDED_HOST'} = 'somehost'; # pretending..
$env->{'HTTP_X_FORWARDED_PORT'} = 443;
die "Need MP" if !$env->{'psgi.multiprocess'}
&& !$env->{'psgi.multithread'};
$app->($env);
};
Plack::App::Proxy->new(
remote => 'http://0:5000/bepath/to' ) ->to_app;
};
};
# In your Plack back-end
my $app = builder {
# /bepath/to/* is proxied (can also be accessed directly)
mount "/bepath/to" => builder { # base adjustments:
# 1) http://0:5000/bepath/to/x
# ReverseProxy fixes scheme/host/port
enable "ReverseProxy";
# 2) https://somehost/bepath/to/x
# ReverseProxyPath uses new headers
# fixes SCRIPT_NAME and PATH_INFO
enable "ReverseProxyPath";
# 3) https://somehost/fepath/from/x
# $req->base + $req->path now is the client-facing url
# so URLs, Set-Cookie, Location can work naively
mount "/base" => \&echo_base;
mount "/env" => \&echo_env;
};
mount "/env" => \&echo_env;
# proxy to myself to keep the synopsis short (needs >1 worker)
proxy_builder();
};
# synopsis plumbing:
sub echo_base { require Plack::Request;
[200, [ qw(Content-type text/plain) ],
[ Plack::Request->new(shift)->base . "\n" ] ]
}
sub echo_env {
my ($env) = @_;
[200, [ qw(Content-type text/plain) ],
[ map { "$_: $env->{$_}\n" } keys %$env ] ]
}
sub mw(&) { my $code = shift;
sub { my $app = shift; sub { $code->($app, @_); } } };
Plack::Runner->new->run($app);
__END__
# with ReverseProxyPath and ReverseProxy applied
GET http://localhost:5000/fepath/from/base
https://somehost/fepath/from/base
# talking directly to the back-end
GET http://localhost:5000/bepath/to/base
http://localhost:5000/bepath/to/base
Product's homepage
Requirements:
· Perl