Arriba
view release on metacpan or search on metacpan
lib/Arriba/Server.pm view on Meta::CPAN
my $null_io = do { open my $io, "<", \""; $io };
use Net::Server::SIG qw(register_sig);
# Override Net::Server's HUP handling - just restart all the workers and that's
# about it
sub sig_hup {
my $self = shift;
$self->hup_children;
}
sub run {
my ($self, $app, $options) = @_;
$self->{app} = $app;
$self->{options} = $options;
my %extra = ();
if ($options->{pid}) {
$extra{pid_file} = $options->{pid};
}
if ($options->{daemonize}) {
$extra{setsid} = $extra{background} = 1;
}
if ($options->{ssl_cert}) {
$extra{SSL_cert_file} = $options->{ssl_cert};
}
if ($options->{ssl_key}) {
$extra{SSL_key_file} = $options->{ssl_key};
}
if (!exists $options->{keepalive}) {
$options->{keepalive} = 1;
}
if (!exists $options->{keepalive_timeout}) {
$options->{keepalive_timeout} = 1;
}
my @port;
for my $listen (@{$options->{listen} || [ "$options->{host}:$options->{port}" ]}) {
my %listen;
if ($listen =~ /:/) {
my($h, $p, $opt) = split /:/, $listen, 3;
$listen{host} = $h if $h;
$listen{port} = $p;
$listen{proto} = 'ssl' if 'ssl' eq lc $opt;
}
else {
%listen = (
host => 'localhost',
port => $listen,
proto => 'unix',
);
}
push @port, \%listen;
}
my $workers = $options->{workers} || 5;
local @ARGV = ();
$self->SUPER::run(
port => \@port,
host => '*',
proto => $options->{ssl} ? 'ssl' : 'tcp',
serialize => 'flock',
log_level => DEBUG ? 4 : 2,
($options->{error_log} ? ( log_file => $options->{error_log} ) : () ),
min_servers => $options->{min_servers} || $workers,
min_spare_servers => $options->{min_spare_servers} || $workers - 1,
max_spare_servers => $options->{max_spare_servers} || $workers - 1,
max_servers => $options->{max_servers} || $workers,
max_requests => $options->{max_requests} || 1000,
user => $options->{user} || $>,
group => $options->{group} || $),
listen => $options->{backlog} || 1024,
check_for_waiting => 1,
no_client_stdout => 1,
%extra
);
}
sub configure_hook {
my $self = shift;
# FIXME: Is this (configure_hook) the best place for this?
if ($self->{options}->{listen_ssl}) {
$self->{server}->{ssl_args}->{SSL_key_file} =
$self->{options}->{ssl_key_file};
$self->{server}->{ssl_args}->{SSL_cert_file} =
$self->{options}->{ssl_cert_file};
}
$self->SUPER::configure_hook(@_);
}
sub pre_bind {
my $self = shift;
$self->SUPER::pre_bind(@_);
if ($self->{options}->{spdy}) {
# Enable SPDY on SSL sockets
for my $sock (@{$self->{server}->{sock}}) {
if ($sock->NS_proto eq 'SSL') {
$sock->SSL_npn_protocols(['spdy/3']);
}
}
}
}
sub pre_loop_hook {
my $self = shift;
my $port = $self->{server}->{port}->[0];
my $proto = $port->{proto} eq 'ssl' ? 'https' :
$port->{proto} eq 'unix' ? 'unix' : 'http';
$self->{options}{server_ready}->({
host => $port->{host},
port => $port->{port},
proto => $proto,
server_software => 'Arriba',
}) if $self->{options}{server_ready};
register_sig(
TTIN => sub { $self->{server}->{$_}++ for qw( min_servers max_servers ) },
TTOU => sub { $self->{server}->{$_}-- for qw( min_servers max_servers ) },
QUIT => sub { $self->server_close(1) },
);
}
sub server_close {
my($self, $quit) = @_;
if ($quit) {
$self->log(2, $self->log_time . " Received QUIT. Running a graceful shutdown\n");
$self->{server}->{$_} = 0 for qw( min_servers max_servers );
$self->hup_children;
while (1) {
Net::Server::SIG::check_sigs();
$self->coordinate_children;
last if !keys %{$self->{server}{children}};
sleep 1;
}
$self->log(2, $self->log_time . " Worker processes cleaned up\n");
}
$self->SUPER::server_close();
}
sub run_parent {
my $self = shift;
$0 = "arriba master " . join(" ", @{$self->{options}{argv} || []});
no warnings 'redefine';
local *Net::Server::PreFork::register_sig = sub {
my %args = @_;
delete $args{QUIT};
Net::Server::SIG::register_sig(%args);
};
$self->SUPER::run_parent(@_);
}
sub child_init_hook {
my $self = shift;
srand();
if ($self->{options}->{psgi_app_builder}) {
$self->{app} = $self->{options}->{psgi_app_builder}->();
}
$0 = "arriba worker " . join(" ", @{$self->{options}{argv} || []});
}
sub post_accept_hook {
my $self = shift;
$self->{client} = { };
}
sub process_request {
my $self = shift;
my $client = $self->{server}->{client};
# Is this an SSL connection?
my $ssl = $client->NS_proto eq 'SSL';
if ($ssl && $client->next_proto_negotiated &&
$client->next_proto_negotiated eq 'spdy/3')
{
# SPDY connection
require Arriba::Connection::SPDY;
$self->{client}->{connection} =
Arriba::Connection::SPDY->new($client);
}
else {
# HTTP(S) connection
require Arriba::Connection::HTTP;
$self->{client}->{connection} =
Arriba::Connection::HTTP->new($client, ssl => $ssl,
chunk_size => CHUNKSIZE);
}
my $connection = $self->{client}->{connection};
while (my $req = $connection->read_request) {
my $env;
my $conn_header;
if ($req->{env}) {
# Headers already parsed
$env = $req->{env};
}
else {
$env = {
REMOTE_ADDR => $self->{server}->{peeraddr},
REMOTE_HOST => $self->{server}->{peerhost} || $self->{server}->{peeraddr},
REMOTE_PORT => $self->{server}->{peerport} || 0,
SERVER_NAME => $self->{server}->{sockaddr} || 0, # XXX: needs to be resolved?
SERVER_PORT => $self->{server}->{sockport} || 0,
SCRIPT_NAME => '',
'psgi.version' => [ 1, 1 ],
( run in 0.623 second using v1.01-cache-2.11-cpan-5a3173703d6 )