Refactored Request/Response into WebGUI::Session::
This commit is contained in:
parent
1ad2f0cfd7
commit
b7e7d5b936
5 changed files with 117 additions and 90 deletions
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
package WebGUI::Request;
|
||||
|
||||
=head2 DESCRIPTION
|
||||
|
||||
The WebGUI server response object. See L<Plack::Response>
|
||||
|
||||
=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<WebGUI::Response> object.
|
||||
|
||||
N.B. A L<WebGUI::Response> object is automatically created when L<WebGUI::Session>
|
||||
is instantiated, so in most cases you will not need to call this method.
|
||||
See L<WebGUI::Session/response>
|
||||
|
||||
=cut
|
||||
|
||||
sub new_response {
|
||||
my $self = shift;
|
||||
my $response = WebGUI::Response->new(@_);
|
||||
$response->session($self->session);
|
||||
return $response;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
@ -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<PSGI> 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<PSGI> 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;
|
||||
}
|
||||
|
|
|
|||
40
lib/WebGUI/Session/Request.pm
Normal file
40
lib/WebGUI/Session/Request.pm
Normal file
|
|
@ -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<Plack::Request>.
|
||||
|
||||
An instance of this object is created automatically when the L<WebGUI::Session>
|
||||
is created.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 new_response ()
|
||||
|
||||
Creates a new L<WebGUI::Session::Response> object.
|
||||
|
||||
N.B. A L<WebGUI::Session::Response> object is automatically created when L<WebGUI::Session>
|
||||
is instantiated, so in most cases you will not need to call this method.
|
||||
See L<WebGUI::Session/response>
|
||||
|
||||
=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;
|
||||
|
|
@ -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<Plack::Response>
|
||||
my $session = WebGUI::Session->open(...);
|
||||
my $response = $session->response;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
WebGUI's PSGI response utility class. Sub-classes L<Plack::Response>.
|
||||
|
||||
An instance of this object is created automatically when the L<WebGUI::Session>
|
||||
is created.
|
||||
|
||||
=cut
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue