in reply to Trying to exit an HTTP::Daemon based on JSON::RPC::Server::Daemon

$svr->handle(signal => 'KILL');

That seems an awful lot like wishful thinking, please see the signal section of threads

I have a similar app based on RPC::XML::Server that works this way. It too relies on HTTP::Daemon so I know its possible.

Great, maybe you can view the source to see how it works :)

  • Comment on Re: Trying to exit an HTTP::Daemon based on JSON::RPC::Server::Daemon
  • Download Code

Replies are listed 'Best First'.
Re^2: Trying to exit an HTTP::Daemon based on JSON::RPC::Server::Daemon
by carcus88 (Acolyte) on Oct 09, 2010 at 14:54 UTC
    Yeah I know it seems a bit wishful but I have found it very reliable under Windows since at least ActivePerl 5.8 (using 5.12 now) At any rate I know it can be done just not sure the best way to get it working with JSON::RPC::Server::Daemon. This package has rather simple startup for the server as follows...
    package JSON::RPC::Server::Daemon; use strict; use JSON::RPC::Server; # for old Perl 5.005 use base qw(JSON::RPC::Server); $JSON::RPC::Server::Daemon::VERSION = '0.03'; use Data::Dumper; sub new { my $class = shift; my $self = $class->SUPER::new(); my $pkg; if( grep { $_ =~ /^SSL_/ } @_ ){ $self->{_daemon_pkg} = $pkg = 'HTTP::Daemon::SSL'; } else{ $self->{_daemon_pkg} = $pkg = 'HTTP::Daemon'; } eval qq| require $pkg; |; if($@){ die $@ } $self->{_daemon} ||= $pkg->new(@_) or die; return $self; } sub handle { my $self = shift; my %opt = @_; my $d = $self->{_daemon} ||= $self->{_daemon_pkg}->new(@_) or d +ie; while (my $c = $d->accept) { $self->{con} = $c; while (my $r = $c->get_request) { $self->request($r); $self->path_info($r->url->path); $self->SUPER::handle(); last; } $c->close; } }
    RPC::XML::Server on the other hand is doing stuff that I think is making the signaling work. Only problem is I'm not quite sure what its doing here and even if I was sure what was going on I need advise on possible ways to make it work for the JSON server package.
    package RPC::XML::Server; use 5.006001; use strict; use warnings; use vars qw($VERSION @ISA $INSTANCE $INSTALL_DIR %FAULT_TABLE @XPL_PA +TH $IO_SOCKET_SSL_HACK_NEEDED $COMPRESSION_AVAILABLE); use Carp qw(carp croak); use AutoLoader 'AUTOLOAD'; use File::Spec; use IO::Handle; use HTTP::Status; use HTTP::Response; use URI; use Scalar::Util 'blessed'; use RPC::XML; use RPC::XML::ParserFactory; use RPC::XML::Procedure; .... sub new ## no critic (ProhibitExcessComplexity) { my ($class, %args) = @_; my ( $self, $http, $resp, $host, $port, $queue, $path, $URI, $srv_name, $srv_version, $timeout ); $class = ref($class) || $class; $self = bless {}, $class; $srv_version = delete $args{server_version} || $self->version; $srv_name = delete $args{server_name} || $class; $self->{__version} = "$srv_name/$srv_version"; if (delete $args{no_http}) { $self->{__host} = delete $args{host} || q{}; $self->{__port} = delete $args{port} || q{}; } else { require HTTP::Daemon; $host = delete $args{host} || q{}; $port = delete $args{port} || q{}; $queue = delete $args{queue} || 5; $http = HTTP::Daemon->new( Reuse => 1, ($host ? (LocalHost => $host) : ()), ($port ? (LocalPort => $port) : ()), ($queue ? (Listen => $queue) : ()) ); if (! $http) { return "${class}::new: Unable to create HTTP::Daemon objec +t"; } $URI = URI->new($http->url); $self->{__host} = $URI->host; $self->{__port} = $URI->port; $self->{__daemon} = $http; } # Create and store the cached response object for later cloning an +d use $resp = HTTP::Response->new(); if (! $resp) { return "${class}::new: Unable to create HTTP::Response object" +; } $resp->header( # This is essentially the same string returned b +y the # default "identity" method that may be loaded f +rom a # XPL file. But it hasn't been loaded yet, and m +ay not # be, hence we set it here (possibly from option + values) RPC_Server => $self->{__version}, RPC_Encoding => 'XML-RPC', # Set any other headers as well Accept => 'text/xml' ); $resp->content_type('text/xml'); $resp->code(RC_OK); $resp->message('OK'); $self->{__response} = $resp; # Basic (scalar) properties $self->{__path} = delete $args{path} || q{}; $self->{__started} = 0; $self->{__method_table} = {}; $self->{__requests} = 0; $self->{__auto_methods} = delete $args{auto_methods} || 0; $self->{__auto_updates} = delete $args{auto_updates} || 0; $self->{__debug} = delete $args{debug} || 0; $self->{__xpl_path} = delete $args{xpl_path} || []; $self->{__timeout} = delete $args{timeout} || 10; $self->{__parser} = RPC::XML::ParserFactory->new( $args{parser} ? @{delete $args{parser}} : ()); # Set up the default methods unless requested not to if (! delete $args{no_default}) { $self->add_default_methods; } # Compression support $self->{__compress} = q{}; if (delete $args{no_compress}) { $self->{__compress} = q{}; } else { $self->{__compress} = $COMPRESSION_AVAILABLE; # Add some more headers to the default response object for com +pression. # It looks wasteful to keep using the hash key, but it makes i +t easier # to change the string in just one place (above) if I have to. if ($self->{__compress}) { $resp->header(Accept_Encoding => $self->{__compress}); } $self->{__compress_thresh} = delete $args{compress_thresh} || +4096; # Yes, I know this is redundant. It's for future expansion/fle +xibility. $self->{__compress_re} = $self->{__compress} ? qr/$self->{__compress}/ : qr/deflate +/; } # Parameters to control the point at which messages are shunted to + temp # files due to size, and where to home the temp files. Start with +a size # threshhold of 1Meg and no specific dir (which will fall-through +to the # tmpdir() method of File::Spec). $self->{__message_file_thresh} = delete $args{message_file_thresh} + || 1_048_576; $self->{__message_temp_dir} = delete $args{message_temp_dir} || q{ +}; # Set up the table of response codes/messages that will be used wh +en the # server is sending a controlled error message to a client (as opp +osed to # something HTTP-level that is less within our control). $self->{__fault_table} = {%FAULT_TABLE}; if ($args{fault_code_base}) { my $base = delete $args{fault_code_base}; # Apply the numerical offset to all (current) error codes for my $key (keys %{$self->{__fault_table}}) { if (ref($self->{__fault_table}->{$key})) { # A ref is a listref where the first element is the co +de $self->{__fault_table}->{$key}->[0] += $base; } else { $self->{__fault_table}->{$key} += $base; } } } if ($args{fault_table}) { my $local_table = delete $args{fault_table}; # Merge any data from this table into the object's fault-table for my $key (keys %{$local_table}) { $self->{__fault_table}->{$key} = (ref $local_table->{$key} +) ? [ @{$local_table->{$key}} ] : $local_table->{$key}; } } # Copy the remaining args over untouched for (keys %args) { $self->{$_} = $args{$_}; } return $self; } ... ###################################################################### +######### # # Sub Name: server_loop # # Description: Enter a server-loop situation, using the accept() +loop of # HTTP::Daemon if $self has such an object, or falli +ng back # Net::Server otherwise. # # The critic disabling is because we may manipulate +@_ # when using Net::Server. # # Arguments: NAME IN/OUT TYPE DESCRIPTION # $self in ref Object of this class # %args in hash Additional parameters +to set up # before calling the s +uperclass # Run method # # Returns: string if error, otherwise void # ###################################################################### +######### sub server_loop ## no critic (RequireArgUnpacking,ProhibitExcessComple +xity) { my $self = shift; if ($self->{__daemon}) { my ($conn, $req, $resp, $reqxml, $respxml, $exit_now, $timeout +); my %args = @_; # Localize and set the signal handler as an exit route my @exit_signals; if (exists $args{signal} and $args{signal} ne 'NONE') { @exit_signals = (ref $args{signal}) ? @{$args{signal}} : $args{signal} +; } else { push @exit_signals, 'INT'; } local @SIG{@exit_signals} = (sub { $exit_now++ }) x @exit_sign +als; $self->started('set'); $exit_now = 0; $timeout = $self->{__daemon}->timeout(1); while (! $exit_now) { $conn = $self->{__daemon}->accept; if ($exit_now) { last; } if (! $conn) { next; } $conn->timeout($self->timeout); $self->process_request($conn); $conn->close; undef $conn; # Free up any lingering resources } if (defined $timeout) { $self->{__daemon}->timeout($timeout); } } else { # This is the Net::Server block, but for now HTTP::Daemon is n +eeded # for the code that converts socket data to a HTTP::Request ob +ject require HTTP::Daemon; my $conf_file_flag = 0; my $port_flag = 0; my $host_flag = 0; # Disable critic on the C-style for-loop because we need to st +ep by # 2 as we check for Net::Server arguments... for (my $i = 0; $i < @_; $i += 2) ## no critic (ProhibitCStyle +ForLoops) { if ($_[$i] eq 'conf_file') { $conf_file_flag = 1; } if ($_[$i] eq 'port') { $port_flag = 1; } if ($_[$i] eq 'host') { $host_flag = 1; } } # An explicitly-given conf-file trumps any specified at creati +on if (exists($self->{conf_file}) and (!$conf_file_flag)) { push @_, 'conf_file', $self->{conf_file}; $conf_file_flag = 1; } # Don't do this next part if they've already given a port, or +are # pointing to a config file: if (! ($conf_file_flag || $port_flag)) { push @_, 'port', $self->{port} || $self->port || 9000; push @_, 'host', $self->{host} || $self->host || q{*}; } # Try to load the Net::Server::MultiType module if (! eval { require Net::Server::MultiType; 1; }) { if ($@) { return ref($self) . "::server_loop: Error loading Net::Server::MultiTy +pe: $@"; } } unshift @RPC::XML::Server::ISA, 'Net::Server::MultiType'; $self->started('set'); # ...and we're off! $self->run(@_); } return; }
    I think that is somehow setting signal handlers in the server_loop function?
      I think that is somehow setting signal handlers in the server_loop function?

      Yes, the only way to set signal handlers is to assign to %SIG. threads explains about threads and signaling, perlipc explains more.