From d172de8eea8fe08c317b32f6d6c6dd5e747e8fd1 Mon Sep 17 00:00:00 2001 From: Russell Jenkins Date: Thu, 22 May 2014 22:13:37 +1000 Subject: [PATCH] Plack middleware for request header munging when behind a reverse proxy Uses existing plack middlewares (ReverseProxy and ReverseProxyPath) to handle all request header modification when operating behing a proxy. Allows for variants (eg HTTP_X_FORWARDED_PROTOCOL) supported by Dancer(2) but not from those existing ReverseProxy middleware. Special cases REQUEST_BASE header to work with ReverseProxyPath so proxies from/to non-root paths "just work"(tm). (Alternate to #571.) As a middleware, devs get more flexability as to where to apply it; they can use Plack::Builder to wrap this around their app as well as further path/header altering middleware. This could be released as a seperate package; its not Dancer2 specific. --- lib/Dancer2/Middleware/BehindProxy.pm | 58 +++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 lib/Dancer2/Middleware/BehindProxy.pm diff --git a/lib/Dancer2/Middleware/BehindProxy.pm b/lib/Dancer2/Middleware/BehindProxy.pm new file mode 100644 index 000000000..a4adf00ff --- /dev/null +++ b/lib/Dancer2/Middleware/BehindProxy.pm @@ -0,0 +1,58 @@ +package Dancer2::Middleware::BehindProxy; +# ABSTRACT: Support Dancer2 apps when operating behing a reverse proxy + +use warnings; +use strict; + +use parent 'Plack::Middleware'; +use Plack::Middleware::ReverseProxy; +use Plack::Middleware::ReverseProxyPath; + +sub call { + my($self, $env) = @_; + + # Plack::Middleware::ReverseProxy only supports + # HTTP_X_FORWARDED_PROTO whereas Dancer2 also supports + # HTTP_X_FORWARDED_PROTOCOL and HTTP_FORWARDED_PROTO + for my $header (qw/HTTP_X_FORWARDED_PROTOCOL HTTP_FORWARDED_PROTO/) { + if ( ! $env->{HTTP_X_FORWARDED_PROTO} + && $env->{$header} ) + { + $env->{HTTP_X_FORWARDED_PROTO} = $env->{$header}; + last; + } + } + + # Pr#503 added support for HTTP_X_FORWARDED_HOST containing multiple + # values. Plack::Middleware::ReverseProxy takes the last (most recent) + # whereas that #503 takes the first. + if ( $env->{HTTP_X_FORWARDED_HOST} ) { + my @hosts = split /\s*,\s*/, $env->{HTTP_X_FORWARDED_HOST}, 2; + $env->{HTTP_X_FORWARDED_HOST} = $hosts[0]; + } + + # Plack::Middleware::ReverseProxyPath uses X-Forwarded-Script-Name + # whereas Dancer previously supported HTTP_REQUEST_BASE + if ( ! $env->{HTTP_X_FORWARDED_SCRIPT_NAME} + && $env->{HTTP_REQUEST_BASE} ) + { + $env->{HTTP_X_FORWARDED_SCRIPT_NAME} = $env->{HTTP_REQUEST_BASE}; + } + + # Wrap in reverse proxy middleware and call the wrapped app + my $app = Plack::Middleware::ReverseProxyPath->wrap($self->app); + $app = Plack::Middleware::ReverseProxy->wrap($app); + return $app->($env); +} + +1; + +__END__ + +=head1 DESCRIPTION + +Modifies request headers supported by L altered by reverse proxies before +wraping the request in the commonly used reverse proxy PSGI middlewares; +L and L. + +=cut