checkpoint
This commit is contained in:
parent
9e535846d5
commit
c0abcc3e4a
3 changed files with 78 additions and 33 deletions
|
|
@ -91,26 +91,44 @@ sub compile_psgi_app {
|
||||||
return sub {
|
return sub {
|
||||||
my $callback = shift;
|
my $callback = shift;
|
||||||
my $request = WebGUI::Request->new($env);
|
my $request = WebGUI::Request->new($env);
|
||||||
my $response = $self->dispatch($request);
|
my $res = $self->dispatch($request);
|
||||||
|
|
||||||
if (ref $response eq 'ARRAY' && ref $response->[2] eq 'CODE') {
|
if ( ref $res eq 'WebGUI::Session' ) {
|
||||||
# Response wants to stream itself, so tell PSGI server to give us
|
my $session = $res;
|
||||||
# a streaming writer object
|
my $response = $session->response;
|
||||||
my $writer = $callback->( [ $response->[0], $response->[1] ] );
|
|
||||||
|
|
||||||
# ..and let the response stream itself
|
|
||||||
try {
|
try {
|
||||||
$response->[2]->($writer);
|
# Response wants to stream itself, so ask PSGI server for a
|
||||||
} catch {
|
# streaming writer object by returning the PSGI response, minus the body
|
||||||
# Response has already been started, so log error and close writer
|
|
||||||
warn "error caught after streaming response started";
|
# Anything in the response body gets cleared (should be empty anyway)
|
||||||
|
$response->body([]);
|
||||||
|
my $psgi_response = $response->finalize;
|
||||||
|
|
||||||
|
my $writer = $callback->( [ $psgi_response->[0], $psgi_response->[1] ] );
|
||||||
|
|
||||||
|
# Store the writer object in the WebGUI::Response object
|
||||||
|
$response->writer($writer);
|
||||||
|
|
||||||
|
# ..and let the response stream itself
|
||||||
|
$response->streamer->($session);
|
||||||
|
|
||||||
$writer->close;
|
$writer->close;
|
||||||
|
$session->close;
|
||||||
|
} catch {
|
||||||
|
if ($response->writer) {
|
||||||
|
# Response has already been started, so log error and close writer
|
||||||
|
warn "error caught after streaming response started";
|
||||||
|
$response->writer->close;
|
||||||
|
} else {
|
||||||
|
$callback->( [ 500, [ 'Content-type: text/html' ], [ 'An error occurred' ] ] );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
# Not streaming, so immediately tell the callback to return
|
# Not streaming, so immediately tell the callback to return
|
||||||
# the response. In the future we could use an Event framework here
|
# the response. In the future we could use an Event framework here
|
||||||
# to make this a non-blocking delayed response.
|
# to make this a non-blocking delayed response.
|
||||||
$callback->($response);
|
$callback->($res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -152,18 +170,33 @@ sub dispatch {
|
||||||
# $session->close;
|
# $session->close;
|
||||||
# $session->output->print("WebGUI PSGI with contentHandlers short-circuited for benchmarking\n");
|
# $session->output->print("WebGUI PSGI with contentHandlers short-circuited for benchmarking\n");
|
||||||
# return $session->response->finalize;
|
# return $session->response->finalize;
|
||||||
|
|
||||||
|
# TODO: From here, contentHandlers need to decide if they want to stream the response body:
|
||||||
|
# $session->response->stream( sub { ... } ) # this replaces 'chunked'
|
||||||
|
# or return a psgi response body.
|
||||||
|
#
|
||||||
|
# We use the $session->response->streaming flag to detect if a contentHandler has requested
|
||||||
|
# to use streaming response.
|
||||||
|
#
|
||||||
|
# Otherwise, whatever they return (arrayref or IO::Handle) is used as the psgi response
|
||||||
|
#
|
||||||
|
# Regular assets should use streaming response body, unless they want to send a file
|
||||||
|
|
||||||
# Streaming content
|
# Here's an example of what a contentHandler would call to do a streaming response:
|
||||||
$session->response->stream(sub {
|
$session->response->stream(sub {
|
||||||
my $writer = shift;
|
my $session = shift;
|
||||||
$writer->write("WebGUI PSGI with contentHandlers short-circuited for benchmarking (streaming)\n");
|
$session->output->print("WebGUI PSGI with contentHandlers short-circuited for benchmarking (streaming)\n");
|
||||||
# sleep 1;
|
sleep 1;
|
||||||
$writer->write("...see?\n");
|
$session->output->print("...see?\n");
|
||||||
$writer->close;
|
|
||||||
});
|
});
|
||||||
if ($session->response->streaming) {
|
|
||||||
$session->close;
|
# Afterwards, we check $session->response->streaming, and if it is set, return the
|
||||||
return $session->response->finalize;
|
# WebGUI::Session (since our caller doesn't have a reference to it) TODO - or does it via $request->session->response???
|
||||||
|
|
||||||
|
# TODO: give WebGUI::Req/Res a weak session reference
|
||||||
|
|
||||||
|
if ( $session->response->streaming ) {
|
||||||
|
return $session;
|
||||||
}
|
}
|
||||||
|
|
||||||
for my $handler (@{$config->get("contentHandlers")}) {
|
for my $handler (@{$config->get("contentHandlers")}) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
package WebGUI::Response;
|
package WebGUI::Response;
|
||||||
|
use strict;
|
||||||
use parent qw(Plack::Response);
|
use parent qw(Plack::Response);
|
||||||
|
|
||||||
use Plack::Util::Accessor qw(streaming);
|
use Plack::Util::Accessor qw(streaming writer streamer);
|
||||||
|
|
||||||
=head2 DESCRIPTION
|
=head2 DESCRIPTION
|
||||||
|
|
||||||
|
|
@ -13,7 +14,16 @@ sub stream {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $streamer = shift;
|
my $streamer = shift;
|
||||||
$self->streaming(1);
|
$self->streaming(1);
|
||||||
$self->body($streamer);
|
$self->streamer($streamer);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub stream_write {
|
||||||
|
my $self = shift;
|
||||||
|
if (!$self->streaming) {
|
||||||
|
Carp::carp("stream_write can only be called inside streaming response");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$self->writer->write(@_);
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
@ -90,18 +90,20 @@ sub print {
|
||||||
my $content = shift;
|
my $content = shift;
|
||||||
my $skipMacros = shift || !($self->session->http->getMimeType =~ /^text/);
|
my $skipMacros = shift || !($self->session->http->getMimeType =~ /^text/);
|
||||||
WebGUI::Macro::process($self->session, \$content) unless $skipMacros;
|
WebGUI::Macro::process($self->session, \$content) unless $skipMacros;
|
||||||
|
my $handle = $self->{_handle};
|
||||||
# Initialise response body if it's empty
|
if (defined $handle) {
|
||||||
$self->session->response->body([]) if !$self->session->response->body;
|
print $handle $content;
|
||||||
|
}
|
||||||
my $body = $self->session->response->body;
|
elsif ($self->session->response) {
|
||||||
if (ref $body ne 'ARRAY') {
|
if ($self->session->response->streaming) {
|
||||||
Carp::carp("Response body is not an ARRAY, it's a " . ref $body);
|
$self->session->response->stream_write($content);
|
||||||
return;
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print $content;
|
||||||
}
|
}
|
||||||
push @{ $body }, $content;
|
|
||||||
|
|
||||||
# TODO - put in IO bound delayed response
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
#-------------------------------------------------------------------
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue