diff --git a/TODO b/TODO index b890d2ab5..70fd1f4e2 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,8 @@ TODO * Deprecate WebGUI::Session::HTTP - replace with WebGUI::Request/Response +* Delete lib/WebGUI/URL and replace with new Middleware(s) +* Investigate moving Cookie handling into middleware +* Turn html debug output into a Plack::Middleware::Debug panel DONE * $session->request is now a Plack::Request object @@ -7,8 +10,8 @@ DONE * WebGUI::authen API changed * urlHandler API changed - no longer gets server, config * Streaming response body -* Delete lib/WebGUI/URL and replace with new Middleware(s) * Mostly decoupled WebGUI from Log4perl +* Exception handling and error doc mapping NB * Periodically do a big stress-test and check for leaks, mysql overload etc.. diff --git a/app.psgi b/app.psgi index 0bc5e18c0..ffc1d3c83 100644 --- a/app.psgi +++ b/app.psgi @@ -3,17 +3,18 @@ use Plack::Builder; use lib '/data/WebGUI/lib'; use WebGUI; -my $wg = WebGUI->new( root => '/data/WebGUI', site => 'dev.localhost.localdomain.conf' ); +my $root = '/data/WebGUI'; +my $wg = WebGUI->new( root => $root, site => 'dev.localhost.localdomain.conf' ); builder { - enable 'Debug', panels => [ qw(Environment Response Timer Memory Session DBITrace PerlConfig Response) ]; - enable 'Log4perl', category => 'mysite', conf => $wg->config->getWebguiRoot . '/etc/log.conf'; + + enable 'Log4perl', category => 'mysite', conf => "$root/etc/log.conf"; + enable 'Static', root => $root, path => sub { s{^/\*give-credit-where-credit-is-due\*$}{docs/credits.txt} }; # Open/close the WebGUI::Session at the outer-most onion layer - enable '+WebGUI::Middleware::Session', config => $wg->config; - - # Any additional WebGUI Middleware goes here - # .. + enable '+WebGUI::Middleware::Session', + config => $wg->config;#, + #error_docs => { 500 => "$root/www/maintenance.html" }; # Return the app $wg; diff --git a/lib/WebGUI.pm b/lib/WebGUI.pm index 178fb61d2..33733f932 100644 --- a/lib/WebGUI.pm +++ b/lib/WebGUI.pm @@ -220,7 +220,7 @@ sub handle { # non-empty output should be used as the response body elsif (defined $output && $output ne "") { # Auto-set the headers - $session->http->sendHeader; # TODO: should be renamed setHeader + $session->http->sendHeader; # Use contentHandler's return value as the output $session->output->print($output); diff --git a/lib/WebGUI/Middleware/Debug.pm b/lib/WebGUI/Middleware/Debug.pm new file mode 100644 index 000000000..6098687bc --- /dev/null +++ b/lib/WebGUI/Middleware/Debug.pm @@ -0,0 +1,39 @@ +package WebGUI::Middleware::Debug; +use strict; +use parent qw(Plack::Middleware); +use Plack::Middleware::StackTrace; +use Plack::Middleware::Debug; +use Plack::Middleware::HttpExceptions; + +=head1 NAME + +WebGUI::Middleware::Debug - + +=head1 DESCRIPTION + +This is PSGI middleware for WebGUI that + +=cut + +sub call { + my ( $self, $env ) = @_; + + my $session = $env->{'webgui.session'} or die 'WebGUI::Session missing'; + + my $app = $self->app; + + if ( $session->log->canShowDebug ) { + warn 'seeing webgui.debug'; + $env->{'webgui.debug'} = 1; + $app = Plack::Middleware::StackTrace->wrap($app); + $app = Plack::Middleware::Debug->wrap( $app, + panels => [qw(Environment Response Timer Memory Session DBITrace PerlConfig Response)] ); + } + + # Turn exceptions into HTTP errors + $app = Plack::Middleware::HTTPExceptions->wrap( $app ); + + return $app->($env); +} + +1; diff --git a/lib/WebGUI/Middleware/Session.pm b/lib/WebGUI/Middleware/Session.pm index ce48b38e7..069bc3506 100644 --- a/lib/WebGUI/Middleware/Session.pm +++ b/lib/WebGUI/Middleware/Session.pm @@ -3,8 +3,13 @@ use strict; use parent qw(Plack::Middleware); use WebGUI::Config; use WebGUI::Session; +use Try::Tiny; +use Plack::Middleware::StackTrace; +use Plack::Middleware::Debug; +use Plack::Middleware::HTTPExceptions; +use Plack::Middleware::ErrorDocument; -use Plack::Util::Accessor qw( config ); +use Plack::Util::Accessor qw( config error_docs ); =head1 NAME @@ -26,13 +31,39 @@ and not worry about closing it. sub call { my ( $self, $env ) = @_; + my $app = $self->app; my $config = $self->config or die 'Mandatory config parameter missing'; - # Open the Session - $env->{'webgui.session'} = WebGUI::Session->open( $config->getWebguiRoot, $config, $env ); + my $session = try { + $env->{'webgui.session'} = WebGUI::Session->open( $config->getWebguiRoot, $config, $env ); + }; + + if (!$session) { + # We don't have access to a db connection to find out if the user is allowed to see + # a verbose error message or not, so resort to a generic Internal Server Error + # (using the error_docs mapping) + return Plack::Middleware::ErrorDocument->wrap( + sub { [ 500, [], [] ] }, + %{ $self->error_docs } )->($env); + } + + my $debug = $session->log->canShowDebug; + if ($debug) { + $app = Plack::Middleware::StackTrace->wrap($app); + $app = Plack::Middleware::Debug->wrap( $app, + panels => [qw(Environment Response Timer Memory Session DBITrace PerlConfig Response)] ); + } + + # Turn exceptions into HTTP errors + $app = Plack::Middleware::HTTPExceptions->wrap($app); + + # HTTP error document mapping + if ( !$debug && $self->error_docs ) { + $app = Plack::Middleware::ErrorDocument->wrap( $app, %{ $self->error_docs } ); + } # Run the app - my $res = $self->app->($env); + my $res = $app->($env); # Use callback style response return $self->response_cb( diff --git a/lib/WebGUI/Session/ErrorHandler.pm b/lib/WebGUI/Session/ErrorHandler.pm index abb6d3c08..783247567 100644 --- a/lib/WebGUI/Session/ErrorHandler.pm +++ b/lib/WebGUI/Session/ErrorHandler.pm @@ -19,6 +19,7 @@ use strict; use JSON; use HTML::Entities qw(encode_entities); use Log::Log4perl; +use WebGUI::Exception; =head1 NAME @@ -221,33 +222,43 @@ sub fatal { my $message = shift; local $Log::Log4perl::caller_depth = $Log::Log4perl::caller_depth + 2; - $self->session->http->setStatus("500","Server Error"); - $self->session->response->content_type('text/html') if ($self->session->response); $self->getLogger->({ level => 'fatal', message => $message }); $self->getLogger->({ level => 'debug', message => "Stack trace for FATAL ".$message."\n".$self->getStackTrace() }); - $self->session->http->sendHeader if ($self->session->response); + my $error; if (! defined $self->session->db(1)) { # We can't even _determine_ whether we can show the debug text. Punt. - $self->session->output->print("
Something unexpected happened that caused this system to fault.
\n",1); - $self->session->output->print("".$message."
\n",1); - $self->session->output->print("" . encode_entities($self->getStackTrace) . "", 1); - $self->session->output->print($self->showDebug(),1); + my $stack = encode_entities($self->getStackTrace); + my $debug = $self->showDebug(); + $error = <
Something unexpected happened that caused this system to fault.
+$message
+$stack+$debug +END_HTML } else { # NOTE: You can't internationalize this because with some types of errors that would cause an infinite loop. - $self->session->output->print("