From b7e7d5b936c9be0e3ffb51a10f1ec26727863689 Mon Sep 17 00:00:00 2001 From: Patrick Donelan Date: Thu, 8 Apr 2010 21:30:55 -0400 Subject: [PATCH] Refactored Request/Response into WebGUI::Session:: --- lib/WebGUI.pm | 82 ++++++++++++++-------------- lib/WebGUI/Request.pm | 33 ----------- lib/WebGUI/Session.pm | 38 ++++++++----- lib/WebGUI/Session/Request.pm | 40 ++++++++++++++ lib/WebGUI/{ => Session}/Response.pm | 14 ++++- 5 files changed, 117 insertions(+), 90 deletions(-) delete mode 100644 lib/WebGUI/Request.pm create mode 100644 lib/WebGUI/Session/Request.pm rename lib/WebGUI/{ => Session}/Response.pm (56%) diff --git a/lib/WebGUI.pm b/lib/WebGUI.pm index 344f88aa0..c8697e678 100644 --- a/lib/WebGUI.pm +++ b/lib/WebGUI.pm @@ -25,7 +25,7 @@ use WebGUI::Config; use WebGUI::Pluggable; use WebGUI::Session; use WebGUI::User; -use WebGUI::Request; +use WebGUI::Session::Request; use Moose; use Try::Tiny; @@ -81,53 +81,51 @@ sub psgi_app { return $self->{psgi_app} ||= $self->compile_psgi_app; } -sub new_session { - my $self = shift; - my $request = shift; - - # determine session id - my $sessionId = $request->cookies->{$self->config->getCookieName}; - - # Instantiate the session object - return WebGUI::Session->open($self->root, $self->config, $request, $sessionId); -} - sub compile_psgi_app { my $self = shift; my $catch = [ 500, [ 'Content-Type' => 'text/plain' ], [ "Internal Server Error\n" ] ]; + # WebGUI is a PSGI app is a Perl code reference. Let's create one. + # Each web request results in a call to this sub my $app = sub { my $env = shift; - my $log = sub { - $env->{'psgi.errors'}->print(join '', @_, "\n"); - }; - + # Use the PSGI callback style response, which allows for nice things like + # delayed response/streaming body (server push). For now we just use this for + # unbuffered response writing return sub { - my $callback = shift; - my $request = WebGUI::Request->new($env); - my $session = $self->new_session($request); + my $responder = shift; - try { - $self->handle($request); - } catch { - $log->( "Error handling request: $_" ); - $callback->( $catch ); - return; - }; + # Open the WebGUI Session + # my $session = WebGUI::Session->open($self->root, $self->config, $env, $env->{'psgix.session'}->id); + my $session = WebGUI::Session->open($self->root, $self->config, $env); + # Handle the request + $self->handle($session); + + # Uncomment to catch errors (currently I prefer letting StackTrace do its thing) + # try { + # $self->handle($session); + # } catch { + # $session->request->log( "Error handling request: $_" ); + # $responder->( $catch ); + # return; + # }; + + # Construct the PSGI response my $response = $session->response; my $psgi_response = $response->finalize; + # See if the content handler is doing unbuffered response writing if ( $response->streaming ) { try { - # Ask PSGI server for a streaming writer object by returning a 2-part - # arrayref instead of a 3-part array - my $writer = $callback->( [ $psgi_response->[0], $psgi_response->[1] ] ); + # Ask PSGI server for a streaming writer object by returning only the first + # two elements of the array reference + my $writer = $responder->( [ $psgi_response->[0], $psgi_response->[1] ] ); - # Store the writer object in the WebGUI::Response object + # Store the writer object in the WebGUI::Session::Response object $response->writer($writer); # Now call the callback that does the streaming @@ -139,13 +137,15 @@ sub compile_psgi_app { } catch { if ($response->writer) { # Response has already been started, so log error and close writer - $log->("Error detected after streaming response started"); + $session->request->TRACE("Error detected after streaming response started"); $response->writer->close; } else { - $callback->( $catch ); + $responder->( $catch ); } + } finally { $session->close; + }; } else { @@ -153,7 +153,7 @@ sub compile_psgi_app { # the response. In the future we could use an Event framework here # to make this a non-blocking delayed response. $session->close; - $callback->($psgi_response); + $responder->($psgi_response); } } }; @@ -176,18 +176,20 @@ sub compile_psgi_app { path => sub { s{^$uploadsURL/}{} }, root => "$uploadsPath/", ); + + # Session - TODO: make this user configurable + # use Plack::Middleware::Session; + # $app = Plack::Middleware::Session->wrap($app); return $app; } sub handle { - my ( $self, $request ) = @_; - - my $session = $request->session; + my ( $self, $session ) = @_; # uncomment the following to short-circuit contentHandlers (for benchmarking PSGI scaffolding vs. modperl) -# $session->output->print("WebGUI PSGI with contentHandlers short-circuited for benchmarking\n"); -# return; + # $session->output->print("WebGUI PSGI with contentHandlers short-circuited for benchmarking\n"); + # return; # contentHandlers that return text will have that content returned as the response # Alternatively, contentHandlers can stream the response body by calling: @@ -203,7 +205,7 @@ sub handle { # sleep 1; # $session->output->print("...see?\n"); # }); -# return; +# return; # TODO: refactor the following loop, find all instances of "chunked" and "empty" in codebase, etc.. for my $handler (@{$self->config->get("contentHandlers")}) { @@ -224,7 +226,7 @@ sub handle { # "chunked" or "empty" means it took care of its own output needs if (defined $output && ( $output eq "chunked" || $output eq "empty" )) { - warn "chunked and empty no longer stream, use session->response->stream() instead"; + #warn "chunked and empty no longer stream, use session->response->stream() instead"; if ($session->errorHandler->canShowDebug()) { $session->output->print($session->errorHandler->showDebug(),1); } diff --git a/lib/WebGUI/Request.pm b/lib/WebGUI/Request.pm deleted file mode 100644 index 0d3c9bc06..000000000 --- a/lib/WebGUI/Request.pm +++ /dev/null @@ -1,33 +0,0 @@ -package WebGUI::Request; - -=head2 DESCRIPTION - -The WebGUI server response object. See L - -=cut - -use strict; -use parent qw(Plack::Request); -use Plack::Util::Accessor qw(session); -use WebGUI::Response; - -=head1 METHODS - -=head2 new_response () - -Creates a new L object. - -N.B. A L object is automatically created when L -is instantiated, so in most cases you will not need to call this method. -See L - -=cut - -sub new_response { - my $self = shift; - my $response = WebGUI::Response->new(@_); - $response->session($self->session); - return $response; -} - -1; \ No newline at end of file diff --git a/lib/WebGUI/Session.pm b/lib/WebGUI/Session.pm index a012560c0..d60538350 100644 --- a/lib/WebGUI/Session.pm +++ b/lib/WebGUI/Session.pm @@ -29,6 +29,7 @@ use WebGUI::Session::Id; use WebGUI::Session::Os; use WebGUI::Session::Output; use WebGUI::Session::Privilege; +use WebGUI::Session::Request; use WebGUI::Session::Scratch; use WebGUI::Session::Setting; use WebGUI::Session::Stow; @@ -424,7 +425,7 @@ sub log { #------------------------------------------------------------------- -=head2 open ( webguiRoot, configFile [, requestObject, sessionId, noFuss ] ) +=head2 open ( webguiRoot, configFile [, env, sessionId, noFuss ] ) Constructor. Opens a closed ( or new ) WebGUI session. @@ -436,13 +437,14 @@ The path to the WebGUI files. The filename of the config file that WebGUI should operate from, or a WebGUI::Config object -=head3 requestObject +=head3 env -The Plack::Request object. If this session is being instanciated from the web, this is required. +The L env hash. If this session is being instanciated from the web, this is required. =head3 sessionId Optionally retrieve a specific session id. Normally this is set by a cookie in the user's browser. +If you have a L env hash, you might find the sessionId at: $env->{'psgix.session'}->id =head3 noFuss @@ -451,21 +453,29 @@ Uses simple session vars. See WebGUI::Session::Var::new() for more details. =cut sub open { - my $class = shift; - my $webguiRoot = shift; - my $c = shift; - my $request = shift; + my ($class, $webguiRoot, $c, $env, $sessionId, $noFuss) = @_; my $config = ref $c ? $c : WebGUI::Config->new($webguiRoot,$c); my $self = {_config=>$config }; # TODO - if we store reference here, should we weaken WebGUI->config? - bless $self , $class; - if (defined $request) { - $request->session($self); # hello circular reference - $self->{_request} = $request; + bless $self, $class; + + if ($env) { + my $request = WebGUI::Session::Request->new($env); + $self->{_request} = $request; $self->{_response} = $request->new_response( 200 ); + + # TODO: it might be nice to set a default Content-type here, but we can't until Assets can override it again + # $self->{_response} = $request->new_response( 200 );#, [ 'Content-type' => 'text/html; charset=UTF-8' ] ); + + # Use the WebGUI::Session::Request object to look up the sessionId from cookies, if it + # wasn't given explicitly + $sessionId ||= $request->cookies->{$config->getCookieName}; } - my $sessionId = shift || $request->cookies->{$config->getCookieName} || $self->id->generate; - $sessionId = $self->id->generate unless $self->id->valid($sessionId); - my $noFuss = shift; + + # If the sessionId is still unset or is invalid, generate a new one + if (!$sessionId || !$self->id->valid($sessionId)) { + $sessionId = $self->id->generate; + } + $self->{_var} = WebGUI::Session::Var->new($self,$sessionId, $noFuss); return $self; } diff --git a/lib/WebGUI/Session/Request.pm b/lib/WebGUI/Session/Request.pm new file mode 100644 index 000000000..2a9113529 --- /dev/null +++ b/lib/WebGUI/Session/Request.pm @@ -0,0 +1,40 @@ +package WebGUI::Session::Request; +use strict; +use parent qw(Plack::Request); +use WebGUI::Session::Response; + +=head1 SYNOPSIS + + my $session = WebGUI::Session->open(...); + my $request = $session->request; + +=head1 DESCRIPTION + +WebGUI's PSGI request utility class. Sub-classes L. + +An instance of this object is created automatically when the L +is created. + +=head1 METHODS + +=head2 new_response () + +Creates a new L object. + +N.B. A L object is automatically created when L +is instantiated, so in most cases you will not need to call this method. +See L + +=cut + +sub new_response { + my $self = shift; + return WebGUI::Session::Response->new(@_); +} + +# This is only temporary +sub TRACE { + shift->env->{'psgi.errors'}->print(join '', @_, "\n"); +} + +1; \ No newline at end of file diff --git a/lib/WebGUI/Response.pm b/lib/WebGUI/Session/Response.pm similarity index 56% rename from lib/WebGUI/Response.pm rename to lib/WebGUI/Session/Response.pm index 6c0e15fc9..f94b196e9 100644 --- a/lib/WebGUI/Response.pm +++ b/lib/WebGUI/Session/Response.pm @@ -1,12 +1,20 @@ -package WebGUI::Response; +package WebGUI::Session::Response; use strict; use parent qw(Plack::Response); use Plack::Util::Accessor qw(session streaming writer streamer); -=head2 DESCRIPTION +=head1 SYNOPSIS -The WebGUI server response object. See of L + my $session = WebGUI::Session->open(...); + my $response = $session->response; + +=head1 DESCRIPTION + +WebGUI's PSGI response utility class. Sub-classes L. + +An instance of this object is created automatically when the L +is created. =cut