Hello Everyone,
since I am facing the same issue with delayed renering via psgi I wanted to
share my solution.
https://groups.google.com/forum/#!searchin/mojolicious/async$20psgi%7Csort:relevance/mojolicious/L--K5c6Yt6U/TlXx-lFrFagJ
I am using AnyEvent and a little cache class written with Moose. So
considder this code as an proposal/idea to improve the psgi interface, not
as a solution.
Cheers Martin
--
You received this message because you are subscribed to the Google Groups
"Mojolicious" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/mojolicious.
For more options, visit https://groups.google.com/d/optout.
package E2L::PSGI::Mojo::Server2;
use common::sense;
use Mojo::Base 'Mojo::Server';
use Time::HiRes qw/time/;
use Carp 'carp';
use E2L::PSGI::Mojo::CacheObject;
sub run {
my ( $self, $env ) = @_;
my $tx = $self->build_tx;
my $req = $tx->req->parse($env);
my $req_cache = E2L::PSGI::Mojo::CacheObject->new();
$tx->local_port( $env->{SERVER_PORT} )->remote_address( $env->{REMOTE_ADDR} );
# Request body (may block if we try to read too much)
my $len = $env->{CONTENT_LENGTH};
until ( $req->is_finished ) {
my $chunk = ( $len && $len < 131072 ) ? $len : 131072;
last unless my $read = $env->{'psgi.input'}->read( my $buffer, $chunk, 0 );
$req->parse($buffer);
last if ( $len -= $read ) <= 0;
}
$tx->on( resume => $self->curry::resume_cb($req_cache, $tx) );
$tx->on( finish => sub {
# keep a reference to $tx, because it gets weakend according to mojo documentation
undef $self;
undef $tx;
}
);
# PSGI response
my $response = sub {
my $responder = shift;
my $callback = sub {
my $write_buff = $responder->( shift->recv ); # statuscode and headers
$req_cache->write_buff($write_buff); # sets io handle
$self->write_data( $req_cache, $tx ); # need to be called untill things are empty
};
$req_cache->cv->cb($callback);
$self->emit( request => $tx );
};
return $response;
}
sub resume_cb {
my $self = shift;
my $req_cache = shift;
my $tx = shift;
if ( $req_cache->write_buff ) {
$self->write_data($req_cache, $tx);
} elsif ($req_cache->first_resp) {
$req_cache->first_resp(0);
# FIRST RESPONSE
my $res = $tx->res->fix_headers;
my $hash = $res->headers->to_hash(1);
my $code = $res->code;
my @headers;
for my $name ( keys %$hash ) {
push @headers, map { $name => $_ } @{ $hash->{$name} };
}
$req_cache->cv->send( [ $code // 404, \@headers ] );
} else {
# but as soon as cv->cb gets call we do a write buff
}
}
sub write_data {
my $self = shift;
my $req_cache = shift;
my $tx = shift;
my $offset = $req_cache->offset;
my $chunk = $tx->res->get_body_chunk($offset);
if (defined $chunk and my $len = length($chunk)) {
$req_cache->offset( $offset + $len );
$req_cache->write_buff->write($chunk);
# again maybe there is more to write
$self->write_data($req_cache, $tx);
} else {
# nothing to write
if ($tx->res->is_finished) {
# otherwise $c->on(finish => ...) does not get called.
$tx->closed();
}
}
}
sub to_psgi_app {
my $self = shift;
# Preload application and wrap it
$self->app;
return sub { $self->run(@_) }
}
1;
package E2L::PSGI::Mojo::CacheObject;
use Moose;
use common::sense;
use namespace::autoclean;
use MooseX::StrictConstructor;
has cv => (
is => 'ro',
isa => 'AnyEvent::CondVar',
required => 1,
default => sub { AnyEvent->condvar }
);
has write_buff => ( is => 'rw', isa => 'Object', required => 0 );
has first_resp => ( is => 'rw', isa => 'Bool', required => 1, default => 1);
has offset => ( is => 'rw', isa => 'Int', required => 1, default => 0 );
__PACKAGE__->meta->make_immutable;
1;