Plack
view release on metacpan or search on metacpan
lib/HTTP/Server/PSGI.pm view on Meta::CPAN
eval { require IO::Socket::SSL; 1 }
or Carp::croak("SSL suport requires IO::Socket::SSL");
$args->{SSL_key_file} = $self->{ssl_key_file};
$args->{SSL_cert_file} = $self->{ssl_cert_file};
return "IO::Socket::SSL";
} elsif ($self->{ipv6}) {
eval { require IO::Socket::IP; 1 }
or Carp::croak("IPv6 support requires IO::Socket::IP");
$self->{host} ||= '::';
$args->{LocalAddr} ||= '::';
return "IO::Socket::IP";
}
return "IO::Socket::INET";
}
sub setup_listener {
my $self = shift;
$self->{listen_sock} ||= do {
my %args = (
Listen => SOMAXCONN,
LocalPort => $self->{port},
LocalAddr => $self->{host},
Proto => 'tcp',
ReuseAddr => 1,
);
my $class = $self->prepare_socket_class(\%args);
$class->new(%args)
or die "failed to listen to port $self->{port}: $!";
};
$self->{server_ready}->({ %$self, proto => $self->{ssl} ? 'https' : 'http' });
}
sub accept_loop {
my($self, $app) = @_;
$app = Plack::Middleware::ContentLength->wrap($app);
while (1) {
local $SIG{PIPE} = 'IGNORE';
if (my $conn = $self->{listen_sock}->accept) {
if (defined TCP_NODELAY) {
$conn->setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
or die "setsockopt(TCP_NODELAY) failed:$!";
}
my $env = {
SERVER_PORT => $self->{port},
SERVER_NAME => $self->{host},
SCRIPT_NAME => '',
REMOTE_ADDR => $conn->peerhost,
REMOTE_PORT => $conn->peerport || 0,
'psgi.version' => [ 1, 1 ],
'psgi.errors' => *STDERR,
'psgi.url_scheme' => $self->{ssl} ? 'https' : 'http',
'psgi.run_once' => Plack::Util::FALSE,
'psgi.multithread' => Plack::Util::FALSE,
'psgi.multiprocess' => Plack::Util::FALSE,
'psgi.streaming' => Plack::Util::TRUE,
'psgi.nonblocking' => Plack::Util::FALSE,
'psgix.harakiri' => Plack::Util::TRUE,
'psgix.input.buffered' => Plack::Util::TRUE,
'psgix.io' => $conn,
};
$self->handle_connection($env, $conn, $app);
$conn->close;
last if $env->{'psgix.harakiri.commit'};
}
}
}
sub handle_connection {
my($self, $env, $conn, $app) = @_;
my $buf = '';
my $res = [ 400, [ 'Content-Type' => 'text/plain' ], [ 'Bad Request' ] ];
while (1) {
my $rlen = $self->read_timeout(
$conn, \$buf, MAX_REQUEST_SIZE - length($buf), length($buf),
$self->{timeout},
) or return;
my $reqlen = parse_http_request($buf, $env);
if ($reqlen >= 0) {
$buf = substr $buf, $reqlen;
if (my $cl = $env->{CONTENT_LENGTH}) {
my $buffer = Stream::Buffered->new($cl);
while ($cl > 0) {
my $chunk;
if (length $buf) {
$chunk = $buf;
$buf = '';
} else {
$self->read_timeout($conn, \$chunk, $cl, 0, $self->{timeout})
or return;
}
$buffer->print($chunk);
$cl -= length $chunk;
}
$env->{'psgi.input'} = $buffer->rewind;
} else {
open my $input, "<", \$buf;
$env->{'psgi.input'} = $input;
}
$res = Plack::Util::run_app $app, $env;
last;
}
if ($reqlen == -2) {
# request is incomplete, do nothing
} elsif ($reqlen == -1) {
# error, close conn
last;
}
}
if (ref $res eq 'ARRAY') {
$self->_handle_response($res, $conn);
( run in 0.585 second using v1.01-cache-2.11-cpan-39bf76dae61 )