package Corona::Server;
use strict;
use 5.008_001;
use base qw( Net::Server::Coro );
use Plack::Util;
use constant HAS_AIO => !$ENV{PLACK_NO_SENDFILE} && eval "use Coro::AIO; 1";
use HTTP::Status;
use Scalar::Util;
use List::Util qw(sum max);
use Plack::HTTPParser qw( parse_http_request );
use constant MAX_REQUEST_SIZE => 131072;
sub process_request {
my $self = shift;
my $fh = $self->{server}{client};
my $env = {
SERVER_PORT => $self->{server}{port}[0],
SERVER_NAME => $self->{server}{host}[0],
SCRIPT_NAME => '',
REMOTE_ADDR => $self->{server}{peeraddr},
'psgi.version' => [ 1, 0 ],
'psgi.errors' => *STDERR,
'psgi.input' => $self->{server}{client},
'psgi.url_scheme' => 'http', # SSL support?
'psgi.nonblocking' => Plack::Util::TRUE,
'psgi.run_once' => Plack::Util::FALSE,
'psgi.multithread' => Plack::Util::TRUE,
'psgi.multiprocess' => Plack::Util::FALSE,
'psgi.streaming' => Plack::Util::TRUE,
'psgix.io' => $fh->fh,
};
my $res = [ 400, [ 'Content-Type' => 'text/plain' ], [ 'Bad Request' ] ];
my $buf = '';
while (1) {
my $read = $fh->readline("\015\012\015\012")
or last;
$buf .= $read;
my $reqlen = parse_http_request($buf, $env);
if ($reqlen >= 0) {
$res = Plack::Util::run_app $self->{app}, $env;
last;
} elsif ($reqlen == -2) {
# incomplete, continue
} else {
last;
}
}
if (ref $res eq 'ARRAY') {
# PSGI standard
$self->_write_response($res, $fh);
} elsif (ref $res eq 'CODE') {
# delayed return
my $cb = Coro::rouse_cb;
$res->(sub {
$self->_write_response(shift, $fh, $cb);
});
Coro::rouse_wait $cb;
}
}
sub _write_response {
my($self, $res, $fh, $rouse_cb) = @_;
my (@lines, $conn_value);
while (my ($k, $v) = splice(@{$res->[1]}, 0, 2)) {
push @lines, "$k: $v\015\012";
if (lc $k eq 'connection') {
$conn_value = $v;
}
}
unshift @lines, "HTTP/1.0 $res->[0] @{[ HTTP::Status::status_message($res->[0]) ]}\015\012";
push @lines, "\015\012";
$fh->syswrite(join '', @lines);
if (!defined $res->[2]) {
# streaming write
return Plack::Util::inline_object
write => sub { $fh->syswrite(join '', @_) },
close => $rouse_cb;
} elsif (HAS_AIO && Plack::Util::is_real_fh($res->[2])) {
my $length = -s $res->[2];
my $offset = 0;
while (1) {
my $sent = aio_sendfile( $fh->fh, $res->[2], $offset, $length - $offset );
$offset += $sent if $sent > 0;
last if $offset >= $length;
}
} else {
Plack::Util::foreach($res->[2], sub { $fh->syswrite(join '', @_) });
}
$rouse_cb->() if $rouse_cb;
}
1;
__END__