From 319c23026512f3c4dc64507a4db4d769ce0bfb06 Mon Sep 17 00:00:00 2001 From: Russell Jenkins Date: Tue, 26 Jun 2018 07:51:52 +0400 Subject: [PATCH 1/2] Config and code for not adding default middleware If `no_default_middleware` is true, do NOT wrap the app in * Plack::Middleware::FixMissingBodyInRedirect * Plack::Middleware::Head This allows anyone who needs to manipulate response headers based on the response body in middleware to do so, before middleware layers like Plack::Middleware::Head are applied. These default middleware layers can then be applied as part of the app's PSGI stack (not part of Dancer2's internals). Tests included! --- lib/Dancer2/Core/App.pm | 16 ++++++----- t/no_default_middleware.t | 59 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 t/no_default_middleware.t diff --git a/lib/Dancer2/Core/App.pm b/lib/Dancer2/Core/App.pm index 68ac038ce..98494c767 100644 --- a/lib/Dancer2/Core/App.pm +++ b/lib/Dancer2/Core/App.pm @@ -1397,11 +1397,7 @@ sub to_app { return $response; }; - # Wrap with common middleware - # FixMissingBodyInRedirect - $psgi = Plack::Middleware::FixMissingBodyInRedirect->wrap( $psgi ); - - # Only add static content handler if requires + # Only add static content handler if required if ( $self->config->{'static_handler'} ) { # Use App::File to "serve" the static content my $static_app = Plack::App::File->new( @@ -1417,8 +1413,14 @@ sub to_app { ); } - # Apply Head. After static so a HEAD request on static content DWIM. - $psgi = Plack::Middleware::Head->wrap( $psgi ); + # Wrap with common middleware + if ( ! $self->config->{'no_default_middleware'} ) { + # FixMissingBodyInRedirect + $psgi = Plack::Middleware::FixMissingBodyInRedirect->wrap( $psgi ); + # Apply Head. After static so a HEAD request on static content DWIM. + $psgi = Plack::Middleware::Head->wrap( $psgi ); + } + return $psgi; } diff --git a/t/no_default_middleware.t b/t/no_default_middleware.t new file mode 100644 index 000000000..8b91d216a --- /dev/null +++ b/t/no_default_middleware.t @@ -0,0 +1,59 @@ +use strict; +use warnings; +use Test::More; +use Plack::Test; +use Plack::Builder; +use HTTP::Request::Common; + +use Plack::Middleware::Head; +use Plack::Middleware::FixMissingBodyInRedirect; + +# No default middleware wrappers + +{ + package MyTestApp; + use Dancer2; + set no_default_middleware => 1; + + get '/' => sub { return 'some content' }; + + get '/redirect' => sub { redirect '/' }; +} + + +subtest 'Head' => sub { + my $plain = Plack::Test->create( MyTestApp->to_app ); + my $res = $plain->request( HEAD '/' ); + ok( length( $res->content ) > 0, 'HEAD request on unwrapped app has content' ); + + my $test = Plack::Test->create( + builder { + enable 'Head'; + MyTestApp->to_app; + } + ); + my $response = $test->request( HEAD '/' ); + is( length( $response->content ), 0, 'HEAD request on wrapped app has no content' ); + + is( $res->header('Content-Length'), + $response->header('Content-Length'), + 'HEAD requests have consistent content length header' + ); +}; + +subtest 'FixMissingBodyInRedirect' => sub { + my $plain = Plack::Test->create( MyTestApp->to_app ); + my $res = $plain->request( GET '/redirect' ); + is( length( $res->content ), 0, 'GET request that redirects on unwrapped app has no content' ); + + my $test = Plack::Test->create( + builder { + enable 'FixMissingBodyInRedirect'; + MyTestApp->to_app; + } + ); + my $response = $test->request( GET '/redirect' ); + ok( length( $response->content ) > 0, 'GET request that redirects on wrapped app has content' ); +}; + +done_testing; From d8182a4a4af8d9863a1a6b05951b48f78358730f Mon Sep 17 00:00:00 2001 From: Russell Jenkins Date: Tue, 26 Jun 2018 08:23:44 +0400 Subject: [PATCH 2/2] Document no_default_middleware setting --- lib/Dancer2/Config.pod | 13 +++++++++++++ lib/Dancer2/Manual.pod | 33 ++++++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/lib/Dancer2/Config.pod b/lib/Dancer2/Config.pod index 720b45fee..f8140b3f9 100644 --- a/lib/Dancer2/Config.pod +++ b/lib/Dancer2/Config.pod @@ -172,6 +172,19 @@ hard-configured in the proxy if possible. For Apache this would be: RequestHeader set X_FORWARDED_PROTO "https" RequestHeader set X_FORWARDED_HOST "www.example.com" +=head3 no_default_middleware (boolean) + +If set to true, your Dancer2 application will B be wrapped with the default +PSGI middleware. The default middleware wrappers are: + +=over 4 + +=item * L + +=item * L + +=back + =head2 Content type / character set =head3 content_type (string) diff --git a/lib/Dancer2/Manual.pod b/lib/Dancer2/Manual.pod index 2f72abce8..587faf549 100644 --- a/lib/Dancer2/Manual.pod +++ b/lib/Dancer2/Manual.pod @@ -1932,7 +1932,6 @@ If you want to use Plack middlewares, you need to enable them using L as such: # in app.psgi or any other handler - use Dancer2; use MyApp; use Plack::Builder; @@ -1940,7 +1939,7 @@ L as such: enable 'Deflater'; enable 'Session', store => 'File'; enable 'Debug', panels => [ qw ]; - dance; + MyApp->to_app; }; The nice thing about this setup is that it will work seamlessly through @@ -1960,17 +1959,41 @@ If you want to set up a middleware for a specific path, you can do that using L which uses L: # in your app.psgi or any other handler - use Dancer2; use MyApp; use Plack::Builder; my $special_handler = sub { ... }; builder { - mount '/' => dance; mount '/special' => $special_handler; + mount '/' => MyApp->to_app; }; +=head3 Removing default middlewares + +By default, a Dancer2 app is automatically wrapped with the following middleware + +=over 4 + +=item * L + +=item * L + +=back + +You can configure the setting C to a true value to stop your +Dancer2 app being wrapped with these default middleware layers. + + # in you Dancer2 app or config.yml + package MyApp; + use Dancer2 + + set no_default_middleware => true; + +This is necessary if you need to add eTag or ContentMD5 headers to +C requests, and you are encouraged to manually add those default +middleware back into your PSGI stack. + =head3 Running on Perl web servers with plackup A number of Perl web servers supporting PSGI are available on CPAN: @@ -2019,7 +2042,7 @@ F) edit it to use L, as described above: builder { enable 'Deflater'; - dance; + MyApp->to_app; }; To test if content compression works, trace the HTTP request and response