Continuity
view release on metacpan or search on metacpan
lib/Continuity/Adapt/PSGI.pm view on Meta::CPAN
}
}
# This is actually returning a subref to PSI/Plack
# So put it at the end
$server->loop;
=cut
use strict;
use warnings;
use Continuity::Request;
use base 'Continuity::Request';
use Coro;
use Coro::Channel;
use Plack;
use Plack::App::File; # use this now; no surprises for later
warn "tested against Plack 0.9938; you have $Plack::VERSION" if $Plack::VERSION < 0.9938;
sub debug_level { exists $_[1] ? $_[0]->{debug_level} = $_[1] : $_[0]->{debug_level} }
sub debug_callback { exists $_[1] ? $_[0]->{debug_callback} = $_[1] : $_[0]->{debug_callback} }
sub docroot { exists $_[1] ? $_[0]->{docroot} = $_[1] : $_[0]->{docroot} }
sub new {
my $class = shift;
bless {
first_request => 1,
debug_level => 1,
debug_callback => sub { print STDERR "@_\n" },
request_queue => Coro::Channel->new(),
@_
}, $class;
}
sub get_request {
# called from Continuity's main loop (new calls start_request_loop; start_request_loop gets requests from here or wherever and passes them to the mapper)
my ($self) = @_;
my $request = $self->{request_queue}->get or die;
return $request;
}
sub loop_hook {
my $self = shift;
# $server->loop calls this; plackup run .psgi files except a coderef as the
# last value and this lets that coderef fall out of the call to
# $server->loop.
# unique to the PSGI adapter -- a coderef that gets invoked when a request
# comes in
my $app = sub {
my $env = shift;
unless ($env->{'psgi.streaming'}) {
die 'This application needs psgi.streaming support!';
}
# stuff $env onto a queue that get_request above pulls from; get_request is
# called from Continuity's main execution context/loop. Continuity's main
# execution loop invokes the Mapper to send the request across a queue to
# the per session execution context (creating a new one as needed).
return sub {
my $response = shift;
async {
local $Coro::current->{desc} = 'PSGI Response Maker';
# make it now and send it through the queue fully formed
my $request = Continuity::Adapt::PSGI::Request->new( $env, $response );
$self->{request_queue}->put($request);
# Now... we wait!
$request->{response_done_watcher}->wait;
};
};
};
# Is this needed?
Coro::cede();
return $app;
}
=head2 C<< $adapter->map_path($path) >>
Decodes URL-encoding in the path and attempts to guard against malice.
Returns the processed filesystem path.
=cut
sub map_path {
my $self = shift;
my $path = shift() || '';
my $docroot = $self->docroot || '';
# my $docroot = Cwd::getcwd();
# $docroot .= '/' if $docroot and $docroot ne '.' and $docroot !~ m{/$};
# some massaging, also makes it more secure
$path =~ s/%([0-9a-fA-F][0-9a-fA-F])/chr hex $1/ge;
$path =~ s%//+%/%g unless $docroot;
$path =~ s%/\.(?=/|$)%%g;
$path =~ s%/[^/]+/\.\.(?=/|$)%%g;
# if($path =~ m%^/?\.\.(?=/|$)%) then bad
$self->Continuity::debug(2,"path: $docroot$path\n");
return "$docroot$path";
}
sub send_static {
my ($self, $r) = @_;
# this is called from Continuity.pm to give a request back to us to deal with that it got from our get_request.
( run in 0.484 second using v1.01-cache-2.11-cpan-140bd7fdf52 )