Net-Server-2.008/0000755000175000017500000000000012334210320012167 5ustar paulpaulNet-Server-2.008/Changes0000644000175000017500000007070512334210017013476 0ustar paulpaulRevision history for Perl extension Net::Server. 2.008 May 12 2014 - Long awaited patch for the IO::Socket::SSL default verify mode bug - Add Time::HiRes as a dep - Update to new github hosted repository with all legacy code imported - Beginnings of change to dzil based releases - There are several dozen outstanding bugs that will be addressed in the next release 2.007 Jan 09 2013 - Update run_dequeue to clear out signals after fork. - Add serialize none for PreFork and PreForkSimple servers that at not multi-port. - Allow for auto-assigned port to be the same when hostname resolves to multiple ips/ipvs (RT #78024) - Fix bug in header parsing (RT #82125) - Fix warning bug (RT #78828) - Fix wrong wantarray context in Proto::TCP (RT #31437) - Simplify _get_commandline to just use $0 - no fishing in proc (RT #80260) - Fix uninitialized begin (RT #78830) - Fix misplaced +2 (RT #78827) - Fix various spelling errors and pod nits (RT #77994) - Allow for log calls from the MUX object (RT #78514) - Give an error at begin time of SSLEAY if the need Fcntl function is not available on this platform (RT #82542) - Make all of the tests self cancel much earlier if there is an error handling the request. - Add a stronger timeout to PreFork tests - ideally we'd like to find a real way to test these one windows (RT #811411, #81993) - still broken - but shouldn't hang - Superficial changes to help packagers not list Log4perl as dependency (it is optional) 2.006 Jun 20 2012 - Allow for case where host is *, and IO::Socket::INET6 is installed, but IPv6 doesn't really work. - Add missing child_init_hook in Fork server (so Fork can be more parallel with PreFork in some respects) - Change BOUND_SOCKETS passing to use ; as a separator rather than a \n 2.005 Jun 12 2012 NOTE: ipv now defaults to * - Change the default of ipv from 4 to *. This means if a host of * (default), or a named host is used, any available IPv4 OR IPv6 address will be used. - Allow for explicit close_client_stdout call - Add dispatch methods and app setup to HTTP - Allow for exec_fork_hook in HTTP - Make sure errors in HTTP use correct logging mechanisms (and do not die un-needed) - Fix 500 call in PSGI - Fix send_header 2.004 Jun 08 2012 NOTE: Version 2.005 will change the default ipv value to * meaning it will attempt to bind IPv4 and IPv6 if they are available if you pass a hostname - Add Net::Server::Proto->get_addr_info which can more reliably resolve domain information. - Use that information to figure out an appropriate host to bind to for tests - Make get_addr_info less reliant on magic values of sysctl net.ipv6.bindv6only - Allow all tests to function in IPv6 only environments - Fix broken number of tests in test suite - Add warnings about changes to the default value of ipv coming in 2.005 2.003 Jun 06 2012 - Make the logging system pluggable - Added net-server server executor for writing easier one line servers - Sys::Syslog and Log::Log4perl are now moved out to their own module subsystems - Added full apache style HTTP log formatting for the HTTP server - Allow for ipv to be specified as part of host, or proto, or passed via $ENV{'IPV'} - Add apache style access logging (access_log_file and access_log_format) to HTTP - Allow HTTP header parsing to not untaint the headers (thanks Miko O'Sullivan) - Fix missing legacy NS_unix_path call (missing since 2.000) - Fix a bug in MultiType that prevented calling server_type HTTP 2.002 May 31 2012 - Make HTTP output header parsing more consistent - and catch more errors - Add exec_cgi and exec_trusted_perl methods to HTTP - Add bugfix for ipv=>"*" combined with UNIX sockets. (Mark Martinec) - Fix the SSL_test.t to use exit rather than quit so the parent departs 2.001 May 30 2012 - Bug fix wrong usage of File::Temp::tempfile. - Fix HTTP_COOKIES to be HTTP_COOKIE - Handle multiple header values better in HTTP - Add Log::Log4perl logging courtesy of TONVOON@cpan 2.000 May 30 2012 - Sorry for the amazingly long delay. This release represents change to much of the code base. Future patch submissions should be more promptly handled - Bring Net::Server::Proto::SSL back. It is now fully functional under all scenarios, including IPv4 and IPv6 - Change Proto interface to allow passing more information. This represents an internal API change. - Updates to the HUP mechanisms to make sure we rebind all types of ports correctly. - Add IPv6 integration via ::1 style addresses as well as the ipv configuration parameter (Mark Martinec) - Added graceful shutdown (Tatsuhiko Miyagawa) - Added hot deploy via TTIN and TTOU (Tatsuhiko Miyagawa) - Internal code retidying - Finish out support for connecting to ports by service name - Don't loose track of fork and prefork children on a hup - make sure to actively wait them off - Correct accept to take a classname, and optionally be called in array context - Cleanup numerous configuration issues. - Added sig_passthrough option to Fork, PreFork, and PreForkSimple servers allowing for arbitrary signals to propagate to children - Add syswrite/sysread support to SSLEAY (Sergey Zasenko). - Add PSGI module. - Many small accumulated bugfixes. 0.99 Jul 13 2010 - Add customizable check_for_spawn and min_child_ttl settings in PreFork (Graham Barr) - Add other_child_died_hook (Daniel Kahn Gillmor) - Make Multiplex do $mux->add($sock) for UDP sockets (Kristoffer Møllerhøj) - Change Net::Server::Daemonize to use kill 0 rather than the unportable `ps` - Fix calling conventions of MultiType - Avoid select in SSLEAY that was allowing for infinite spin loop - Fix tie_stdout mode to not warn about unopen handles. - Added Net::Server::HTTP base class for basic HTTP daemon handling. - Change examples/httpd to use Net::Server::HTTP 0.98 May 05 2010 - Add SSLeay proto - finally a workable SSL solution. - Add minimal Net::Server::TiedHandle to allow for STDIN and STDOUT to work with SSLEAY - Net::Server::TiedHandle also support tied_stdin_callback and tied_stdout_callback Feb 08 2008 - Allow for port => 0 which lets the OS auto assign a port on some OSes (Blackie Hlasek) - Add idle_loop_hook to PreForkSimple and PreFork (David Zuhn) - Add consistent formatting capabilities to the log method (whethere Syslog is used or not) (David Zuhn) - Warn when default listen value is used - try to make it a sensible default (Mark Martinec) - Allow for non-zero exit value - particularly when called from fatal (David Schweikert) 0.97 Jul 25 2007 - Allow for better handling of setlogsock depending upon the version of Sys::Syslog installed (David Schweikert) - Update examples with minimal pod and working synopses - Added post_client_connection_hook (Mihail Nasedkin) 0.96 Mar 23 2007 - Allow for conf_file to be specified in the default_values. - Add perldoc for why we use a template in options. - Fix syslog log options regex again (Carlos Velasco) - Fix ->autoflush (needs FileHandle) (Paul Miller) - Add handle_syslog_error to allow catching errors during syslog writes (Patrik Wallstrom) - Add open_syslog to slightly abstract opening of syslog. - Add numerous patches from Rob Mueller to cleanup child accounting in PreFork server. 0.95 Feb 02 2007 - Warn clean on the chld hanlder in PreFork. (Michael Virnstein) - Allow lock_file for lock serialization to only be opened once (Rob Mueller) - Add additional log messages during failure in accept (Mark Martinec) - Fix double decrement bug in PreFork.pm (Bill Nesbitt, Carlos Velasco) (rt #21271) - Fix precedence bug with non-parened open (John W. Krahn) - Check setuid better after POSIX setuid (Ricardo Signes) (rt #21262) - Update Syslog options parsing (Carlos Velasco) (rt #21265) - Allow no_client_stdout to work with Multiplex (Steven Lembark) - Allow Sys::SysLog keyworks be passed through the ->log method (Peter Beckman) - Allow more characters through in syslog_ident (Peter Beckman) - Fix Fork server bug which had post_accept_hook called twice (Curtis Wilbar) - Added pre_fork_hook to Fork server to handle removed duplicate post_accept_hook call. - Reopen STDIN/STDOUT to /dev/null at end of child connection to avoid spurious warnings (Rob Mueller) - Don't process STDIN/STDOUT in post_accept if udp_true (Rob Mueller) - Cleanup child processing code in PreFork server (Rob Mueller) - Try and let tests fail gracefully if localhost is not setup properly (Peter Beckman) - Add numerous tests for configuration passing. - Add perldoc about adding your own custom options. 0.94 Jul 08 2006 - Add nofatal to Sys::Syslog::openlog if Sys::Syslog version >= 0.15 (thanks to DSCHWEI on cpan) - Added the leave_children_open_on_hup flag which leaves open connections open when the server is hupped. It is false by default. - Make sure new and run can both take a hash or a hashref of values. - More fixes to HUP under taint (thanks to LUPE on cpan) - Allow for port, host, and proto to be passed as arrayrefs to run and new. - Fix bug in a check for dead child processes algorithm in PreFork server (thanks to Michael Virnstein). 0.93 Mar 23 2006 - Allow for get sock info routines to use $peer->{client} rather than STDIN which may not be set if the "no_client_stdout" flag is set. (thanks to Mark Martinec for pointing this out) 0.92 Mar 13 2006 - Allow for duplicated STDIN and STDOUT to properly close. Previously they closed because they were simple symbol globs. Now they need an explicit close be cause they are opened to the client socket's file descriptors. - Add flag to disable all of the binding of client to STDIN and STDOUT 0.91 Mar 08 2006 - Abstract shutdown_sockets method that is called at the end of server_close (to allow for calling in other places). - Make sure close_children unsets the overridden signals in the forked and preforked servers. - Better handling of STDIN and STDOUT as provided by tye on perlmonks in response to Ben Cohen's question (in node http://www.perlmonks.org/?node_id=534791) - Finally added a new method. - Added much missing perldoc. - Pass parameters to ->run the second time it is called. This allows for multitype to handle more parameters - but needs to be tested for all use cases (it could result in array fields getting multiple entries which should be fine in most cases). Thanks to Aron Ujvari for pointing this out. - Add default_values method (suggested by Malte S. Stretz). - Fix udp_broadcast issue (fix by Rob Mueller) 0.90 Dec 05 2005 - Make HUP work correctly on Multiplex server. - Allow socket files to cleanup correctly. - Allow Net::Server::Daemonize to function properly in Taint mode again (broken in .88). - Add ->commandline method to allow for getting and setting the commandline for use during a HUP. This is to allow for untainting as necessary. - Add ->can_read_hook (see the documentation) to allow for processing of arbitrary handles in accept_multi_port. 0.89 Nov 22 2005 - Added SSL_passwd_cb to Proto/SSL.pm (Irving A. Bermudez S.) - Fix rt #13450 which is caused by broken POSIX::setuid on perl 5.8.0 on RedHat 9.0. - Allow for graceful skipping if a port is configured twice in the configuration file. - Allow tests that can pass to pass on Win32 (skip those that cannot) - Allow "-" in user names. (Carl Lewis) - Add Reuse = 1 to Proto::UDP. (Slaven Rezic) - Allow for udp_broadcast setting in Proto::UDP. (Tim Watt) - Add bug note to Proto::SSL (Christopher A Bongaarts) - setsid property is now boolean rather than checking definedness. - Command line parameters override conf file parameters. - Store command line a little better in preparation for HUP. - Allow for cleaner HUP and better error if process already running. 0.88 Jun 21 2005 - Change maintainer back to paul@seamons.com (Paul Seamons) - Add run_n_children_hook to prefork servers (At suggestion of James Fitzgibbon and Paul B. Henson) - Make delete child only delete children it knows about. Fixes ancient bug http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=168784 filed by Christian Mock and worked on by Lucas Filipozzi. - Store $ENV{PWD} as part of script name for HUP'ing (Russel Pettway) - Allow PreFork and PreForkSimple to have child mark explicitly as done the same as other server types via the ->done(1) call. (Idea from Marc Martinec) - After numerous requests, the CHLD SIGNAL is now set to DEFAULT in the child process of PreFork and PreForkSimple servers. This should allow grand child processes to run without affecting the child process (or parent). - Fix parent/child communication channel buffering issue (Matt Sergeant) - Check for child's sock before closing with child_communication enabled (Alexander Hlawenka) - Documentation fix (Mark Morgan) - Allow 'stream' option for syslog_logsock property (Daniel Matuschek) - Fix syslog format vulnerability. (Carlos Velasco) This has potential to break some log implementations that were planning on ->log passing all of @_ to syslog. Now only the first item from @_ is passed as the message. - Allow for '-' in group names. (Corey Minyard) - Prevent locking caused by interupt of flock (Dietmar Maurer [cpan #11693]) - Finally fix UID/GID bugs during daemonization. This is the biggest bug winner. The new model Calls POSIX::setuid and setgid and tests for success by checking the values of $< and $( and not by checking the response of the setuid/setgid functions. - Add CIDR style lookups for allow/deny using cidr_allow and cidr_deny (Carsten Wolff) - Allow for port configured in perl to not have to be arrayref. 0.87 Feb 14 2004 - Patch by Enrik.Berkhan@planb.de (Enrik Berkhan) that fixes RT Bug #3671 - Patch by chris@dyndns.org (Chris Reinhardt) Integrate pre_accept_hook and post_accept_hook into Net::Server::Fork 0.86 Nov 06 2003 - Changed maintainer to bbb@cpan.org (Rob Brown). - Patch to fix Net::Server::Daemonize setuid bug: http://www.ijs.si/software/amavisd/net-server.patch - Add a fix in the argument handling of configure to account for some alpha systems (James Vasak) - For RedHat 8.0 perl.req, avoid demanding that perl(IO::Muliplex) and perl(IO::Socket::SSL) rpms are installed just to use Net::Server. 0.85 Mar 06 18:00 2003 - Lower timeouts during tests (Anil Madhavapeddy) - Add configure_hook to MultiType (Michael Alan Dorman) - More graceful exit of children in PreForkSimple (Helge Kraenz) - Correct test for POSIX::setuid(0) success (Peter Chen) - Allow DOS filenames for conf files (Mark M. Adkins) - Allow for ndelay on Sys::Syslog::openlog (Doug Perham) - Add documentation about run_dequeue. - Add run_dequeue feature to Multiplex personality. 0.84 May 22 08:00 2002 - Safer peername check in get_client_info to avoid crashing under certain conditions. - Create noarch RPM since Net::Server is pure perl. - Always chown log and pid files when started as root but running as non-root. - More graceful exit of children in PreFork - Kill children with a kill 15 rather than kill 2 - Fixes on Tru64 UNIX (Marco Sbodio) - Allow for SOCK_STREAM and SOCK_DGRAM to be passed as strings to proto (ie "/tmp/path/|SOCK_STREAM|unix") (Andrzej Filip) - Backward compatibility fix for IO::Socket usage (Matt Sergeant) - Avoid reopening STDIN and STDOUT in INET mode. (Bernard Quatermass) 0.83 Mar 26 15:33 2002 - Prevent race condition warning between accept on socket and assigning client to STDIN - Fix bug in Net::Server::Proto::UNIX which affected older perls (<= 5.005) - Allow failed attempt to change user to continue with warning if not root. - Add parent/child communication code to PreFork.pm based off code submitted by Vadim. Allows children to speak to parent. - Improved accounting of child processes in PreFork. - Add spec file for rpm. 0.82 Jan 29 16:20 2002 - Add changes pointed out by Vadim to make sure that SSL client handle is blessed into correct class. 0.81 Nov 19 12:39 2001 - Fix Net::Server::Fork - Bug in forking server once parent has reached max_servers caused. slow infinite loop and no processing of connections. - Some perldoc cleanups - Don't require IO::Multiplex for base test. 0.80 Nov 14 09:30 2001 - Fix Net::Server::Multiplex::MUX::mux_eof to pass a ref to the remaining data. 0.79 Oct 23 12:00 2001 - Added Net::Server::Multiplex - NOTE: IO::Multiplex >= 1.01 is required to use this personality. 0.78 Sep 28 9:13 - Added post_child_cleanup_hook to server_close - Moved pre_server_close_hook inside server_close - Various small cleanups - Added no_close_by_child flag (see perldoc) 0.77 Aug 27 10:00 - Added dequeuing ability to Fork mode server. - All Fork and PreFork modes now have dequeue ability. 0.76 Aug 24 11:16 - Added Net::Server::PreForkSimple - Simpler PreFork server that only attempts to maintain a constant number of child servers. - Changed Net::Server::PreFork to subclass off of Net::Server::PreForkSimple. No functional changes. - Fixed a bug in Net::Server::Daemonize::set_user. - Fixed syntax bug on 5.005_03 in Proto's 0.75 Aug 23 10:49 - Both Net::Server::Fork and Net::Server::PreFork are using safe signals via Net::Server::SIG. - Net::Server::PreFork has new child managment model. NOTE: spare_servers is no longer used. It has been replaced by min_spare_servers and max_spare_servers. This is a major change. The server will die if these parameters are not properly set. - operates better under high loads - provides better clean up of waiting servers. - more configurable. - Read the perldoc for updates. - Net::Server::Fork and Net::Server::PreFork HUP properly again. - t/Server_PreFork.t and t/Server_Fork.t execute properly. - Fix in Multiport accept with signals. - Updated perldocs 0.73 Aug 21 17:06 - Net::Server::PreFork is on safe signals. 0.72 Aug 21 16:22 2001 - Beginning work on Safe signals - Net::Server::Fork is on safe signals. - Added Net::Server::SIG - Added examples/sigtest.pl 0.71 Aug 17 15:51 2001 - Die on failed change to another user or group. WARNING: No longer defaults to nobody.nobody. Defaults to currently running user and group. - Various cleanups with file removal. - All files to be removed are now chowned. 0.70 Aug 17 10:34 2001 - Added support for different protocols to Net::Server. This implemented via Net::Server::Proto and its classes. Included Net::Server::Proto::TCP, Net::Server::Proto::UDP, Net::Server::Proto::UNIX, and experimental Net::Server::Proto::SSL. TCP, UDP, and UNIX are fully tested. - Added Net::Server::Daemonize. - Allows for modular daemonization/forking routines. - Allowed for configure to be called multiple times. Configure method can be called at later times during server startup. Arguments are cached. This allows new protocols to add arguments without modification to Net::Server base class. - Updated perldocs. No more protocol specific information in central perldoc. More information on new protocol layout. - Added t/UNIX_test.t - Added examples/connection_test.pl - UNIX, UDP, and TCP types are fully operational. Server can bind to all three types. Properties are determined according to type. Server can HUP on all three types. - SSL type added, but experimental (read "extremely alpha") 0.65 Jul 05 22:01 2001 - Modified test suite to no longer depend upon hard coded ports. Improves test reliability on systems where reuse on a socket is not reliable itself. 0.64 Jul 03 21:21 2001 - Allow fall back to main run method at server close - Clean up signal processing in PreFork server - Clean up child management in PreFork server - Added run_dequeue and dequeue methods to the PreFork server (intended to allow for management of items such as mail queues) 0.63 May 07 22:39 2001 - Updated UDP parameter names. Names are now udp_recv_len (previously udp_packet_size) and udp_recv_flags (previously udp_packet_offset). - Updated udp_server.pl to use new names. 0.62 May 01 00:44 2001 - Updated to use getsockopt (determine proto on the fly) - Updated perldoc. - Added udp_server.pl example. - Added UDP_test.t for "make test". - Allow customization of udp recv parameters. 0.61 Apr 30 06:32 2001 - Sig HUP with UDP now works. - Peer info is correctly accessed under UDP - Net::Server::INET will not allow one server to do both tcp and udp without special parameters being passed to the server. - Need to make test program for UDP. 0.60 Apr 28 01:56 2001 - Added support for UDP. Can now simultaneously accept TCP and UDP. Still to do: - allow for SIG HUP under UDP - better determination of peerinfo under UDP - clean up inetd mode. - Added restart_close_hook. - Added restart_open_hook. - Added more documentation (socket access, restarting, protocols) 0.59 Apr 24 07:40 2001 - Forced STDIN,STDOUT,and STDERR to reopen to /dev/null if setsid or log_file is set. This allows for true daemonization (so no output ends up at the terminal). - Made appropriate changes in MultiType as well. 0.58 Apr 06 12:29 2001 - SIG HUP is complete. Fixed bug in SIG HUP'ing PreFork mode. Now effectively restarts. - Various clean ups in code. - More unification of code. 0.57 Mar 29 01:36 2001 - SIG HUP is now functional on multiport mode under Single and Fork Mode. No functionality is lost under PreFork, but HUP'ing results in seg fault. - Various bug fixes. 0.56 Mar 20 12:34 2001 - Catch SIG pipes - Clean up of existing signal handling. - Trim memory in PreFork 0.55 Mar 19 10:44 2001 - Allow overwrite of pid file - safe as other user. - More unified Signal handling, removal of duplicate code. - Allow Fork Server to shutdown the socket immediately after client accept to allow parent to HUP more easily. - Check to see if parent process is still around in PreFork Server. (Don't keep running if parent was "kill 9"ed.) - Save commandline parameters in preparation for HUP 0.54 Mar 16 12:47 2001 - Better handling of sigs in prefork - Improved logic on child coordination routine - Added parent_read_hook - Added httpd example - Added LoadTester.pl example 0.53 Mar 14 01:13 2001 - Allow host to be set to '*' Allows for the server to bind to port whatever on all addresses at that box. - Make passing of host and proto on command line taint clean. - Added setsid functionality. - Added syslog_facility option - default is daemon. - Changed Fork and PreFork to handle $SIG{CHLD} in a more reliable fashion. - Added parent_read_hook 0.52 Mar 13 01:16 2001 - Added syslog ability. This allows for logging to syslog instead of STDERR or a log file. Logging still takes place via $self->log() but is configurable at startup. - Standardized existing log numbers to match syslog levels. 0.51 Mar 10 16:35 2001 - Added piped serialization option. This allows for serialization to be done on a wider range of machines. Flock is more bulletproof, but pipe is more portable. See the Net::Server::PreFork manpage. 0.50 Mar 10 10:06 2001 - Added serialize option to PreFork. This allows for serialization to be turned on even on non_multi port process - this is done to get around some OS's which don't allow children to accept on the same socket at the same time. - Added semaphore type to the serialize option This type uses IPC::Semaphore instead of flock to serialize the child accept sequence. Thanks to Bennett Todd for sample code. 0.48 Mar 08 23:57 2001 - Catch $SIG{INT}, $SIG{TERM} and $SIG{QUIT} in PreFork and Fork. This allows parent to shutdown children properly. - Catch $SIG{HUP} (currently shuts down server, needs to be able to restart server re-reading conf file) - Changed pid_file creation to after chroot, change of group and change of user - making sure the server has permission to write out the pid file. - Remove use of "use subs" in PreFork. 0.47 Mar 08 07:03 2001 - Fix reverse lookup bug - thanks to Jonathan J. Miner for pointing out the missing pieces. - Cleaned up pod examples - Clarified some of the pod 0.46 Mar 05 07:37 2001 - secure removal of pid_file - only happens on success - possibly should only happen after process has become another user - secure removal of lock_file - only happens if we generated it - added child_init_hook and child_finish_hook to PreFork - changed pre_configure_hook to configure_hook - added simple httpd example script 0.45 Mar 02 00:44 2001 - clean up make process. - change version to hard coded number. - improve testing scripts 0.44 Mar 01 00:55 2001 - partitioned properties in single hashref value. - changed versioning system to use cvs revision. - general clean up and add documentation. 0.43 Feb 28 01:08 2001 - this revision and last add bulk of documentation. -various clean ups 0.4.1 Feb 26 17:48 2001 - first build. Up to this point many revisions, bug fixes and optimizations had been made. 0.1.0 Feb 08 06:28 2001 - first cvs check in. Up to this point, much thought and research had gone into the server. Net-Server-2.008/bin/0000755000175000017500000000000012334210320012737 5ustar paulpaulNet-Server-2.008/bin/net-server0000755000175000017500000000654112331755703015005 0ustar paulpaul#!/usr/bin/env perl package net_server; use strict; use warnings; if (grep {$_ eq '--help' || $_ eq '-h'} @ARGV) { require Pod::Usage; Pod::Usage::pod2usage(-verbose => 1); exit; } my $pkg; if (@ARGV && $ARGV[0] && $ARGV[0] =~ /^(\w+)$/ && ($pkg = $1) && eval { require "Net/Server/$pkg.pm" } ) { $pkg = "Net::Server::$pkg"; } else { if ($pkg && grep {-e "$_/Net/Server/$pkg.pm"} @INC) { die "Error trying to become a Net::Server::$pkg:\n\n$@"; } $pkg = 'Net::Server::MultiType'; } require base; import base $pkg; __PACKAGE__->run; exit; sub default_port { my $self = shift; return 8080 if $> && $self->isa('Net::Server::HTTP'); return $self->SUPER::default_port; } __END__ =head1 NAME net-server - Base Net::Server starting module =head1 SYNOPSIS net-server [base type] [net server arguments] net-server PreFork ipv '*' net-server HTTP net-server HTTP app foo.cgi net-server HTTP app foo.cgi app /=bar.cgi net-server HTTP port 8080 port 8443/ssl ipv '*' server_type PreFork --SSL_key_file=my.key --SSL_cert_file=my.crt access_log_file STDERR =head1 DESCRIPTION The net-server program gives a simple way to test out code and try port connection parameters. Though the running server can be robust enough for full tim use, it is anticipated that this binary will just be used for basic testing of net-server ports, acting as a simple echo server, or for running development scripts as CGI. =head1 OPTIONS =over 4 =item C The very first argument may be a Net::Server flavor. This is given as shorthand for writing out server_type "ServerFlavor". Additionally, this allows types such as HTTP and PSGI, which are not true Net::Server base types, to subclass other server types via an additional server_type argument. net-server PreFork net-server HTTP # becomes a HTTP server in the Fork flavor net-server HTTP server_type PreFork # preforking HTTP server =item C Port to bind upon. Default is 80 if running a HTTP server as root, 8080 if running a HTTP server as non-root, or 20203 otherwise. Multiple value can be given for binding to multiple ports. All of the methods for specifying port attributes enumerated in L and L are available here. net-server port 20201 net-server port 20202 net-server port 20203/IPv6 =item C Host to bind to. Default is *. Will bind to an IPv4 socket if an IPv4 address is given. Will bind to an IPv6 socket if an IPv6 address is given (requires installation of IO::Socket::INET6). If a hostname is given and C is still set to 4, an IPv4 socket will be created. If a hostname is given and C is set to 6, an IPv6 socket will be created. If a hostname is given and C is set to * (default), a lookup will be performed and any available IPv4 or IPv6 addresses will be bound. The C parameter can be set directly, or passed along in the port, or additionally can be passed as part of the hostname. net-server host localhost net-server host localhost/IPv4 =back There are many more options available. Please see the L documentation. =head1 AUTHOR Paul Seamons =head1 LICENSE This package may be distributed under the terms of either the GNU General Public License or the Perl Artistic License =cut Net-Server-2.008/META.yml0000664000175000017500000000107012334210320013440 0ustar paulpaul--- abstract: 'Extensible, general Perl server engine' author: - 'Paul Seamons and Rob Brown ' build_requires: ExtUtils::MakeMaker: 0 configure_requires: ExtUtils::MakeMaker: 0 dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 6.66, CPAN::Meta::Converter version 2.133380' license: unknown meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: Net-Server no_index: directory: - t - inc requires: IO::Socket: 0 POSIX: 0 Socket: 0 Time::HiRes: 0 version: 2.008 Net-Server-2.008/lib/0000755000175000017500000000000012334210320012735 5ustar paulpaulNet-Server-2.008/lib/Net/0000755000175000017500000000000012334210320013463 5ustar paulpaulNet-Server-2.008/lib/Net/Server.pod0000644000175000017500000017737012331756053015473 0ustar paulpaul=head1 NAME Net::Server - Extensible, general Perl server engine =head1 SYNOPSIS #!/usr/bin/perl -w -T package MyPackage; use base qw(Net::Server); sub process_request { my $self = shift; while () { s/[\r\n]+$//; print "You said '$_'\015\012"; # basic echo last if /quit/i; } } MyPackage->run(port => 160, ipv => '*'); # one liner to get going quickly perl -e 'use base qw(Net::Server); main->run(port => 20208)' NOTE: beginning in Net::Server 2.005, the default value for ipv is IPv* meaning that if no host is passed, or a hostname is past, any available IPv4 and IPv6 sockets will be bound. You can force IPv4 only by adding an ipv => 4 configuration in any of the half dozen ways we let you specify it. =head1 FEATURES * Full IPv6 support * Working SSL sockets and https (both with and without IO::Socket::SSL) * Single Server Mode * Inetd Server Mode * Preforking Simple Mode (PreForkSimple) * Preforking Managed Mode (PreFork) * Forking Mode * Multiplexing Mode using a single process * Multi port accepts on Single, Preforking, and Forking modes * Basic HTTP Daemon (supports IPv6, SSL, full apache style logs) * Basic PSGI Daemon * Simultaneous accept/recv on tcp/udp/unix, ssl/tcp, and IPv4/IPv6 sockets * Safe signal handling in Fork/PreFork avoids perl signal trouble * User customizable hooks * Chroot ability after bind * Change of user and group after bind * Basic allow/deny access control * Pluggable logging (Sys::Syslog, Log::Log4perl, log_file, STDERR, or your own) * HUP able server (clean restarts via sig HUP) * Graceful shutdowns (via sig QUIT) * Hot deploy in Fork and PreFork modes (via sig TTIN and TTOU) * Dequeue ability in all Fork and PreFork modes. * Taint clean * Written in Perl * Protection against buffer overflow * Clean process flow * Extensibility =head1 DESCRIPTION C is an extensible, generic Perl server engine. C attempts to be a generic server as in C and C. It includes with it the ability to run as an inetd process (C), a single connection server (C or C), a forking server (C), a preforking server which maintains a constant number of preforked children (C), or as a managed preforking server which maintains the number of children based on server load (C). In all but the inetd type, the server provides the ability to connect to one or to multiple server ports. The additional server types are made possible via "personalities" or sub classes of the C. By moving the multiple types of servers out of the main C class, the C concept is easily extended to other types (in the near future, we would like to add a "Thread" personality). C borrows several concepts from the Apache Webserver. C uses "hooks" to allow custom servers such as SMTP, HTTP, POP3, etc. to be layered over the base C class. In addition the C class borrows concepts of min_start_servers, max_servers, and min_waiting servers. C also uses the concept of an flock serialized accept when accepting on multiple ports (PreFork can choose between flock, IPC::Semaphore, and pipe to control serialization). =head1 PERSONALITIES C is built around a common class (Net::Server) and is extended using sub classes, or C. Each personality inherits, overrides, or enhances the base methods of the base class. Included with the Net::Server package are several basic personalities, each of which has their own use. =over 4 =item Fork Found in the module Net/Server/Fork.pm (see L). This server binds to one or more ports and then waits for a connection. When a client request is received, the parent forks a child, which then handles the client and exits. This is good for moderately hit services. =item INET Found in the module Net/Server/INET.pm (see L). This server is designed to be used with inetd. The C, C, C, and C are all overridden as these services are taken care of by the INET daemon. =item MultiType Found in the module Net/Server/MultiType.pm (see L). This server has no server functionality of its own. It is designed for servers which need a simple way to easily switch between different personalities. Multiple C parameters may be given and Net::Server::MultiType will cycle through until it finds a class that it can use. =item Multiplex Found in the module Net/Server/Multiplex.pm (see L). This server binds to one or more ports. It uses IO::Multiplex to multiplex between waiting for new connections and waiting for input on currently established connections. This personality is designed to run as one process without forking. The C method is never used but the C callback is used instead (see also L). See examples/samplechat.pl for an example using most of the features of Net::Server::Multiplex. =item PreForkSimple Found in the module Net/Server/PreFork.pm (see L). This server binds to one or more ports and then forks C child process. The server will make sure that at any given time there are always C available to receive a client request. Each of these children will process up to C client connections. This type is good for a heavily hit site that can dedicate max_server processes no matter what the load. It should scale well for most applications. Multi port accept is accomplished using either flock, IPC::Semaphore, or pipe to serialize the children. Serialization may also be switched on for single port in order to get around an OS that does not allow multiple children to accept at the same time. For a further discussion of serialization see L. =item PreFork Found in the module Net/Server/PreFork.pm (see L). This server binds to one or more ports and then forks C child process. The server will make sure that at any given time there are at least C but not more than C available to receive a client request, up to C. Each of these children will process up to C client connections. This type is good for a heavily hit site, and should scale well for most applications. Multi port accept is accomplished using either flock, IPC::Semaphore, or pipe to serialize the children. Serialization may also be switched on for single port in order to get around an OS that does not allow multiple children to accept at the same time. For a further discussion of serialization see L. =item Single All methods fall back to Net::Server. This personality is provided only as parallelism for Net::Server::MultiType. =item HTTP Not a distinct personality. Provides a basic HTTP daemon. This can be combined with the SSL or SSLEAY proto to provide an HTTPS Daemon. See L. =back C was partially written to make it easy to add new personalities. Using separate modules built upon an open architecture allows for easy addition of new features, a separate development process, and reduced code bloat in the core module. =head1 SOCKET ACCESS Once started, the Net::Server will take care of binding to port and waiting for connections. Once a connection is received, the Net::Server will accept on the socket and will store the result (the client connection) in $self-E{server}-E{client}. This property is a Socket blessed into the the IO::Socket classes. UDP servers are slightly different in that they will perform a B instead of an B. To make programming easier, during the post_accept phase, STDIN and STDOUT are opened to the client connection. This allows for programs to be written using ESTDINE and print "out\n" to print to the client connection. UDP will require using a -Esend call. =head1 SAMPLE CODE The following is a very simple server. The main functionality occurs in the process_request method call as shown below. Notice the use of timeouts to prevent Denial of Service while reading. (Other examples of using C can, or will, be included with this distribution). #!/usr/bin/perl -w -T package MyPackage; use strict; use base qw(Net::Server::PreFork); # any personality will do MyPackage->run; # over-ride the default echo handler sub process_request { my $self = shift; eval { local $SIG{'ALRM'} = sub { die "Timed Out!\n" }; my $timeout = 30; # give the user 30 seconds to type some lines my $previous_alarm = alarm($timeout); while () { s/\r?\n$//; print "You said '$_'\r\n"; alarm($timeout); } alarm($previous_alarm); }; if ($@ =~ /timed out/i) { print STDOUT "Timed Out.\r\n"; return; } } 1; Playing this file from the command line will invoke a Net::Server using the PreFork personality. When building a server layer over the Net::Server, it is important to use features such as timeouts to prevent Denial Of Service attacks. Net::Server comes with a built in echo server by default. You can test it out by simply running the following from the commandline: net-server If you wanted to try another flavor you could try net-server PreFork If you wanted to try out a basic HTTP server you could use net-server HTTP Or if you wanted to test out a CGI you are writing you could use net-server HTTP --app ../../mycgi.cgi =head1 ARGUMENTS There are at least five possible ways to pass arguments to Net::Server. They are I, I, I, I, I, or I. The C method is used to determine which arguments the server will search for and can be used to extend the parsed parameters. Any arguments found from the command line, parameters passed to run, and arguments found in the conf_file will be matched against the keys of the options template. Any commandline parameters that do not match will be left in place and can be further processed by the server in the various hooks (by looking at @ARGV). Arguments passed to new will automatically win over any other options (this can be used if you would like to disallow a user passing in other arguments). Arguments consist of key value pairs. On the commandline these pairs follow the POSIX fashion of C<--key value> or C<--key=value>, and also C. In the conf file the parameter passing can best be shown by the following regular expression: ($key,$val)=~/^(\w+)\s+(\S+?)\s+$/. Passing arguments to the run method is done as follows: C<run(key1 => 'val1')>>. Passing arguments via a prebuilt object can best be shown in the following code: #!/usr/bin/perl -w -T package MyPackage; use strict; use base qw(Net::Server); my $server = MyPackage->new({ key1 => 'val1', }); $server->run; All five methods for passing arguments may be used at the same time. Once an argument has been set, it is not over written if another method passes the same argument. C will look for arguments in the following order: 1) Arguments passed to the C method. 2) Arguments passed on command line. 3) Arguments passed to the C method. 4) Arguments passed via a conf file. 5) Arguments set in the C method. Additionally the following hooks are available: 1) Arguments set in the configure_hook (occurs after new but before any of the other areas are checked). 2) Arguments set and validated in the post_configure_hook (occurs after all of the other areas are checked). Each of these levels will override parameters of the same name specified in subsequent levels. For example, specifying --setsid=0 on the command line will override a value of "setsid 1" in the conf file. Note that the configure_hook method doesn't return values to set, but is there to allow for setting up configured values before the configure method is called. Key/value pairs used by the server are removed by the configuration process so that server layers on top of C can pass and read their own parameters. =head1 ADDING CUSTOM ARGUMENTS It is possible to add in your own custom parameters to those parsed by Net::Server. The following code shows how this is done: sub options { my $self = shift; my $prop = $self->{'server'}; my $template = shift; # setup options in the parent classes $self->SUPER::options($template); # add a single value option $prop->{'my_option'} ||= undef; $template->{'my_option'} = \ $prop->{'my_option'}; # add a multi value option $prop->{'an_arrayref_item'} ||= []; $template->{'an_arrayref_item'} = $prop->{'an_arrayref_item'}; } Overriding the C method allows for adding your own custom fields. A template hashref is passed in, that should then be modified to contain an of your custom fields. Fields which are intended to receive a single scalar value should have a reference to the destination scalar given. Fields which are intended to receive multiple values should reference the corresponding destination arrayref. You are responsible for validating your custom options once they have been parsed. The post_configure_hook is a good place to do your validation. Some emails have asked why we use this "template" method. The idea is that you are creating the the data structure to store the values in, and you are also creating a way to get the values into the data structure. The template is the way to get the values to the servers data structure. One of the possibilities (that probably isn't used that much) is that by letting you specify the mapping, you could build a nested data structure - even though the passed in arguments are flat. It also allows you to setup aliases to your names. For example, a basic structure might look like this: $prop = $self->{'server'} $prop->{'my_custom_option'} ||= undef; $prop->{'my_custom_array'} ||= []; $template = { my_custom_option => \ $prop->{'my_custom_option'}, mco => \ $prop->{'my_custom_option'}, # alias my_custom_array => $prop->{'my_custom_array'}, mca => $prop->{'my_custom_array'}, # an alias }; $template->{'mco2'} = $template->{'mco'}; # another way to alias But you could also have more complex data: $prop = $self->{'server'}; $prop->{'one_layer'} = { two_layer => [ undef, undef, ], }; $template = { param1 => \ $prop->{'one_layer'}->{'two_layer'}->[0], param2 => \ $prop->{'one_layer'}->{'two_layer'}->[1], }; This is of course a contrived example - but it does show that you can get the data from the flat passed in arguments to whatever type of structure you need - with only a little bit of effort. =head1 DEFAULT ARGUMENTS FOR Net::Server The following arguments are available in the default C or C modules. (Other personalities may use additional parameters and may optionally not use parameters from the base class.) Key Value Default conf_file "filename" undef log_level 0-4 2 log_file (filename|Sys::Syslog |Log::Log4perl) undef port \d+ 20203 host "host" "*" ipv (4|6|*) * proto (tcp|udp|unix) "tcp" listen \d+ SOMAXCONN ## syslog parameters (if log_file eq Sys::Syslog) syslog_logsock (native|unix|inet|udp |tcp|stream|console) unix (on Sys::Syslog < 0.15) syslog_ident "identity" "net_server" syslog_logopt (cons|ndelay|nowait|pid) pid syslog_facility \w+ daemon reverse_lookups 1 undef allow /regex/ none deny /regex/ none cidr_allow CIDR none cidr_deny CIDR none ## daemonization parameters pid_file "filename" undef chroot "directory" undef user (uid|username) "nobody" group (gid|group) "nobody" background 1 undef setsid 1 undef no_close_by_child (1|undef) undef ## See Net::Server::Proto::(TCP|UDP|UNIX|SSL|SSLeay|etc) ## for more sample parameters. =over 4 =item conf_file Filename from which to read additional key value pair arguments for starting the server. Default is undef. There are two ways that you can specify a default location for a conf_file. The first is to pass the default value to the run method as in: MyServer->run({ conf_file => '/etc/my_server.conf', }); If the end user passes in --conf_file=/etc/their_server.conf then the value will be overridden. The second way to do this was added in the 0.96 version. It uses the default_values method as in: sub default_values { return { conf_file => '/etc/my_server.conf', } } This method has the advantage of also being able to be overridden in the run method. If you do not want the user to be able to specify a conf_file at all, you can pass conf_file to the new method when creating your object: MyServer->new({ conf_file => '/etc/my_server.conf', })->run; If passed this way, the value passed to new will "win" over any of the other passed in values. =item log_level Ranges from 0 to 4 in level. Specifies what level of error will be logged. "O" means logging is off. "4" means very verbose. These levels should be able to correlate to syslog levels. Default is 2. These levels correlate to syslog levels as defined by the following key/value pairs: 0=>'err', 1=>'warning', 2=>'notice', 3=>'info', 4=>'debug'. =item log_file Name of log file or log subsystem to be written to. If no name is given and the write_to_log_hook is not overridden, log goes to STDERR. Default is undef. The log_file may also be the name of a Net::Server pluggable logging class. Net::Server is packaged with Sys::Syslog and Log::Log4perl. If the log_file looks like a module name, it will have "Net::Server::Log::" added to the front and it will then be required. The package should provide an C class method that returns a single function which will be used for logging. This returned function will be passed log_level, and message. If the magic name "Sys::Syslog" is used, all logging will take place via the Net::Server::Log::Sys::Syslog module. If syslog is used the parameters C, C, and C,and C may also be defined. See L. If the magic name "Log::Log4perl" is used, all logging will be directed to the Log4perl system. If used, the C, C, C may also be defined. See L. If a C is given or if C is set, STDIN and STDOUT will automatically be opened to /dev/null and STDERR will be opened to STDOUT. This will prevent any output from ending up at the terminal. =item pid_file Filename to store pid of parent process. Generally applies only to forking servers. Default is none (undef). =item port See L for further examples of configuration. Local port/socket on which to bind. If it is a low port, the process must start as root. If multiple ports are given, all will be bound at server startup. May be of the form C, C, C, C, or C, where I represents a hostname residing on the local box, where I represents either the number of the port (eg. "80") or the service designation (eg. "http"), where I represents the IP protocol version (IPv4 or IPv6 or IPv*) and where I represents the protocol to be used. See L. The following are some valid port strings: 20203 # port only localhost:20203 # host and port localhost:http # localhost bound to port 80 localhost:20203/tcp # host, port, protocol localhost:20203/tcp/IPv* # host, port, protocol and family localhost, 20203, tcp, IPv* # same localhost | 20203 | tcp | IPv* # same localhost:20203/IPv* # bind any configured interfaces for IPv4 or 6 (default) localhost:20203/IPv4/IPv6 # bind localhost on IPv4 and 6 (fails if it cannot do both) *:20203 # bind all local interfaces Additionally, when passed in the code (non-commandline, and non-config), the port may be passed as a hashref or array hashrefs of information: port => { host => 'localhost', port => '20203', ipv => 6, # IPv6 only proto => 'udp', # UDP protocol } port => [{ host => '*', port => '20203', ipv => 4, # IPv4 only proto => 'tcp', # (default) }, { host => 'localhost', port => '20204', ipv => '*', # default - all IPv4 and IPv6 interfaces tied to localhost proto => 'ssleay', # or ssl - Using SSL }], An explicit I given in a port specification overrides a default binding address (a C setting, see below). The I part may be enclosed in square brackets, but when it is a numerical IPv6 address it B be enclosed in square brackets to avoid ambiguity in parsing a port number, e.g.: "[::1]:80". However you could also use pipes, white space, or commas to separate these. Note that host and port number must come first. If the protocol is not specified, I will default to the C specified in the arguments. If C is not specified there it will default to "tcp". If I is not specified, I will default to C specified in the arguments. If C is not specified there it will default to "*". Default port is 20203. Configuration passed to new or run may be either a scalar containing a single port number or an arrayref of ports. If C is not specified it will default to "*" (Any resolved addresses under IPv4 or IPv6). If you are working with unix sockets, you may also specify C or C where type is SOCK_DGRAM or SOCK_STREAM. On systems that support it, a port value of 0 may be used to ask the OS to auto-assign a port. The value of the auto-assigned port will be stored in the NS_port property of the Net::Server::Proto::TCP object and is also available in the sockport method. When the server is processing a request, the $self->{server}->{sockport} property contains the port that was connected through. =item host Local host or addr upon which to bind port. If a value of '*' is given, the server will bind that port on all available addresses on the box. The C argument provides a default local host address if the C argument omits a host specification. See L. See L. Configuration passed to new or run may be either a scalar containing a single host or an arrayref of hosts - if the hosts array is shorter than the ports array, the last host entry will be used to augment the hosts arrary to the size of the ports array. If an IPv4 address is passed, an IPv4 socket will be created. If an IPv6 address is passed, an IPv6 socket will be created. If a hostname is given, Net::Server will look at the value of ipv (default IPv4) to determine which type of socket to create. Optionally the ipv specification can be passed as part of the hostname. host => "127.0.0.1", # an IPv4 address host => "::1", # an IPv6 address host => 'localhost', # addresses matched by localhost (default any IPv4 and/or IPv6) host => 'localhost/IPv*', # same ipv => 6, host => 'localhost', # addresses matched by localhost (IPv6) ipv => 4, host => 'localhost', # addresses matched by localhost (IPv4) ipv => 'IPv4 IPv6', host => 'localhost', # addresses matched by localhost (requires IPv6 and IPv4) host => '*', # any local interfaces (any IPv6 or IPv4) host => '*/IPv*', # same (any IPv6 or IPv4) ipv => 4, host => '*', # any local IPv4 interfaces interfaces =item proto See L. Protocol to use when binding ports. See L. As of release 2.0, Net::Server supports tcp, udp, and unix, unixdgram, ssl, and ssleay. Other types will need to be added later (or custom modules extending the Net::Server::Proto class may be used). Configuration passed to new or run may be either a scalar containing a single proto or an arrayref of protos - if the protos array is shorter than the ports array, the last proto entry will be used to augment the protos arrary to the size of the ports array. Additionally the proto may also contain the ipv specification. =item ipv (IPv4 and IPv6) See L. IPv6 is now available under Net::Server. It will be used automatically if an IPv6 address is passed, or if the ipv is set explicitly to IPv6, or if ipv is left as the default value of IPv*. This is a significant change from version 2.004 and earlier where the default value was IPv4. However, the previous behavior led to confusion on IPv6 only hosts, and on hosts that only had IPv6 entries for a local hostname. Trying to pass an IPv4 address when ipv is set to 6 (only 6 - not * or 4) will result in an error. localhost:20203 # will use IPv6 if there is a corresponding entry for localhost # it will also use IPv4 if there is a corresponding v4 entry for localhost localhost:20203:IPv* # same (default) localhost:20203:IPv6 # will use IPv6 [::1]:20203 # will use IPv6 (IPv6 style address) localhost:20203:IPv4 # will use IPv4 127.0.0.1:20203 # will use IPv4 (IPv4 style address localhost:20203:IPv4:IPv6 # will bind to both v4 and v6 - fails otherwise # or as a hashref as port => { host => "localhost", ipv => 6, # only binds IPv6 } port => { host => "localhost", ipv => 4, # only binds IPv4 } port => { host => "::1", ipv => "IPv6", # same as passing "6" } port => { host => "localhost/IPv*", # any IPv4 or IPv6 } port => { host => "localhost IPv4 IPv6", # must create both } In many proposed Net::Server solutions, IPv* was enabled by default. For versions 2.000 through 2.004, the previous default of IPv4 was used. We have attempted to make it easy to set IPv4, IPv6, or IPv*. If you do not want or need IPv6, simply set ipv to 4, pass IPv4 along in the port specification, set $ENV{'IPV'}=4; before running the server, or uninstall IO::Socket::INET6. On my local box the following command results in the following output: perl -e 'use base qw(Net::Server); main->run(host => "localhost")' Resolved [localhost]:20203 to [::1]:20203, IPv6 Resolved [localhost]:20203 to [127.0.0.1]:20203, IPv4 Binding to TCP port 20203 on host ::1 with IPv6 Binding to TCP port 20203 on host 127.0.0.1 with IPv4 My local box has IPv6 enabled and there are entries for localhost on both IPv6 ::1 and IPv4 127.0.0.1. I could also choose to explicitly bind ports rather than depending upon ipv => "*" to resolve them for me as in the following: perl -e 'use base qw(Net::Server); main->run(port => [20203,20203], host => "localhost", ipv => [4,6])' Binding to TCP port 20203 on host localhost with IPv4 Binding to TCP port 20203 on host localhost with IPv6 There is a special case of using host => "*" as well as ipv => "*". The Net::Server::Proto::_bindv6only method is used to check the system setting for C (or net.inet6.ip6.v6only). If this setting is false, then an IPv6 socket will listen for the corresponding IPv4 address. For example the address [::] (IPv6 equivalent of INADDR_ANY) will also listen for 0.0.0.0. The address ::FFFF:127.0.0.1 (IPv6) would also listen to 127.0.0.1 (IPv4). In this case, only one socket will be created because it will handle both cases (an error is returned if an attempt is made to listen to both addresses when bindv6only is false). However, if net.ipv6.bindv6only (or equivalent) is true, then a hostname (such as *) resolving to both a IPv4 entry as well as an IPv6 will result in both an IPv4 socket as well as an IPv6 socket. On my linux box which defaults to net.ipv6.bindv6only=0, the following is output. perl -e 'use base qw(Net::Server); main->run(host => "*")' Resolved [*]:8080 to [::]:8080, IPv6 Not including resolved host [0.0.0.0] IPv4 because it will be handled by [::] IPv6 Binding to TCP port 8080 on host :: with IPv6 If I issue a C, the following is output. perl -e 'use base qw(Net::Server); main->run(host => "*")' Resolved [*]:8080 to [0.0.0.0]:8080, IPv4 Resolved [*]:8080 to [::]:8080, IPv6 Binding to TCP port 8080 on host 0.0.0.0 with IPv4 Binding to TCP port 8080 on host :: with IPv6 BSD differs from linux and generally defaults to net.inet6.ip6.v6only=0. If it cannot be determined on your OS, it will default to false and the log message will change from "it will be handled" to "it should be handled" (if you have a non-resource intensive way to check on your platform, feel free to email me). Be sure to check the logs as you test your server to make sure you have bound the ports you desire. You can always pass in individual explicit IPv4 and IPv6 port specifications if you need. For example, if your system has both IPv4 and IPv6 interfaces but you'd only like to bind to IPv6 entries, then you should use a hostname of [::] instead of [*]. If bindv6only (or equivalent) is false, and you receive an IPv4 connection on a bound IPv6 port, the textual representation of the peer's IPv4 address will typically be in a form of an IPv4-mapped IPv6 addresses, e.g. "::FFFF:127.0.0.1" . The ipv parameter was chosen because it does not conflict with any other existing usage, it is very similar to ipv4 or ipv6, it allows for user code to not need to know about Socket::AF_INET or Socket6::AF_INET6 or Socket::AF_UNSPEC, and it is short. =item listen See L. Not used with udp protocol (or UNIX SOCK_DGRAM). =item reverse_lookups Specify whether to lookup the hostname of the connected IP. Information is cached in server object under C property. Default is to not use reverse_lookups (undef). =item allow/deny May be specified multiple times. Contains regex to compare to incoming peeraddr or peerhost (if reverse_lookups has been enabled). If allow or deny options are given, the incoming client must match an allow and not match a deny or the client connection will be closed. Defaults to empty array refs. =item cidr_allow/cidr_deny May be specified multiple times. Contains a CIDR block to compare to incoming peeraddr. If cidr_allow or cidr_deny options are given, the incoming client must match a cidr_allow and not match a cidr_deny or the client connection will be closed. Defaults to empty array refs. =item chroot Directory to chroot to after bind process has taken place and the server is still running as root. Defaults to undef. =item user Userid or username to become after the bind process has occured. Defaults to "nobody." If you would like the server to run as root, you will have to specify C equal to "root". =item group Groupid or groupname to become after the bind process has occured. Defaults to "nobody." If you would like the server to run as root, you will have to specify C equal to "root". =item background Specifies whether or not the server should fork after the bind method to release itself from the command line. Defaults to undef. Process will also background if C is set. =item setsid Specifies whether or not the server should fork after the bind method to release itself from the command line and then run the C command to truly daemonize. Defaults to undef. If a C is given or if C is set, STDIN and STDOUT will automatically be opened to /dev/null and STDERR will be opened to STDOUT. This will prevent any output from ending up at the terminal. =item no_close_by_child Boolean. Specifies whether or not a forked child process has permission or not to shutdown the entire server process. If set to 1, the child may NOT signal the parent to shutdown all children. Default is undef (not set). =item no_client_stdout Boolean. Default undef (not set). Specifies that STDIN and STDOUT should not be opened on the client handle once a connection has been accepted. By default the Net::Server will open STDIN and STDOUT on the client socket making it easier for many types of scripts to read directly from and write directly to the socket using normal print and read methods. Disabling this is useful on clients that may be opening their own connections to STDIN and STDOUT. This option has no affect on STDIN and STDOUT which has a magic client property that is tied to the already open STDIN and STDOUT. =item leave_children_open_on_hup Boolean. Default undef (not set). If set, the parent will not attempt to close child processes if the parent receives a SIG HUP. The parent will rebind the the open port and begin tracking a fresh set of children. Children of a Fork server will exit after their current request. Children of a Prefork type server will finish the current request and then exit. Note - the newly restarted parent will start up a fresh set of servers on fork servers. The new parent will attempt to keep track of the children from the former parent but custom communication channels (open pipes from the child to the old parent) will no longer be available to the old child processes. New child processes will still connect properly to the new parent. =item sig_passthrough Default none. Allow for passing requested signals through to children. Takes a single signal name, a comma separated list of names, or an arrayref of signal names. It first sends the signals to the children before calling any currently registered signal by that name. =item tie_client_stdout Default undef. If set will use Net::Server::TiedHandle tied interface for STDIN and STDOUT. This interface allows SSL and SSLEAY to work. It also allows for intercepting read and write via the tied_stdin_callback and tied_stdout_callback. =item tied_stdin_callback Default undef. Called during a read of STDIN data if tie_client_stdout has been set, or if the client handle's tie_stdout method returns true. It is passed the client connection, the name of the method that would be called, and the arguments that are being passed. The callback is then responsible for calling that method on the handle or for performing some other input operation. =item tied_stdout_callback Default undef. Called during a write of data to STDOUT if tie_client_stdout has been set, or if the client handle's tie_stdout method returns true. It is passed the client connection, the name of the method that would be called, and the arguments that are being passed. The callback is then responsible for calling that method on the handle or for performing some other output operation. =back =head1 PROPERTIES All of the C listed above become properties of the server object under the same name. These properties, as well as other internal properties, are available during hooks and other method calls. The structure of a Net::Server object is shown below: $self = bless({ server => { key1 => 'val1', # more key/vals }, }, 'Net::Server'); This structure was chosen so that all server related properties are grouped under a single key of the object hashref. This is so that other objects could layer on top of the Net::Server object class and still have a fairly clean namespace in the hashref. You may get and set properties in two ways. The suggested way is to access properties directly via my $val = $self->{server}->{key1}; Accessing the properties directly will speed the server process - though some would deem this as bad style. A second way has been provided for object oriented types who believe in methods. The second way consists of the following methods: my $val = $self->get_property( 'key1' ); my $self->set_property( key1 => 'val1' ); Properties are allowed to be changed at any time with caution (please do not undef the sock property or you will close the client connection). =head1 CONFIGURATION FILE C allows for the use of a configuration file to read in server parameters. The format of this conf file is simple key value pairs. Comments and blank lines are ignored. #-------------- file test.conf -------------- ### user and group to become user somebody group everybody # logging ? log_file /var/log/server.log log_level 3 pid_file /tmp/server.pid # optional syslog directive # used in place of log_file above #log_file Sys::Syslog #syslog_logsock unix #syslog_ident myserver #syslog_logopt pid|cons # access control allow .+\.(net|com) allow domain\.com deny a.+ cidr_allow 127.0.0.0/8 cidr_allow 192.0.2.0/24 cidr_deny 192.0.2.4/30 # background the process? background 1 # ports to bind (this should bind # 127.0.0.1:20205 on IPv6 and # localhost:20204 on IPv4) # See Net::Server::Proto host 127.0.0.1 ipv IPv6 port localhost:20204/IPv4 port 20205 # reverse lookups ? # reverse_lookups on #-------------- file test.conf -------------- =head1 PROCESS FLOW The process flow is written in an open, easy to override, easy to hook, fashion. The basic flow is shown below. This is the flow of the C<$self-Erun> method. $self->configure_hook; $self->configure(@_); $self->post_configure; $self->post_configure_hook; $self->pre_bind; $self->bind; $self->post_bind_hook; $self->post_bind; $self->pre_loop_hook; $self->loop; ### routines inside a standard $self->loop # $self->accept; # $self->run_client_connection; # $self->done; $self->pre_server_close_hook; $self->server_close; The server then exits. During the client processing phase (C<$self-Erun_client_connection>), the following represents the program flow: $self->post_accept; $self->get_client_info; $self->post_accept_hook; if ($self->allow_deny && $self->allow_deny_hook) { $self->process_request; } else { $self->request_denied_hook; } $self->post_process_request_hook; $self->post_process_request; $self->post_client_connection_hook; The process then loops and waits for the next connection. For a more in depth discussion, please read the code. During the server shutdown phase (C<$self-Eserver_close>), the following represents the program flow: $self->close_children; # if any $self->post_child_cleanup_hook; if (Restarting server) { $self->restart_close_hook(); $self->hup_server; } $self->shutdown_sockets; $self->server_exit; =head1 MAIN SERVER METHODS =over 4 =item C<$self-Erun> This method incorporates the main process flow. This flow is listed above. The method run may be called in any of the following ways. MyPackage->run(port => 20201); MyPackage->new({port => 20201})->run; my $obj = bless {server=>{port => 20201}}, 'MyPackage'; $obj->run; The ->run method should typically be the last method called in a server start script (the server will exit at the end of the ->run method). =item C<$self-Econfigure> This method attempts to read configurations from the commandline, from the run method call, or from a specified conf_file (the conf_file may be specified by passed in parameters, or in the default_values). All of the configured parameters are then stored in the {"server"} property of the Server object. =item C<$self-Epost_configure> The post_configure hook begins the startup of the server. During this method running server instances are checked for, pid_files are created, log_files are created, Sys::Syslog is initialized (as needed), process backgrounding occurs and the server closes STDIN and STDOUT (as needed). =item C<$self-Epre_bind> This method is used to initialize all of the socket objects used by the server. =item C<$self-Ebind> This method actually binds to the inialized sockets (or rebinds if the server has been HUPed). =item C<$self-Epost_bind> During this method priveleges are dropped. The INT, TERM, and QUIT signals are set to run server_close. Sig PIPE is set to IGNORE. Sig CHLD is set to sig_chld. And sig HUP is set to call sig_hup. Under the Fork, PreFork, and PreFork simple personalities, these signals are registered using Net::Server::SIG to allow for safe signal handling. =item C<$self-Eloop> During this phase, the server accepts incoming connections. The behavior of how the accepting occurs and if a child process handles the connection is controlled by what type of Net::Server personality the server is using. Net::Server and Net::Server single accept only one connection at a time. Net::Server::INET runs one connection and then exits (for use by inetd or xinetd daemons). Net::Server::MultiPlex allows for one process to simultaneously handle multiple connections (but requires rewriting the process_request code to operate in a more "packet-like" manner). Net::Server::Fork forks off a new child process for each incoming connection. Net::Server::PreForkSimple starts up a fixed number of processes that all accept on incoming connections. Net::Server::PreFork starts up a base number of child processes which all accept on incoming connections. The server throttles the number of processes running depending upon the number of requests coming in (similar to concept to how Apache controls its child processes in a PreFork server). Read the documentation for each of the types for more information. =item C<$self-Eserver_close> This method is called once the server has been signaled to end, or signaled for the server to restart (via HUP), or the loop method has been exited. This method takes care of cleaning up any remaining child processes, setting appropriate flags on sockets (for HUPing), closing up logging, and then closing open sockets. Can optionally be passed an exit value that will be passed to the server_exit call. =item C<$self-Eserver_exit> This method is called at the end of server_close. It calls exit, but may be overridden to do other items. At this point all services should be shut down. Can optionally be passed an exit value that will be passed to the exit call. =back =head1 MAIN CLIENT CONNECTION METHODS =over 4 =item C<$self-Erun_client_connection> This method is run after the server has accepted and received a client connection. The full process flow is listed above under PROCESS FLOWS. This method takes care of handling each client connection. =item C<$self-Epost_accept> This method opens STDIN and STDOUT to the client socket. This allows any of the methods during the run_client_connection phase to print directly to and read directly from the client socket. =item C<$self-Eget_client_info> This method looks up information about the client connection such as ip address, socket type, and hostname (as needed). =item C<$self-Eallow_deny> This method uses the rules defined in the allow and deny configuration parameters to determine if the ip address should be accepted. =item C<$self-Eprocess_request> This method is intended to handle all of the client communication. At this point STDIN and STDOUT are opened to the client, the ip address has been verified. The server can then interact with the client connection according to whatever API or protocol the server is implementing. Note that the stub implementation uses STDIN and STDOUT and will not work if the no_client_stdout flag is set. This is the main method to override. The default method implements a simple echo server that will repeat whatever is sent. It will quit the child if "quit" is sent, and will exit the server if "exit" is sent. As of version 2.000, the client handle is passed as an argument. =item C<$self-Epost_process_request> This method is used to clean up the client connection and to handle any parent/child accounting for the forking servers. =back =head1 HOOKS C provides a number of "hooks" allowing for servers layered on top of C to respond at different levels of execution without having to "SUPER" class the main built-in methods. The placement of the hooks can be seen in the PROCESS FLOW section. Almost all of the default hook methods do nothing. To use a hook you simply need to override the method in your subclass. For example to add your own post_configure_hook you could do something like the following: package MyServer; sub post_configure_hook { my $self = shift; my $prop = $self->{'server'}; # do some validation here } The following describes the hooks available in the plain Net::Server class (other flavors such as Fork or PreFork have additional hooks). =over 4 =item C<$self-Econfigure_hook()> This hook takes place immediately after the C<-Erun()> method is called. This hook allows for setting up the object before any built in configuration takes place. This allows for custom configurability. =item C<$self-Epost_configure_hook()> This hook occurs just after the reading of configuration parameters and initiation of logging and pid_file creation. It also occurs before the C<-Epre_bind()> and C<-Ebind()> methods are called. This hook allows for verifying configuration parameters. =item C<$self-Epost_bind_hook()> This hook occurs just after the bind process and just before any chrooting, change of user, or change of group occurs. At this point the process will still be running as the user who started the server. =item C<$self-Epre_loop_hook()> This hook occurs after chroot, change of user, and change of group has occured. It allows for preparation before looping begins. =item C<$self-Ecan_read_hook()> This hook occurs after a socket becomes readible on an accept_multi_port request (accept_multi_port is used if there are multiple bound ports to accept on, or if the "multi_port" configuration parameter is set to true). This hook is intended to allow for processing of arbitrary handles added to the IO::Select used for the accept_multi_port. These handles could be added during the post_bind_hook. No internal support is added for processing these handles or adding them to the IO::Socket. Care must be used in how much occurs during the can_read_hook as a long response time will result in the server being susceptible to DOS attacks. A return value of true indicates that the Server should not pass the readible handle on to the post_accept and process_request phases. It is generally suggested that other avenues be pursued for sending messages via sockets not created by the Net::Server. =item C<$self-Epost_accept_hook()> This hook occurs after a client has connected to the server. At this point STDIN and STDOUT are mapped to the client socket. This hook occurs before the processing of the request. =item C<$self-Eallow_deny_hook()> This hook allows for the checking of ip and host information beyond the C<$self-Eallow_deny()> routine. If this hook returns 1, the client request will be processed, otherwise, the request will be denied processing. As of version 2.000, the client connection is passed as an argument. =item C<$self-Erequest_denied_hook()> This hook occurs if either the C<$self-Eallow_deny()> or C<$self-Eallow_deny_hook()> have taken place. =item C<$self-Epost_process_request_hook()> This hook occurs after the processing of the request, but before the client connection has been closed. =item C<$self-Epost_client_connection_hook> This is one final hook that occurs at the very end of the run_client_connection method. At this point all other methods and hooks that will run during the run_client_connection have finished and the client connection has already been closed. item C<$self-Eother_child_died_hook($pid)> Net::Server takes control of signal handling and child process cleanup; this makes it difficult to tell when a child process terminates if that child process was not started by Net::Server itself. If Net::Server notices another child process dying that it did not start, it will fire this hook with the PID of the terminated process. =item C<$self-Epre_server_close_hook()> This hook occurs before the server begins shutting down. =item C<$self-Ewrite_to_log_hook> This hook handles writing to log files. The default hook is to write to STDERR, or to the filename contained in the parameter C. The arguments passed are a log level of 0 to 4 (4 being very verbose), and a log line. If log_file is equal to "Sys::Syslog", then logging will go to Sys::Syslog and will bypass the write_to_log_hook. =item C<$self-Efatal_hook> This hook occurs when the server has encountered an unrecoverable error. Arguments passed are the error message, the package, file, and line number. The hook may close the server, but it is suggested that it simply return and use the built in shut down features. =item C<$self-Epost_child_cleanup_hook> This hook occurs in the parent server process after all children have been shut down and just before the server either restarts or exits. It is intended for additional cleanup of information. At this point pid_files and lockfiles still exist. =item C<$self-Erestart_open_hook> This hook occurs if a server has been HUPed (restarted via the HUP signal. It occurs just before reopening to the filenos of the sockets that were already opened. =item C<$self-Erestart_close_hook> This hook occurs if a server has been HUPed (restarted via the HUP signal. It occurs just before restarting the server via exec. =item C<$self-Echild_init_hook()> This hook is called during the forking servers. It is also called during run_dequeue. It runs just after the fork and after signals have been cleaned up. If it is a dequeue process, the string 'dequeue' will be passed as an argument. If your child processes will be needing random numbers, this hook is a good location to initialize srand (forked processes maintain the same random seed unless changed). sub child_init_hook { # from perldoc -f srand srand(time ^ $$ ^ unpack "%L*", `ps axww | gzip -f`); } =item C<$self-Epre_fork_hook()> Similar to the child_init_hook, but occurs just before the fork. =item C<$self-Echild_finish_hook()> Similar to the child_init_hook, but ran when the forked process is about to finish up. =back =head1 OTHER METHODS =over 4 =item C<$self-Edefault_values> Allow for returning configuration values that will be used if no other value could be found. Should return a hashref. sub default_values { return { port => 20201, }; } =item C<$self-Ehandle_syslog_error> Called when log_file is set to 'Sys::Syslog' and an error occurs while writing to the syslog. It is passed two arguments, the value of $@, and an arrayref containing the arguments that were passed to the log method when the error occured. =item C<$self-Elog> Parameters are a log_level and a message. If log_level is set to 'Sys::Syslog', the parameters may alternately be a log_level, a format string, and format string parameters. (The second parameter is assumed to be a format string if additional arguments are passed along). Passing arbitrary format strings to Sys::Syslog will allow the server to be vulnerable to exploit. The server maintainer should make sure that any string treated as a format string is controlled. # assuming log_file = 'Sys::Syslog' $self->log(1, "My Message with %s in it"); # sends "%s", "My Message with %s in it" to syslog $self->log(1, "My Message with %s in it", "Foo"); # sends "My Message with %s in it", "Foo" to syslog If log_file is set to a file (other than Sys::Syslog), the message will be appended to the log file by calling the write_to_log_hook. If the log_file is Sys::Syslog and an error occurs during write, the handle_syslog_error method will be called and passed the error exception. The default option of handle_syslog_error is to die - but could easily be told to do nothing by using the following code in your subclassed server: sub handle_syslog_error {} It the log had been closed, you could attempt to reopen it in the error handler with the following code: sub handle_syslog_error { my $self = shift; $self->open_syslog; } =item C<$self-Enew> As of Net::Server 0.91 there is finally a "new" method. This method takes a class name and an argument hashref as parameters. The argument hashref becomes the "server" property of the object. package MyPackage; use base qw(Net::Server); my $obj = MyPackage->new({port => 20201}); # same as my $obj = bless {server => {port => 20201}}, 'MyPackage'; =item C<$self-Eopen_syslog> Called during post_configure when the log_file option is set to 'Sys::Syslog'. By default it use the parsed configuration options listed in this document. If more custom behavior is desired, the method could be overridden and Sys::Syslog::openlog should be called with the custom parameters. =item C<$self-Eshutdown_sockets> This method will close any remaining open sockets. This is called at the end of the server_close method. =back =head1 RESTARTING Each of the server personalities (except for INET), support restarting via a HUP signal (see "kill -l"). When a HUP is received, the server will close children (if any), make sure that sockets are left open, and re-exec using the same commandline parameters that initially started the server. (Note: for this reason it is important that @ARGV is not modified until C<-Erun> is called). The Net::Server will attempt to find out the commandline used for starting the program. The attempt is made before any configuration files or other arguments are processed. The outcome of this attempt is stored using the method C<-Ecommandline>. The stored commandline may also be retrieved using the same method name. The stored contents will undoubtedly contain Tainted items that will cause the server to die during a restart when using the -T flag (Taint mode). As it is impossible to arbitrarily decide what is taint safe and what is not, the individual program must clean up the tainted items before doing a restart. sub configure_hook{ my $self = shift; ### see the contents my $ref = $self->commandline; use Data::Dumper; print Dumper $ref; ### arbitrary untainting - VERY dangerous my @untainted = map {/(.+)/;$1} @$ref; $self->commandline(\@untainted) } =head1 SHUTDOWN Each of the Fork and PreFork personalities support graceful shutdowns via the QUIT signal. When a QUIT is received, the parent will signal the children and then wait for them to exit. All server personalities support the normal TERM and INT signal shutdowns. =head1 HOT DEPLOY Since version 2.000, the Fork and PreFork personalities have accepted the TTIN and TTOU signals. When a TTIN is received, the max_servers is increased by 1. If a TTOU signal is received the max_servers is decreased by 1. This allows for adjusting the number of handling processes without having to restart the server. If the log_level is set to at 3, then the new value is displayed in the logs. =head1 FILES The following files are installed as part of this distribution. Net/Server.pm Net/Server/Fork.pm Net/Server/INET.pm Net/Server/MultiType.pm Net/Server/PreForkSimple.pm Net/Server/PreFork.pm Net/Server/Single.pm Net/Server/Daemonize.pm Net/Server/SIG.pm Net/Server/Proto.pm Net/Server/Proto/*.pm =head1 INSTALL Download and extract tarball before running these commands in its base directory: perl Makefile.PL make make test make install =head1 AUTHOR Paul Seamons =head1 THANKS Thanks to Rob Brown (bbb at cpan.org) for help with miscellaneous concepts such as tracking down the serialized select via flock ala Apache and the reference to IO::Select making multiport servers possible. And for researching into allowing sockets to remain open upon exec (making HUP possible). Thanks to Jonathan J. Miner for patching a blatant problem in the reverse lookups. Thanks to Bennett Todd for pointing out a problem in Solaris 2.5.1 which does not allow multiple children to accept on the same port at the same time. Also for showing some sample code from Viktor Duchovni which now represents the semaphore option of the serialize argument in the PreFork server. Thanks to I and I from http://perlmonks.org for pointing me in the right direction for determining the protocol used on a socket connection. Thanks to Jeremy Howard for numerous suggestions and for work on Net::Server::Daemonize. Thanks to Vadim for patches to implement parent/child communication on PreFork.pm. Thanks to Carl Lewis for suggesting "-" in user names. Thanks to Slaven Rezic for suggesing Reuse => 1 in Proto::UDP. Thanks to Tim Watt for adding udp_broadcast to Proto::UDP. Thanks to Christopher A Bongaarts for pointing out problems with the Proto::SSL implementation that currently locks around the socket accept and the SSL negotiation. See L. Thanks to Alessandro Zummo for pointing out various bugs including some in configuration, commandline args, and cidr_allow. Thanks to various other people for bug fixes over the years. These and future thank-you's are available in the Changes file as well as CVS comments. Thanks to Ben Cohen and tye (on Permonks) for finding and diagnosing more correct behavior for dealing with re-opening STDIN and STDOUT on the client handles. Thanks to Mark Martinec for trouble shooting other problems with STDIN and STDOUT (he proposed having a flag that is now the no_client_stdout flag). Thanks to David (DSCHWEI) on cpan for asking for the nofatal option with syslog. Thanks to Andreas Kippnick and Peter Beckman for suggesting leaving open child connections open during a HUP (this is now available via the leave_children_open_on_hup flag). Thanks to LUPE on cpan for helping patch HUP with taint on. Thanks to Michael Virnstein for fixing a bug in the check_for_dead section of PreFork server. Thanks to Rob Mueller for patching PreForkSimple to only open lock_file once during parent call. This patch should be portable on systems supporting flock. Rob also suggested not closing STDIN/STDOUT but instead reopening them to /dev/null to prevent spurious warnings. Also suggested short circuit in post_accept if in UDP. Also for cleaning up some of the child managment code of PreFork. Thanks to Mark Martinec for suggesting additional log messages for failure during accept. Thanks to Bill Nesbitt and Carlos Velasco for pointing out double decrement bug in PreFork.pm (rt #21271) Thanks to John W. Krahn for pointing out glaring precended with non-parened open and ||. Thanks to Ricardo Signes for pointing out setuid bug for perl 5.6.1 (rt #21262). Thanks to Carlos Velasco for updating the Syslog options (rt #21265). And for additional fixes later. Thanks to Steven Lembark for pointing out that no_client_stdout wasn't working with the Multiplex server. Thanks to Peter Beckman for suggesting allowing Sys::SysLog keyworks be passed through the ->log method and for suggesting we allow more types of characters through in syslog_ident. Also to Peter Beckman for pointing out that a poorly setup localhost will cause tests to hang. Thanks to Curtis Wilbar for pointing out that the Fork server called post_accept_hook twice. Changed to only let the child process call this, but added the pre_fork_hook method. And just a general Thanks You to everybody who is using Net::Server or who has contributed fixes over the years. Thanks to Paul Miller for some ->autoflush, FileHandle fixes. Thanks to Patrik Wallstrom for suggesting handling syslog errors better. Thanks again to Rob Mueller for more logic cleanup for child accounting in PreFork server. Thanks to David Schweikert for suggesting handling setlogsock a little better on newer versions of Sys::Syslog (>= 0.15). Thanks to Mihail Nasedkin for suggesting adding a hook that is now called post_client_connection_hook. Thanks to Graham Barr for adding the ability to set the check_for_spawn and min_child_ttl settings of the PreFork server. Thanks to Daniel Kahn Gillmor for adding the other_child_died_hook. Thanks to Dominic Humphries for helping not kill pid files on HUP. Thanks to Kristoffer Møllerhøj for fixing UDP on Multiplex. Thanks to mishikal for patches for helping identify un-cleaned up children. Thanks to rpkelly and tim@retout for pointing out error in header regex of HTTP. Thanks to dmcbride for some basic HTTP parsing fixes, as well as for some broken tied handle fixes. Thanks to Gareth for pointing out glaring bug issues with broken pipe and semaphore serialization. Thanks to CATONE for sending the idea for arbitrary signal passing to children. (See the sig_passthrough option) Thanks to intrigeri@boum for pointing out and giving code ideas for NS_port not functioning after a HUP. Thanks to Sergey Zasenko for adding sysread/syswrite support to SSLEAY as well as the base test. Thanks to mbarbon@users. for adding tally dequeue to prefork server. Thanks to stefanos@cpan for fixes to PreFork under Win32 Thanks to Mark Martinec for much of the initial work towards getting IPv6 going. Thanks to the munin developers and Nicolai Langfeldt for hosting the development verion of Net::Server for so long and for fixes to the allow_deny checking for IPv6 addresses. Thanks to Tatsuhiko Miyagawa for feedback, and for suggesting adding graceful shutdowns and hot deploy (max_servers adjustment). Thanks to TONVOON@cpan for submitting a patch adding Log4perl functionality. Thanks to Miko O'Sullivan for fixes to HTTP to correct tainting issues and passing initial log fixes, and for patches to fix CLOSE on tied stdout and various other HTTP issues. =head1 SEE ALSO Please see also L, L, L, L, L, L L =head1 TODO Improve test suite to fully cover code (using Devel::Cover). Anybody that wanted to send me patches to the t/*.t tests that improved coverage would earn a big thank you. =head1 CODE REPOSITORY https://github.com/rhandom/perl-net-server =head1 AUTHOR Paul Seamons http://seamons.com/ Rob Brown =head1 LICENSE This package may be distributed under the terms of either the GNU General Public License or the Perl Artistic License All rights reserved. =cut Net-Server-2.008/lib/Net/Server.pm0000644000175000017500000011367512334164757015332 0ustar paulpaul# -*- perl -*- # # Net::Server # ABSTRACT: Extensible Perl internet server # # Copyright (C) 2001-2014 # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # Rob Brown bbb@cpan,org # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # # All rights reserved. # ################################################################ package Net::Server; use strict; use Socket qw(AF_INET AF_UNIX SOCK_DGRAM SOCK_STREAM); use IO::Socket (); use IO::Select (); use POSIX (); use Net::Server::Proto (); use Net::Server::Daemonize qw(check_pid_file create_pid_file safe_fork get_uid get_gid set_uid set_gid); our $VERSION = '2.008'; sub new { my $class = shift || die "Missing class"; my $args = @_ == 1 ? shift : {@_}; return bless {server => {%$args}}, $class; } sub net_server_type { __PACKAGE__ } sub get_property { $_[0]->{'server'}->{$_[1]} } sub set_property { $_[0]->{'server'}->{$_[1]} = $_[2] } sub run { my $self = ref($_[0]) ? shift() : shift->new; # pass package or object $self->{'server'}->{'_run_args'} = [@_ == 1 ? %{$_[0]} : @_]; $self->_initialize; # configure all parameters $self->post_configure; # verification of passed parameters $self->post_configure_hook; # user customizable hook $self->pre_bind; # finalize ports to be bound $self->bind; # connect to port(s), setup selection handle for multi port $self->post_bind_hook; # user customizable hook $self->post_bind; # allow for chrooting, becoming a different user and group $self->pre_loop_hook; # user customizable hook $self->loop; # repeat accept/process cycle $self->server_close; # close the server and release the port } sub run_client_connection { my $self = shift; my $c = $self->{'server'}->{'client'}; $self->post_accept($c); # prepare client for processing $self->get_client_info($c); # determines information about peer and local $self->post_accept_hook($c); # user customizable hook my $ok = $self->allow_deny($c) && $self->allow_deny_hook($c); # do allow/deny check on client info if ($ok) { $self->process_request($c); # This is where the core functionality of a Net::Server should be. } else { $self->request_denied_hook($c); # user customizable hook } $self->post_process_request_hook($ok); # user customizable hook $self->post_process_request; # clean up client connection, etc $self->post_client_connection_hook; # one last hook } ###----------------------------------------------------------------### sub _initialize { my $self = shift; my $prop = $self->{'server'} ||= {}; $self->commandline($self->_get_commandline) if ! eval { $self->commandline }; # save for a HUP $self->configure_hook; # user customizable hook $self->configure; # allow for reading of commandline, program, and configuration file parameters my @defaults = %{ $self->default_values || {} }; # allow yet another way to pass defaults $self->process_args(\@defaults) if @defaults; } sub commandline { my $self = shift; $self->{'server'}->{'commandline'} = ref($_[0]) ? shift : \@_ if @_; return $self->{'server'}->{'commandline'} || die "commandline was not set during initialization"; } sub _get_commandline { my $self = shift; my $script = $0; $script = $ENV{'PWD'} .'/'. $script if $script =~ m|^[^/]+/| && $ENV{'PWD'}; # add absolute to relative - avoid Cwd $script =~ /^(.+)$/; # untaint for later use in hup return [$1, @ARGV] } sub configure_hook {} sub configure { my $self = shift; my $prop = $self->{'server'}; my $template = ($_[0] && ref($_[0])) ? shift : undef; $self->process_args(\@ARGV, $template) if @ARGV; # command line $self->process_args($prop->{'_run_args'}, $template) if $prop->{'_run_args'}; # passed to run if ($prop->{'conf_file'}) { $self->process_args($self->_read_conf($prop->{'conf_file'}), $template); } else { my $def = $self->default_values || {}; $self->process_args($self->_read_conf($def->{'conf_file'}), $template) if $def->{'conf_file'}; } } sub default_values { {} } sub post_configure { my $self = shift; my $prop = $self->{'server'}; $prop->{'log_level'} = 2 if ! defined($prop->{'log_level'}) || $prop->{'log_level'} !~ /^\d+$/; $prop->{'log_level'} = 4 if $prop->{'log_level'} > 4; $self->initialize_logging; if ($prop->{'pid_file'}) { # see if a daemon is already running if (! eval{ check_pid_file($prop->{'pid_file'}) }) { warn $@ if !$ENV{'BOUND_SOCKETS'}; $self->fatal(my $e = $@); } } if (! $prop->{'_is_inet'}) { # completetly daemonize by closing STDIN, STDOUT (should be done before fork) if ($prop->{'setsid'} || length($prop->{'log_file'})) { open(STDIN, '<', '/dev/null') || die "Cannot read /dev/null [$!]"; open(STDOUT, '>', '/dev/null') || die "Cannot write /dev/null [$!]"; } } if (!$ENV{'BOUND_SOCKETS'}) { # don't need to redo this if hup'ing if ($prop->{'setsid'} || $prop->{'background'}) { my $pid = eval { safe_fork() }; $self->fatal(my $e = $@) if ! defined $pid; exit(0) if $pid; $self->log(2, "Process Backgrounded"); } POSIX::setsid() if $prop->{'setsid'}; # completely remove myself from parent process } if (length($prop->{'log_file'}) && !$prop->{'log_function'}) { open STDERR, '>&_SERVER_LOG' || die "Cannot open STDERR to _SERVER_LOG [$!]"; } elsif ($prop->{'setsid'}) { # completely daemonize by closing STDERR (should be done after fork) open STDERR, '>&STDOUT' || die "Cannot open STDERR to STDOUT [$!]"; } # allow for a pid file (must be done after backgrounding and chrooting) # Remove of this pid may fail after a chroot to another location... however it doesn't interfere either. if ($prop->{'pid_file'}) { if (eval { create_pid_file($prop->{'pid_file'}) }) { $prop->{'pid_file_unlink'} = 1; } else { $self->fatal(my $e = $@); } } # make sure that allow and deny look like array refs $prop->{$_} = [] for grep {! ref $prop->{$_}} qw(allow deny cidr_allow cidr_deny); } sub initialize_logging { my $self = shift; my $prop = $self->{'server'}; if (! defined($prop->{'log_file'})) { $prop->{'log_file'} = ''; # log to STDERR return; } # pluggable logging if ($prop->{'log_file'} =~ /^([a-zA-Z]\w*(?:::[a-zA-Z]\w*)*)$/) { my $pkg = "Net::Server::Log::$prop->{'log_file'}"; (my $file = "$pkg.pm") =~ s|::|/|g; if (eval { require $file }) { $prop->{'log_function'} = $pkg->initialize($self); $prop->{'log_class'} = $pkg; return; } elsif ($file =~ /::/ || grep {-e "$_/$file"} @INC) { $self->fatal("Unable to load log module $pkg from file $file: $@"); } } # regular file based logging die "Unsecure filename \"$prop->{'log_file'}\"" if $prop->{'log_file'} !~ m|^([\:\w\.\-/\\]+)$|; $prop->{'log_file'} = $1; # open a logging file open(_SERVER_LOG, ">>", $prop->{'log_file'}) || die "Couldn't open log file \"$prop->{'log_file'}\" [$!]."; _SERVER_LOG->autoflush(1); push @{ $prop->{'chown_files'} }, $prop->{'log_file'}; } sub post_configure_hook {} sub _server_type { ref($_[0]) } sub pre_bind { # make sure we have good port parameters my $self = shift; my $prop = $self->{'server'}; my $super = $self->net_server_type; my $type = $self->_server_type; if ($self->isa('Net::Server::MultiType')) { my $base = delete($prop->{'_recursive_multitype'}) || Net::Server::MultiType->net_server_type; $super = "$super -> MultiType -> $base"; } $type .= " (type $super)" if $type ne $super; $self->log(2, $self->log_time ." $type starting! pid($$)"); $prop->{'sock'} = [grep {$_} map { $self->proto_object($_) } @{ $self->prepared_ports }]; $self->fatal("No valid socket parameters found") if ! @{ $prop->{'sock'} }; } sub prepared_ports { my $self = shift; my $prop = $self->{'server'}; my ($ports, $hosts, $protos, $ipvs) = @$prop{qw(port host proto ipv)}; $ports ||= $prop->{'ports'}; if (!defined($ports) || (ref($ports) && !@$ports)) { $ports = $self->default_port; if (!defined($ports) || (ref($ports) && !@$ports)) { $ports = default_port(); $self->log(2, "Port Not Defined. Defaulting to '$ports'"); } } my %bound; my $bind = $prop->{'_bind'} = []; for my $_port (ref($ports) ? @$ports : $ports) { my $_host = ref($hosts) ? $hosts->[ @$bind >= @$hosts ? -1 : $#$bind + 1] : $hosts; # if ports are greater than hosts - augment with the last host my $_proto = ref($protos) ? $protos->[@$bind >= @$protos ? -1 : $#$bind + 1] : $protos; my $_ipv = ref($ipvs) ? $ipvs->[ @$bind >= @$ipvs ? -1 : $#$bind + 1] : $ipvs; foreach my $info ($self->port_info($_port, $_host, $_proto, $_ipv)) { my ($port, $host, $proto, $ipv) = @$info{qw(port host proto ipv)}; # use cleaned values if ($port ne "0" && $bound{"$host\e$port\e$proto\e$ipv"}++) { $self->log(2, "Duplicate configuration (\U$proto\E) on [$host]:$port with IPv$ipv) - skipping"); next; } push @$bind, $info; } } return $bind; } sub port_info { my ($self, $port, $host, $proto, $ipv) = @_; return Net::Server::Proto->parse_info($port, $host, $proto, $ipv, $self); } sub proto_object { my ($self, $info) = @_; return Net::Server::Proto->object($info, $self); } sub bind { # bind to the port (This should serve all but INET) my $self = shift; my $prop = $self->{'server'}; if (exists $ENV{'BOUND_SOCKETS'}) { $self->restart_open_hook; $self->log(2, "Binding open file descriptors"); my %map; foreach my $info (split /\s*;\s*/, $ENV{'BOUND_SOCKETS'}) { my ($fd, $host, $port, $proto, $ipv, $orig) = split /\|/, $info; $orig = $port if ! defined $orig; # allow for things like service ports or port 0 $fd = ($fd =~ /^(\d+)$/) ? $1 : $self->fatal("Bad file descriptor"); $map{"$host|$orig|$proto|$ipv"}->{$fd} = $port; } foreach my $sock (@{ $prop->{'sock'} }) { $sock->log_connect($self); if (my $ref = $map{$sock->hup_string}) { my ($fd, $port) = each %$ref; $sock->reconnect($fd, $self, $port); delete $ref->{$fd}; delete $map{$sock->hup_string} if ! keys %$ref; } else { $self->log(2, "Added new port configuration"); $sock->connect($self); } } foreach my $str (keys %map) { foreach my $fd (keys %{ $map{$str} }) { $self->log(2, "Closing un-mapped port ($str) on fd $fd"); POSIX::close($fd); } } delete $ENV{'BOUND_SOCKETS'}; $self->{'hup_waitpid'} = 1; } else { # connect to fresh ports foreach my $sock (@{ $prop->{'sock'} }) { $sock->log_connect($self); $sock->connect($self); } } if (@{ $prop->{'sock'} } > 1 || $prop->{'multi_port'}) { $prop->{'multi_port'} = 1; $prop->{'select'} = IO::Select->new; # if more than one socket we'll need to select on it $prop->{'select'}->add($_) for @{ $prop->{'sock'} }; } else { $prop->{'multi_port'} = undef; $prop->{'select'} = undef; } } sub post_bind_hook {} sub post_bind { # secure the process and background it my $self = shift; my $prop = $self->{'server'}; if (! defined $prop->{'group'}) { $self->log(1, "Group Not Defined. Defaulting to EGID '$)'"); $prop->{'group'} = $); } elsif ($prop->{'group'} =~ /^([\w-]+(?: [\w-]+)*)$/) { $prop->{'group'} = eval { get_gid($1) }; $self->fatal(my $e = $@) if $@; } else { $self->fatal("Invalid group \"$prop->{'group'}\""); } if (! defined $prop->{'user'}) { $self->log(1, "User Not Defined. Defaulting to EUID '$>'"); $prop->{'user'} = $>; } elsif ($prop->{'user'} =~ /^([\w-]+)$/) { $prop->{'user'} = eval { get_uid($1) }; $self->fatal(my $e = $@) if $@; } else { $self->fatal("Invalid user \"$prop->{'user'}\""); } # chown any files or sockets that we need to if ($prop->{'group'} ne $) || $prop->{'user'} ne $>) { my @chown_files; push @chown_files, map {$_->NS_port} grep {$_->NS_proto =~ /^UNIX/} @{ $prop->{'sock'} }; push @chown_files, $prop->{'pid_file'} if $prop->{'pid_file_unlink'}; push @chown_files, $prop->{'lock_file'} if $prop->{'lock_file_unlink'}; push @chown_files, @{ $prop->{'chown_files'} || [] }; my $uid = $prop->{'user'}; my $gid = (split /\ /, $prop->{'group'})[0]; foreach my $file (@chown_files){ chown($uid, $gid, $file) || $self->fatal("Couldn't chown \"$file\" [$!]"); } } if ($prop->{'chroot'}) { $self->fatal("Specified chroot \"$prop->{'chroot'}\" doesn't exist.") if ! -d $prop->{'chroot'}; $self->log(2, "Chrooting to $prop->{'chroot'}"); chroot($prop->{'chroot'}) || $self->fatal("Couldn't chroot to \"$prop->{'chroot'}\": $!"); } # drop privileges eval { if ($prop->{'group'} ne $)) { $self->log(2, "Setting gid to \"$prop->{'group'}\""); set_gid($prop->{'group'} ); } if ($prop->{'user'} ne $>) { $self->log(2, "Setting uid to \"$prop->{'user'}\""); set_uid($prop->{'user'}); } }; if ($@) { if ($> == 0) { $self->fatal(my $e = $@); } elsif ($< == 0) { $self->log(2, "NOTICE: Effective UID changed, but Real UID is 0: $@"); } else { $self->log(2, my $e = $@); } } $prop->{'requests'} = 0; # record number of request $SIG{'INT'} = $SIG{'TERM'} = $SIG{'QUIT'} = sub { $self->server_close; }; $SIG{'PIPE'} = 'IGNORE'; # most cases, a closed pipe will take care of itself $SIG{'CHLD'} = \&sig_chld; # catch children (mainly for Fork and PreFork but works for any chld) $SIG{'HUP'} = sub { $self->sig_hup }; } sub sig_chld { 1 while waitpid(-1, POSIX::WNOHANG()) > 0; $SIG{'CHLD'} = \&sig_chld; } sub pre_loop_hook {} sub loop { my $self = shift; while ($self->accept) { $self->run_client_connection; last if $self->done; } } sub accept { my $self = shift; my $prop = $self->{'server'}; my $sock = undef; my $retries = 30; while ($retries--) { if ($prop->{'multi_port'}) { # with more than one port, use select to get the next one return 0 if $prop->{'_HUP'}; $sock = $self->accept_multi_port || next; # keep trying for the rest of retries return 0 if $prop->{'_HUP'}; if ($self->can_read_hook($sock)) { $retries++; next; } } else { $sock = $prop->{'sock'}->[0]; # single port is bound - just accept } $self->fatal("Received a bad sock!") if ! defined $sock; if (SOCK_DGRAM == $sock->getsockopt(Socket::SOL_SOCKET(), Socket::SO_TYPE())) { # receive a udp packet $prop->{'client'} = $sock; $prop->{'udp_true'} = 1; $prop->{'udp_peer'} = $sock->recv($prop->{'udp_data'}, $sock->NS_recv_len, $sock->NS_recv_flags); } else { # blocking accept per proto delete $prop->{'udp_true'}; $prop->{'client'} = $sock->accept(); } return 0 if $prop->{'_HUP'}; return 1 if $prop->{'client'}; $self->log(2,"Accept failed with $retries tries left: $!"); sleep(1); } $self->log(1,"Ran out of accept retries!"); return undef; } sub accept_multi_port { my @waiting = shift->{'server'}->{'select'}->can_read(); return undef if ! @waiting; return $waiting[rand @waiting]; } sub can_read_hook {} sub post_accept { my $self = shift; my $prop = $self->{'server'}; my $client = shift || $prop->{'client'}; $prop->{'requests'}++; return if $prop->{'udp_true'}; # no need to do STDIN/STDOUT in UDP if (!$client) { $self->log(1,"Client socket information could not be determined!"); return; } $client->post_accept() if $client->can("post_accept"); if (! $prop->{'no_client_stdout'}) { close STDIN; # duplicate some handles and flush them close STDOUT; if ($prop->{'tie_client_stdout'} || ($client->can('tie_stdout') && $client->tie_stdout)) { open STDIN, '<', '/dev/null' or die "Couldn't open STDIN to the client socket: $!"; open STDOUT, '>', '/dev/null' or die "Couldn't open STDOUT to the client socket: $!"; tie *STDOUT, 'Net::Server::TiedHandle', $client, $prop->{'tied_stdout_callback'} or die "Couldn't tie STDOUT: $!"; tie *STDIN, 'Net::Server::TiedHandle', $client, $prop->{'tied_stdin_callback'} or die "Couldn't tie STDIN: $!"; } elsif (defined(my $fileno = fileno $prop->{'client'})) { open STDIN, '<&', $fileno or die "Couldn't open STDIN to the client socket: $!"; open STDOUT, '>&', $fileno or die "Couldn't open STDOUT to the client socket: $!"; } else { *STDIN = \*{ $client }; *STDOUT = \*{ $client }; } STDIN->autoflush(1); STDOUT->autoflush(1); select STDOUT; } } sub get_client_info { my $self = shift; my $prop = $self->{'server'}; my $client = shift || $prop->{'client'}; if ($client->NS_proto =~ /^UNIX/) { delete @$prop{qw(sockaddr sockport peeraddr peerport peerhost)}; $self->log(3, $self->log_time." CONNECT ".$client->NS_proto." Socket: \"".$client->NS_port."\"") if $prop->{'log_level'} && 3 <= $prop->{'log_level'}; return; } if (my $sockname = $client->sockname) { $prop->{'sockaddr'} = $client->sockhost; $prop->{'sockport'} = $client->sockport; } else { @{ $prop }{qw(sockaddr sockhost sockport)} = ($ENV{'REMOTE_HOST'} || '0.0.0.0', 'inet.test', 0); # commandline } my $addr; if ($prop->{'udp_true'}) { if ($client->sockdomain == AF_INET) { ($prop->{'peerport'}, $addr) = Socket::sockaddr_in($prop->{'udp_peer'}); $prop->{'peeraddr'} = Socket::inet_ntoa($addr); } else { warn "Right here\n"; ($prop->{'peerport'}, $addr) = Socket6::sockaddr_in6($prop->{'udp_peer'}); $prop->{'peeraddr'} = Socket6->can('inet_ntop') ? Socket6::inet_ntop($client->sockdomain, $addr) : Socket::inet_ntoa($addr); } } elsif ($prop->{'peername'} = $client->peername) { $addr = $client->peeraddr; $prop->{'peeraddr'} = $client->peerhost; $prop->{'peerport'} = $client->peerport; } else { @{ $prop }{qw(peeraddr peerhost peerport)} = ('0.0.0.0', 'inet.test', 0); # commandline } if ($addr && defined $prop->{'reverse_lookups'}) { if ($INC{'Socket6.pm'} && Socket6->can('getnameinfo')) { my @res = Socket6::getnameinfo($addr, 0); $prop->{'peerhost'} = $res[0] if @res > 1; }else{ $prop->{'peerhost'} = gethostbyaddr($addr, AF_INET); } } $self->log(3, $self->log_time ." CONNECT ".$client->NS_proto ." Peer: \"[$prop->{'peeraddr'}]:$prop->{'peerport'}\"" ." Local: \"[$prop->{'sockaddr'}]:$prop->{'sockport'}\"") if $prop->{'log_level'} && 3 <= $prop->{'log_level'}; } sub post_accept_hook {} sub allow_deny { my $self = shift; my $prop = $self->{'server'}; my $sock = shift || $prop->{'client'}; # unix sockets are immune to this check return 1 if $sock && $sock->NS_proto =~ /^UNIX/; # if no allow or deny parameters are set, allow all return 1 if ! @{ $prop->{'allow'} } && ! @{ $prop->{'deny'} } && ! @{ $prop->{'cidr_allow'} } && ! @{ $prop->{'cidr_deny'} }; # work around Net::CIDR::cidrlookup() croaking, # if first parameter is an IPv4 address in IPv6 notation. my $peeraddr = ($prop->{'peeraddr'} =~ /^\s*::ffff:([0-9.]+\s*)$/) ? $1 : $prop->{'peeraddr'}; # if the addr or host matches a deny, reject it immediately foreach (@{ $prop->{'deny'} }) { return 0 if $prop->{'reverse_lookups'} && defined($prop->{'peerhost'}) && $prop->{'peerhost'} =~ /^$_$/; return 0 if $peeraddr =~ /^$_$/; } if (@{ $prop->{'cidr_deny'} }) { require Net::CIDR; return 0 if Net::CIDR::cidrlookup($peeraddr, @{ $prop->{'cidr_deny'} }); } # if the addr or host isn't blocked yet, allow it if it is allowed foreach (@{ $prop->{'allow'} }) { return 1 if $prop->{'reverse_lookups'} && defined($prop->{'peerhost'}) && $prop->{'peerhost'} =~ /^$_$/; return 1 if $peeraddr =~ /^$_$/; } if (@{ $prop->{'cidr_allow'} }) { require Net::CIDR; return 1 if Net::CIDR::cidrlookup($peeraddr, @{ $prop->{'cidr_allow'} }); } return 0; } sub allow_deny_hook { 1 } # false to deny request sub request_denied_hook {} sub process_request { # sample echo server - override for full functionality my $self = shift; my $prop = $self->{'server'}; if ($prop->{'udp_true'}) { # udp echo server my $client = shift || $prop->{'client'}; if ($prop->{'udp_data'} =~ /dump/) { require Data::Dumper; return $client->send(Data::Dumper::Dumper($self), 0); } return $client->send("You said \"$prop->{'udp_data'}\"", 0); } print "Welcome to \"".ref($self)."\" ($$)\015\012"; my $previous_alarm = alarm 30; eval { local $SIG{'ALRM'} = sub { die "Timed Out!\n" }; while () { s/[\r\n]+$//; print ref($self),":$$: You said \"$_\"\015\012"; $self->log(5, $_); # very verbose log if (/get\s+(\w+)/) { print "$1: $self->{'server'}->{$1}\015\012" } elsif (/dump/) { require Data::Dumper; print Data::Dumper::Dumper($self) } elsif (/quit/) { last } elsif (/exit/) { $self->server_close } alarm 30; # another 30 } alarm($previous_alarm); }; alarm 0; print "Timed Out.\015\012" if $@ eq "Timed Out!\n"; } sub post_process_request_hook {} sub post_client_connection_hook {} sub post_process_request { my $self = shift; $self->close_client_stdout; } sub close_client_stdout { my $self = shift; my $prop = $self->{'server'}; return if $prop->{'udp_true'}; if (! $prop->{'no_client_stdout'}) { my $t = tied *STDOUT; if ($t) { undef $t; untie *STDOUT }; $t = tied *STDIN; if ($t) { undef $t; untie *STDIN }; open(STDIN, '<', '/dev/null') || die "Cannot read /dev/null [$!]"; open(STDOUT, '>', '/dev/null') || die "Cannot write /dev/null [$!]"; } $prop->{'client'}->close; } sub done { my $self = shift; $self->{'server'}->{'done'} = shift if @_; return $self->{'server'}->{'done'}; } sub pre_fork_hook {} sub child_init_hook {} sub child_finish_hook {} sub run_dequeue { # fork off a child process to handle dequeuing my $self = shift; $self->pre_fork_hook('dequeue'); my $pid = fork; $self->fatal("Bad fork [$!]") if ! defined $pid; if (!$pid) { # child $SIG{'INT'} = $SIG{'TERM'} = $SIG{'QUIT'} = $SIG{'HUP'} = sub { $self->child_finish_hook('dequeue'); exit; }; $SIG{'PIPE'} = $SIG{'TTIN'} = $SIG{'TTOU'} = 'DEFAULT'; $self->child_init_hook('dequeue'); $self->dequeue(); $self->child_finish_hook('dequeue'); exit; } $self->log(4, "Running dequeue child $pid"); $self->{'server'}->{'children'}->{$pid}->{'status'} = 'dequeue' if $self->{'server'}->{'children'}; } sub default_port { 20203 } sub dequeue {} sub pre_server_close_hook {} sub server_close { my ($self, $exit_val) = @_; my $prop = $self->{'server'}; $SIG{'INT'} = 'DEFAULT'; ### if this is a child process, signal the parent and close ### normally the child shouldn't, but if they do... ### otherwise the parent continues with the shutdown ### this is safe for non standard forked child processes ### as they will not have server_close as a handler if (defined($prop->{'ppid'}) && $prop->{'ppid'} != $$ && ! defined($prop->{'no_close_by_child'})) { $self->close_parent; exit; } $self->pre_server_close_hook; $self->log(2, $self->log_time . " Server closing!"); if ($prop->{'kind_quit'} && $prop->{'children'}) { $self->log(3, "Attempting a slow shutdown"); $prop->{$_} = 0 for qw(min_servers max_servers); $self->hup_children; # send children signal to finish up while (1) { Net::Server::SIG::check_sigs(); $self->coordinate_children if $self->can('coordinate_children'); last if !keys %{$self->{'server'}->{'children'}}; sleep 1; } } if ($prop->{'_HUP'} && $prop->{'leave_children_open_on_hup'}) { $self->hup_children; } else { $self->close_children() if $prop->{'children'}; $self->post_child_cleanup_hook; } if (defined($prop->{'lock_file'}) && -e $prop->{'lock_file'} && defined($prop->{'lock_file_unlink'})) { unlink($prop->{'lock_file'}) || $self->log(1, "Couldn't unlink \"$prop->{'lock_file'}\" [$!]"); } if (defined($prop->{'pid_file'}) && -e $prop->{'pid_file'} && !$prop->{'_HUP'} && defined($prop->{'pid_file_unlink'})) { unlink($prop->{'pid_file'}) || $self->log(1, "Couldn't unlink \"$prop->{'pid_file'}\" [$!]"); } if ($prop->{'_HUP'}) { $self->restart_close_hook(); $self->hup_server; # execs at the end } $self->shutdown_sockets; return $self if $prop->{'no_exit_on_close'}; $self->server_exit($exit_val); } sub server_exit { my ($self, $exit_val) = @_; exit($exit_val || 0); } sub shutdown_sockets { my $self = shift; my $prop = $self->{'server'}; foreach my $sock (@{ $prop->{'sock'} }) { # unlink remaining socket files (if any) $sock->shutdown(2); unlink $sock->NS_port if $sock->NS_proto =~ /^UNIX/; } $prop->{'sock'} = []; # delete the sock objects return 1; } ### Allow children to send INT signal to parent (or use another method) ### This method is only used by forking servers sub close_parent { my $self = shift; my $prop = $self->{'server'}; die "Missing parent pid (ppid)" if ! $prop->{'ppid'}; kill 2, $prop->{'ppid'}; } ### SIG INT the children ### This method is only used by forking servers (ie Fork, PreFork) sub close_children { my $self = shift; my $prop = $self->{'server'}; return unless $prop->{'children'} && scalar keys %{ $prop->{'children'} }; foreach my $pid (keys %{ $prop->{'children'} }) { $self->log(4, "Kill TERM pid $pid"); if (kill(15, $pid) || ! kill(0, $pid)) { # if it is killable, kill it $self->delete_child($pid); } } 1 while waitpid(-1, POSIX::WNOHANG()) > 0; } sub is_prefork { 0 } sub hup_children { my $self = shift; my $prop = $self->{'server'}; return unless defined $prop->{'children'} && scalar keys %{ $prop->{'children'} }; return if ! $self->is_prefork; $self->log(2, "Sending children hup signal"); for my $pid (keys %{ $prop->{'children'} }) { $self->log(4, "Kill HUP pid $pid"); kill(1, $pid) or $self->log(2, "Failed to kill pid $pid: $!"); } } sub post_child_cleanup_hook {} ### handle sig hup ### this will prepare the server for a restart via exec sub sig_hup { my $self = shift; my $prop = $self->{'server'}; $self->log(2, "Received a SIG HUP"); my $i = 0; my @fd; $prop->{'_HUP'} = []; foreach my $sock (@{ $prop->{'sock'} }) { my $fd = POSIX::dup($sock->fileno) || $self->fatal("Cannot duplicate the socket [$!]"); # hold on to the socket copy until exec; # just temporary: any socket domain will do, # forked process will decide to use IO::Socket::INET6 if necessary $prop->{'_HUP'}->[$i] = IO::Socket::INET->new; $prop->{'_HUP'}->[$i]->fdopen($fd, 'w') || $self->fatal("Cannot open to file descriptor [$!]"); # turn off the FD_CLOEXEC bit to allow reuse on exec require Fcntl; $prop->{'_HUP'}->[$i]->fcntl(Fcntl::F_SETFD(), my $flags = ""); push @fd, $fd .'|'. $sock->hup_string; # save file-descriptor and host|port|proto|ipv $sock->close(); $i++; } delete $prop->{'select'}; # remove any blocking obstacle $ENV{'BOUND_SOCKETS'} = join "; ", @fd; if ($prop->{'leave_children_open_on_hup'} && scalar keys %{ $prop->{'children'} }) { $ENV{'HUP_CHILDREN'} = join "\n", map {"$_\t$prop->{'children'}->{$_}->{'status'}"} sort keys %{ $prop->{'children'} }; } } sub hup_server { my $self = shift; $self->log(0, $self->log_time()." Re-exec server during HUP"); delete @ENV{$self->hup_delete_env_keys}; exec @{ $self->commandline }; } sub hup_delete_env_keys { return qw(PATH) } sub restart_open_hook {} # this hook occurs if a server has been HUP'ed it occurs just before opening to the fileno's sub restart_close_hook {} # this hook occurs if a server has been HUP'ed it occurs just before exec'ing the server ###----------------------------------------------------------### sub fatal { my ($self, $error) = @_; my ($package, $file, $line) = caller; $self->fatal_hook($error, $package, $file, $line); $self->log(0, $self->log_time ." $error\n at line $line in file $file"); $self->server_close(1); } sub fatal_hook {} ###----------------------------------------------------------### sub log { my ($self, $level, $msg, @therest) = @_; my $prop = $self->{'server'}; return if ! $prop->{'log_level'}; return if $level =~ /^\d+$/ && $level > $prop->{'log_level'}; $msg = sprintf($msg, @therest) if @therest; # if multiple arguments are passed, assume that the first is a format string if ($prop->{'log_function'}) { return if eval { $prop->{'log_function'}->($level, $msg); 1 }; my $err = $@; if ($prop->{'log_class'} && $prop->{'log_class'}->can('handle_error')) { $prop->{'log_class'}->handle_log_error($self, $err, [$level, $msg]); } else { $self->handle_log_error($err, [$level, $msg]); } } return if $level !~ /^\d+$/; $self->write_to_log_hook($level, $msg); } sub handle_log_error { my ($self, $error) = @_; die $error } sub handle_syslog_error { &handle_log_error } sub write_to_log_hook { my ($self, $level, $msg) = @_; my $prop = $self->{'server'}; chomp $msg; $msg =~ s/([^\n\ -\~])/sprintf("%%%02X",ord($1))/eg; if ($prop->{'log_file'}) { print _SERVER_LOG $msg, "\n"; } elsif ($prop->{'setsid'}) { # do nothing ? } else { my $old = select STDERR; print $msg. "\n"; select $old; } } sub log_time { my ($sec,$min,$hour,$day,$mon,$year) = localtime; return sprintf "%04d/%02d/%02d-%02d:%02d:%02d", $year + 1900, $mon + 1, $day, $hour, $min, $sec; } ###----------------------------------------------------------### sub options { my $self = shift; my $ref = shift || {}; my $prop = $self->{'server'}; foreach (qw(port host proto ipv allow deny cidr_allow cidr_deny)) { if (! defined $prop->{$_}) { $prop->{$_} = []; } elsif (! ref $prop->{$_}) { $prop->{$_} = [$prop->{$_}]; # nicely turn us into an arrayref if we aren't one already } $ref->{$_} = $prop->{$_}; } foreach (qw(conf_file user group chroot log_level log_file pid_file background setsid listen reverse_lookups no_close_by_child no_client_stdout tie_client_stdout tied_stdout_callback tied_stdin_callback leave_children_open_on_hup )) { $ref->{$_} = \$prop->{$_}; } return $ref; } ### routine for parsing commandline, module, and conf file ### method has the benefit of leaving unused arguments in @ARGV sub process_args { my ($self, $args, $template) = @_; $self->options($template = {}) if ! $template || ! ref $template; if (!$_[2] && !scalar(keys %$template) && !$self->{'server'}->{'_no_options'}++) { warn "Configuration options were empty - skipping any commandline, config file, or run argument parsing.\n"; } # we want subsequent calls to not overwrite or add to previously set values so that command line arguments win my %previously_set; foreach (my $i = 0; $i < @$args; $i++) { if ($args->[$i] =~ /^(?:--)?(\w+)(?:[=\ ](\S+))?$/ && exists $template->{$1}) { my ($key, $val) = ($1, $2); splice @$args, $i, 1; if (! defined $val) { if ($i > $#$args || ($args->[$i] && $args->[$i] =~ /^--\w+/)) { $val = 1; # allow for options such as --setsid } else { $val = splice @$args, $i, 1; $val = $val->[0] if ref($val) eq 'ARRAY' && @$val == 1 && ref($template->{$key}) ne 'ARRAY'; } } $i--; $val =~ s/%([A-F0-9])/chr(hex $1)/eig if ! ref $val; if (ref $template->{$key} eq 'ARRAY') { if (! defined $previously_set{$key}) { $previously_set{$key} = scalar @{ $template->{$key} }; } next if $previously_set{$key}; push @{ $template->{$key} }, ref($val) eq 'ARRAY' ? @$val : $val; } else { if (! defined $previously_set{$key}) { $previously_set{$key} = defined(${ $template->{$key} }) ? 1 : 0; } next if $previously_set{$key}; die "Found multiple values on the configuration item \"$key\" which expects only one value" if ref($val) eq 'ARRAY'; ${ $template->{$key} } = $val; } } } } sub _read_conf { my ($self, $file) = @_; my @args; $file = ($file =~ m|^([\w\.\-\/\\\:]+)$|) ? $1 : $self->fatal("Unsecure filename \"$file\""); open my $fh, '<', $file or do { $self->fatal("Couldn't open conf \"$file\" [$!]") if $ENV{'BOUND_SOCKETS'}; warn "Couldn't open conf \"$file\" [$!]\n"; }; while (defined(my $line = <$fh>)) { push @args, $1, $2 if $line =~ m/^\s* ((?:--)?\w+) (?:\s*[=:]\s*|\s+) (\S+)/x; } close $fh; return \@args; } ###----------------------------------------------------------------### sub other_child_died_hook {} sub delete_child { my ($self, $pid) = @_; my $prop = $self->{'server'}; return $self->other_child_died_hook($pid) if ! exists $prop->{'children'}->{$pid}; # prefork server check to clear child communication if ($prop->{'child_communication'}) { if ($prop->{'children'}->{$pid}->{'sock'}) { $prop->{'child_select'}->remove($prop->{'children'}->{$pid}->{'sock'}); $prop->{'children'}->{$pid}->{'sock'}->close; } } delete $prop->{'children'}->{$pid}; } # send signal to all children - used by forking servers sub sig_pass { my ($self, $sig) = @_; foreach my $chld (keys %{ $self->{'server'}->{'children'} }) { $self->log(4, "signaling $chld with $sig" ); kill($sig, $chld) || $self->log(1, "child $chld not signaled with $sig"); } } # register sigs to allow passthrough to children sub register_sig_pass { my $self = shift; my $ref = $self->{'server'}->{'sig_passthrough'} || []; $ref = [$ref] if ! ref $ref; $self->fatal('invalid sig_passthrough') if ref $ref ne 'ARRAY'; return if ! @$ref; $self->log(4, "sig_passthrough option found"); require Net::Server::SIG; foreach my $sig (map {split /\s*,\s*/, $_} @$ref) { my $code = Net::Server::SIG::sig_is_registered($sig); if ($code) { $self->log(2, "Installing passthrough for $sig even though it is already registered."); } else { $code = ref($SIG{$sig}) eq 'CODE' ? $SIG{$sig} : undef; } Net::Server::SIG::register_sig($sig => sub { $self->sig_pass($sig); $code->($sig) if $code; }); $self->log(2, "Installed passthrough for $sig"); } } ###----------------------------------------------------------------### package Net::Server::TiedHandle; sub TIEHANDLE { my $pkg = shift; return bless [@_], $pkg } sub READLINE { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'getline', @_) : $s->[0]->getline } sub SAY { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'say', @_) : $s->[0]->say(@_) } sub PRINT { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'print', @_) : $s->[0]->print(@_) } sub PRINTF { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'printf', @_) : $s->[0]->printf(@_) } sub READ { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'read', @_) : $s->[0]->read(@_) } sub WRITE { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'write', @_) : $s->[0]->write(@_) } sub SYSREAD { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'sysread', @_) : $s->[0]->sysread(@_) } sub SYSWRITE { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'syswrite', @_) : $s->[0]->syswrite(@_) } sub SEEK { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'seek', @_) : $s->[0]->seek(@_) } sub BINMODE {} sub FILENO {} sub CLOSE { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'close', @_) : $s->[0]->close(@_) } 1; ### The documentation is in Net/Server.pod Net-Server-2.008/lib/Net/Server/0000755000175000017500000000000012334210320014731 5ustar paulpaulNet-Server-2.008/lib/Net/Server/Daemonize.pm0000644000175000017500000002274312331755703017232 0ustar paulpaul# -*- perl -*- # # Net::Server::Daemonize - Daemonization utilities. # # $Id$ # # Copyright (C) 2001-2012 # # Jeremy Howard # j+daemonize@howard.fm # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # # All rights reserved. # ################################################################ package Net::Server::Daemonize; use strict; use base qw(Exporter); use POSIX qw(SIGINT SIG_BLOCK SIG_UNBLOCK); our $VERSION = "0.06"; our @EXPORT_OK = qw(check_pid_file create_pid_file unlink_pid_file is_root_user get_uid get_gid set_uid set_gid set_user safe_fork daemonize); ###----------------------------------------------------------------### ### check for existance of pid_file ### if the file exists, check for a running process sub check_pid_file ($) { my $pid_file = shift; return 1 if ! -e $pid_file; open my $fh, '<', $pid_file or die "Couldn't open existant pid_file \"$pid_file\" [$!]\n"; my $current_pid = <$fh>; close $fh; $current_pid = ($current_pid =~ /^(\d{1,10})/) ? $1 : die "Couldn't find pid in existing pid_file"; my $exists; if ($$ == $current_pid) { warn "Pid_file created by this same process. Doing nothing.\n"; return 1; } elsif (-d "/proc/$$") { # try a proc file system $exists = -e "/proc/$current_pid"; } elsif (kill 0, $current_pid) { $exists = 1; } die "Pid_file already exists for running process ($current_pid)... aborting\n" if $exists; # remove the pid_file warn "Pid_file \"$pid_file\" already exists. Overwriting!\n"; unlink $pid_file || die "Couldn't remove pid_file \"$pid_file\" [$!]\n"; return 1; } ### actually create the pid_file, calls check_pid_file ### before proceeding sub create_pid_file ($) { my $pid_file = shift; check_pid_file($pid_file); open my $fh, '>', $pid_file or die "Couldn't open pid file \"$pid_file\" [$!].\n"; print $fh "$$\n"; close $fh; die "Pid_file \"$pid_file\" not created.\n" if ! -e $pid_file; return 1; } ### Allow for safe removal of the pid_file. ### Make sure this process owns it. sub unlink_pid_file ($) { my $pid_file = shift; return 1 if ! -e $pid_file; # no pid_file = return success open my $fh, '<', $pid_file or die "Couldn't open existant pid_file \"$pid_file\" [$!]\n"; # slight race my $current_pid = <$fh>; close $fh; chomp $current_pid; die "Process $$ doesn't own pid_file \"$pid_file\". Can't remove it.\n" if $current_pid ne $$; unlink($pid_file) || die "Couldn't unlink pid_file \"$pid_file\" [$!]\n"; return 1; } ###----------------------------------------------------------------### sub is_root_user () { my $id = get_uid('root'); return ! defined($id) || $< == $id || $> == $id; } ### get the uid for the passed user sub get_uid ($) { my $user = shift; my $uid = ($user =~ /^(\d+)$/) ? $1 : getpwnam($user); die "No such user \"$user\"\n" unless defined $uid; return $uid; } ### get all of the gids that this group is (space delimited) sub get_gid { my @gid; foreach my $group ( split( /[, ]+/, join(" ",@_) ) ){ if( $group =~ /^\d+$/ ){ push @gid, $group; }else{ my $id = getgrnam($group); die "No such group \"$group\"\n" unless defined $id; push @gid, $id; } } die "No group found in arguments.\n" unless @gid; return join(" ",$gid[0],@gid); } ### change the process to run as this uid sub set_uid { my $uid = get_uid(shift()); POSIX::setuid($uid); if ($< != $uid || $> != $uid) { # check $> also (rt #21262) $< = $> = $uid; # try again - needed by some 5.8.0 linux systems (rt #13450) if ($< != $uid) { die "Couldn't become uid \"$uid\": $!\n"; } } return 1; } ### change the process to run as this gid(s) ### multiple groups must be space or comma delimited sub set_gid { my $gids = get_gid(@_); my $gid = (split /\s+/, $gids)[0]; eval { $) = $gids }; # store all the gids - this is really sort of optional POSIX::setgid($gid); if (! grep {$gid == $_} split /\s+/, $() { # look for any valid id in the list die "Couldn't become gid \"$gid\": $!\n"; } return 1; } ### backward compatibility sub sub set_user { my ($user, @group) = @_; set_gid(@group) || return undef; set_uid($user) || return undef; return 1; } ###----------------------------------------------------------------### ### routine to protect process during fork sub safe_fork () { # block signal for fork my $sigset = POSIX::SigSet->new(SIGINT); POSIX::sigprocmask(SIG_BLOCK, $sigset) or die "Can't block SIGINT for fork: [$!]\n"; my $pid = fork; die "Couldn't fork: [$!]" if ! defined $pid; $SIG{'INT'} = 'DEFAULT'; # make SIGINT kill us as it did before POSIX::sigprocmask(SIG_UNBLOCK, $sigset) or die "Can't unblock SIGINT for fork: [$!]\n"; return $pid; } ###----------------------------------------------------------------### ### routine to completely dissociate from terminal process. sub daemonize ($$$) { my ($user, $group, $pid_file) = @_; check_pid_file($pid_file) if defined $pid_file; my $uid = get_uid($user); my $gid = get_gid($group); # returns list of groups $gid = (split /\s+/, $gid)[0]; my $pid = safe_fork(); exit(0) if $pid; # exit parent # child create_pid_file($pid_file) if defined $pid_file; chown($uid, $gid, $pid_file) if defined $pid_file; set_user($uid, $gid); open STDIN, '<', '/dev/null' or die "Can't open STDIN from /dev/null: [$!]\n"; open STDOUT, '>', '/dev/null' or die "Can't open STDOUT to /dev/null: [$!]\n"; open STDERR, '>&STDOUT' or die "Can't open STDERR to STDOUT: [$!]\n"; ### does this mean to be chroot ? chdir '/' or die "Can't chdir to \"/\": [$!]"; POSIX::setsid(); # Turn process into session leader, and ensure no controlling terminal ### install a signal handler to make sure SIGINT's remove our pid_file $SIG{'INT'} = sub { HUNTSMAN($pid_file) } if defined $pid_file; return 1; } ### SIGINT routine that will remove the pid_file sub HUNTSMAN { my $path = shift; unlink $path; eval { require Unix::Syslog; Unix::Syslog::syslog(Unix::Syslog::LOG_ERR(), "Exiting on INT signal."); }; exit; } 1; __END__ =head1 NAME Net::Server::Daemonize - Safe fork and daemonization utilities =head1 SYNOPSIS use Net::Server::Daemonize qw(daemonize); daemonize( 'nobody', # User 'nobody', # Group '/var/state/mydaemon.pid' # Path to PID file - optional ); =head1 DESCRIPTION This module is intended to let you simply and safely daemonize your server on systems supporting the POSIX module. This means that your Perl script runs in the background, and it's process ID is stored in a file so you can easily stop it later. =head1 EXPORTED FUNCTIONS =over 4 =item daemonize Main routine. Arguments are user (or userid), group (or group id or space delimited list of groups), and pid_file (path to file). This routine will check on the pid file, safely fork, create the pid file (storing the pid in the file), become another user and group, close STDIN, STDOUT and STDERR, separate from the process group (become session leader), and install $SIG{INT} to remove the pid file. In otherwords - daemonize. All errors result in a die. As of version 0.89 the pid_file is optional. =item safe_fork Block SIGINT during fork. No arguments. Returns pid of forked child. All errors result in a die. =item set_user Become another user and group. Arguments are user (or userid) and group (or group id or space delimited list of groups). =item set_uid Become another user. Argument is user (or userid). All errors die. =item set_gid Become another group. Arguments are groups (or group ids or space delimited list of groups or group ids). All errors die. =item get_uid Find the uid. Argument is user (userid returns userid). Returns userid. All errors die. =item get_gid Find the gids. Arguments are groups or space delimited list of groups. All errors die. =item is_root_user Determine if the process is running as root. Returns 1 or undef. =item check_pid_file Arguments are pid_file (full path to pid_file). Checks for existance of pid_file. If file exists, open it and determine if the process that created it is still running. This is done first by checking for a /proc file system and second using a "ps" command (BSD syntax). (If neither of these options exist it assumed that the process has ended) If the process is still running, it aborts. Otherwise, returns true. All errors die. =item create_pid_file. Arguments are pid_file (full path to pid_file). Calls check_pid_file. If it is successful (no pid_file exists), creates a pid file and stores $$ in the file. =item unlink_pid_file Does just that. =back =head1 SEE ALSO L. L, The Perl Cookbook Recipe 17.15. =head1 AUTHORS Jeremy Howard Program flow, concepts and initial work. Paul Seamons Code rework and componentization. Ongoing maintainer. =head1 LICENSE This package may be distributed under the terms of either the GNU General Public License or the Perl Artistic License All rights reserved. =cut Net-Server-2.008/lib/Net/Server/Fork.pm0000644000175000017500000002232712331755703016216 0ustar paulpaul# -*- perl -*- # # Net::Server::Fork - Net::Server personality # # $Id$ # # Copyright (C) 2001-2012 # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # # All rights reserved. # ################################################################ package Net::Server::Fork; use strict; use base qw(Net::Server); use Net::Server::SIG qw(register_sig check_sigs); use Socket qw(SO_TYPE SOL_SOCKET SOCK_DGRAM); use POSIX qw(WNOHANG); sub net_server_type { __PACKAGE__ } sub options { my $self = shift; my $ref = $self->SUPER::options(@_); my $prop = $self->{'server'}; $ref->{$_} = \$prop->{$_} for qw(max_servers max_dequeue check_for_dead check_for_dequeue); $ref->{'sig_passthrough'} = $prop->{'sig_passthrough'} = []; return $ref; } sub post_configure { my $self = shift; my $prop = $self->{'server'}; $self->SUPER::post_configure(@_); $prop->{'max_servers'} = 256 if ! defined $prop->{'max_servers'}; $prop->{'check_for_dead'} = 60 if ! defined $prop->{'check_for_dead'}; $prop->{'ppid'} = $$; $prop->{'multi_port'} = 1; } sub loop { my $self = shift; my $prop = $self->{'server'}; $prop->{'children'} = {}; if ($ENV{'HUP_CHILDREN'}) { my %children = map {/^(\w+)$/; $1} split(/\s+/, $ENV{'HUP_CHILDREN'}); $children{$_} = {status => $children{$_}, hup => 1} foreach keys %children; $prop->{'children'} = \%children; } # register some of the signals for safe handling register_sig( PIPE => 'IGNORE', INT => sub { $self->server_close() }, TERM => sub { $self->server_close() }, HUP => sub { $self->sig_hup() }, CHLD => sub { while (defined(my $chld = waitpid(-1, WNOHANG))) { last if $chld <= 0; $self->delete_child($chld); } }, QUIT => sub { $self->{'server'}->{'kind_quit'} = 1; $self->server_close() }, TTIN => sub { $self->{'server'}->{'max_servers'}++; $self->log(3, "Increasing max server count ($self->{'server'}->{'max_servers'})") }, TTOU => sub { $self->{'server'}->{'max_servers'}--; $self->log(3, "Decreasing max server count ($self->{'server'}->{'max_servers'})") }, ); $self->register_sig_pass; if ($ENV{'HUP_CHILDREN'}) { while (defined(my $chld = waitpid(-1, WNOHANG))) { last unless $chld > 0; $self->delete_child($chld); } } my ($last_checked_for_dead, $last_checked_for_dequeue) = (time(), time()); while (1) { ### make sure we don't use too many processes my $n_children = grep { $_->{'status'} !~ /dequeue/ } values %{ $prop->{'children'} }; while ($n_children > $prop->{'max_servers'}){ select(undef, undef, undef, 5); # block for a moment (don't look too often) check_sigs(); my $time = time(); if ($time - $last_checked_for_dead > $prop->{'check_for_dead'}) { $last_checked_for_dead = $time; $self->log(2, "Max number of children reached ($prop->{max_servers}) -- checking for alive."); foreach (keys %{ $prop->{'children'} }){ kill(0,$_) or $self->delete_child($_); } } $n_children = grep { $_->{'status'} !~ /dequeue/ } values %{ $prop->{'children'} }; } if ($prop->{'check_for_dequeue'}) { my $time = time(); if ($time - $last_checked_for_dequeue > $prop->{'check_for_dequeue'}) { $last_checked_for_dequeue = $time; if ($prop->{'max_dequeue'}) { my $n_dequeue = grep { $_->{'status'} =~ /dequeue/ } values %{ $prop->{'children'} }; $self->run_dequeue() if $n_dequeue < $prop->{'max_dequeue'}; } } } $self->pre_accept_hook; if (! $self->accept()) { last if $prop->{'_HUP'}; last if $prop->{'done'}; next; } $self->pre_fork_hook; ### fork a child so the parent can go back to listening local $!; my $pid = fork; if (! defined $pid) { $self->log(1, "Bad fork [$!]"); sleep 5; next; } # child if (! $pid) { $self->run_client_connection; exit; } # parent close($prop->{'client'}) if !$prop->{'udp_true'}; $prop->{'children'}->{$pid}->{'status'} = 'processing'; } } sub pre_accept_hook {}; sub accept { my ($self, $class) = @_; my $prop = $self->{'server'}; # block on trying to get a handle (select created because we specified multi_port) my @socks = $prop->{'select'}->can_read(2); if (check_sigs()) { return undef if $prop->{'_HUP'}; return undef if ! @socks; # don't continue unless we have a connection } my $sock = $socks[rand @socks]; return undef if ! defined $sock; # check if this is UDP if (SOCK_DGRAM == $sock->getsockopt(SOL_SOCKET,SO_TYPE)) { $prop->{'udp_true'} = 1; $prop->{'client'} = $sock; $prop->{'udp_peer'} = $sock->recv($prop->{'udp_data'}, $sock->NS_recv_len, $sock->NS_recv_flags); # Receive a SOCK_STREAM (TCP or UNIX) packet } else { delete $prop->{'udp_true'}; $prop->{'client'} = $sock->accept($class) || return; } } sub run_client_connection { my $self = shift; ### close the main sock, we still have ### the client handle, this will allow us ### to HUP the parent at any time $_ = undef foreach @{ $self->{'server'}->{'sock'} }; ### restore sigs (for the child) $SIG{'HUP'} = $SIG{'CHLD'} = $SIG{'INT'} = $SIG{'TERM'} = $SIG{'QUIT'} = 'DEFAULT'; $SIG{'PIPE'} = 'IGNORE'; delete $self->{'server'}->{'children'}; $self->child_init_hook; $self->SUPER::run_client_connection; $self->child_finish_hook; } sub close_children { my $self = shift; $self->SUPER::close_children(@_); check_sigs(); # since we have captured signals - make sure we handle them register_sig(PIPE => 'DEFAULT', INT => 'DEFAULT', TERM => 'DEFAULT', QUIT => 'DEFAULT', HUP => 'DEFAULT', CHLD => 'DEFAULT', TTIN => 'DEFAULT', TTOU => 'DEFAULT', ); } 1; __END__ =head1 NAME Net::Server::Fork - Net::Server personality =head1 SYNOPSIS use base qw(Net::Server::Fork); sub process_request { #...code... } __PACKAGE__->run(); =head1 DESCRIPTION Please read the pod on Net::Server first. This module is a personality, or extension, or sub class, of the Net::Server module. This personality binds to one or more ports and then waits for a client connection. When a connection is received, the server forks a child. The child handles the request and then closes. With the exception of parent/child signaling, this module will work (with basic functionality) on Win32 systems. =head1 ARGUMENTS =over 4 =item check_for_dead Number of seconds to wait before looking for dead children. This only takes place if the maximum number of child processes (max_servers) has been reached. Default is 60 seconds. =item max_servers The maximum number of children to fork. The server will not accept connections until there are free children. Default is 256 children. =item max_dequeue The maximum number of dequeue processes to start. If a value of zero or undef is given, no dequeue processes will be started. The number of running dequeue processes will be checked by the check_for_dead variable. =item check_for_dequeue Seconds to wait before forking off a dequeue process. It is intended to use the dequeue process to take care of items such as mail queues. If a value of undef is given, no dequeue processes will be started. =back =head1 CONFIGURATION FILE See L. =head1 PROCESS FLOW Process flow follows Net::Server until the post_accept phase. At this point a child is forked. The parent is immediately able to wait for another request. The child handles the request and then exits. =head1 HOOKS The Fork server has the following hooks in addition to the hooks provided by the Net::Server base class. See L =over 4 =item C<$self-Epre_accept_hook()> This hook occurs just before the accept is called. =item C<$self-Epost_accept_hook()> This hook occurs in the child after the accept and fork. =item C<$self-Erun_dequeue()> This hook only gets called in conjunction with the check_for_dequeue setting. =back =head1 HOT DEPLOY Since version 2.000, the Fork server has accepted the TTIN and TTOU signals. When a TTIN is received, the max_servers is increased by 1. If a TTOU signal is received the max_servers is decreased by 1. This allows for adjusting the number of handling processes without having to restart the server. =head1 AUTHOR Paul Seamons Rob Brown =head1 SEE ALSO Please see also L, L, L, L L =cut Net-Server-2.008/lib/Net/Server/MultiType.pm0000644000175000017500000001237612331755703017254 0ustar paulpaul# -*- perl -*- # # Net::Server::MultiType - Net::Server personality # # $Id$ # # Copyright (C) 2001-2012 # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # # All rights reserved. # ################################################################ package Net::Server::MultiType; use strict; use base qw(Net::Server); #sub net_server_type { shift->SUPER::net_server_type }; # not-needed sub options { my $self = shift; my $ref = $self->SUPER::options(@_); $ref->{'server_type'} = $self->{'server'}->{'server_type'} ||= []; return $ref; } sub default_server_type { 'Fork' } sub run { my $self = ref($_[0]) ? shift() : shift->new; $self->{'server'}->{'_run_args'} = [@_ == 1 ? %{$_[0]} : @_]; $self->_initialize; my $prop = $self->{'server'}; if (!defined $prop->{'server_type'} || ! @{ $prop->{'server_type'} }) { if (my $ref = $self->can('default_server_type') && $self->default_server_type) { $prop->{'server_type'} = ref($ref) ? $ref : [$ref]; } } foreach my $type (@{ $prop->{'server_type'} || []}) { next if $type eq 'MultiType'; $type = ($type =~ /^(\w+)$/) ? $1 : next; # satisfy taint my $pkg = ($type =~ /::/) ? $type : "Net::Server::$type"; (my $file = "$pkg.pm") =~ s{::}{/}g; eval { require $file }; if ($@){ warn "Couldn't become server type \"$pkg\" [$@]\n"; next; } # handle items like HTTP and PSGI that aren't true Net::Server flavors, but themselves are MultiType if ($pkg->isa(__PACKAGE__)) { my $type = $self->default_server_type || 'Single'; $type = ($type =~ /^(\w+)$/) ? $1 : next; # satisfy taint my $_pkg = ($type =~ /::/) ? $type : "Net::Server::$type"; $prop->{'_recursive_multitype'} = $_pkg; (my $file = "$_pkg.pm") =~ s{::}{/}g; eval { require $file } or die "Trouble becoming server type $pkg while loading default package $_pkg: $@\n"; die "Recursive inheritance - Package $pkg inherits from $_pkg.\n" if $_pkg->isa($pkg); no strict 'refs'; @{"${pkg}::ISA"} = ($_pkg); } # cludgy - doesn't allow multiple Net::Server::MultiType servers within same process # but it is probably better than modifying our child's class for it @Net::Server::MultiType::ISA = ($pkg); last; } # now run as the new type of thingy # passing self, instead of package, doesn't instantiate a new object $self->SUPER::run(@_); } 1; __END__ =head1 NAME Net::Server::MultiType - Net::Server personality =head1 SYNOPSIS use base qw(Net::Server::MultiType); sub process_request { #...code... } my @types = qw(PreFork Fork Single); Net::Server::MultiType->run(server_type => \@types); =head1 DESCRIPTION Please read the pod on Net::Server first. This module is a personality, or extension, or sub class, of the Net::Server module. This personality is intended to allow for easy use of multiple Net::Server personalities. Given a list of server types, Net::Server::MultiType will require one at a time until it finds one that is installed on the system. It then adds that package to its @ISA, thus inheriting the methods of that personality. =head1 ARGUMENTS In addition to the command line arguments of the Net::Server base class, Net::Server::MultiType contains one other configurable parameter. Key Value Default server_type 'server_type' 'Single' =over 4 =item server_type May be called many times to build up an array or possible server_types. At execution, Net::Server::MultiType will find the first available one and then inherit the methods of that personality =back =head1 CONFIGURATION FILE C allows for the use of a configuration file to read in server parameters. The format of this conf file is simple key value pairs. Comments and white space are ignored. #-------------- file test.conf -------------- ### multi type info ### try PreFork first, then go to Single server_type PreFork server_type Single ### server information min_servers 20 max_servers 80 spare_servers 10 max_requests 1000 ### user and group to become user somebody group everybody ### logging ? log_file /var/log/server.log log_level 3 pid_file /tmp/server.pid ### access control allow .+\.(net|com) allow domain\.com deny a.+ ### background the process? background 1 ### ports to bind host 127.0.0.1 port localhost:20204 port 20205 ### reverse lookups ? # reverse_lookups on #-------------- file test.conf -------------- =head1 PROCESS FLOW See L =head1 HOOKS There are no additional hooks in Net::Server::MultiType. =head1 TO DO See L =head1 AUTHOR Paul T. Seamons paul@seamons.com =head1 SEE ALSO Please see also L, L, L, L, L =cut Net-Server-2.008/lib/Net/Server/Multiplex.pm0000644000175000017500000003625712331755703017307 0ustar paulpaul# -*- perl -*- # # Net::Server::Multiplex - Net::Server personality # # $Id$ # # Copyright (C) 2001-2012 # # Rob Brown bbb@cpan,org # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # ################################################################ package Net::Server::Multiplex; use strict; use base qw(Net::Server); use Net::Server::SIG qw(register_sig check_sigs); use Carp qw(confess); eval { require IO::Multiplex; import IO::Multiplex 1.05; }; $@ && warn "Module IO::Multiplex is required for Multiplex."; our $VERSION = $Net::Server::VERSION; sub net_server_type { __PACKAGE__ } sub loop { my $self = shift; my $prop = $self->{server}; my $mux = IO::Multiplex->new; $self->{mux} = $mux; foreach my $sock ( @{ $prop->{sock} } ) { if (Net::Server::SOCK_DGRAM == $sock->getsockopt(Socket::SOL_SOCKET(),Socket::SO_TYPE())) { $mux->add($sock); } else { $mux->listen($sock); } } $mux->set_callback_object(Net::Server::Multiplex::MUX->init($self)); ### ### Use Net::Server::SIG for safe signal handling. ### ### register some of the signals for safe handling register_sig(PIPE => sub { $self->log(4, "SIG$_[0] received") }, INT => sub { $self->server_close() }, TERM => sub { $self->server_close() }, QUIT => sub { $self->server_close() }, HUP => sub { $self->sig_hup() }, CHLD => sub { $self->sig_chld() }, ); if ( defined $prop->{check_for_dequeue} ) { # It does not matter which socket the timeout is associated with. $mux->set_timeout( $prop->{sock}->[0], $prop->{check_for_dequeue} ); } $mux->loop(sub { my ($rdready, $wrready) = @_; check_sigs(); $mux->endloop if $prop->{_HUP}; }); ### fall back to the main run routine } ### make sure that we properly disconnect from the mux if we are HUPing sub sig_hup { my $self = shift; my $prop = $self->{server}; if (my $mux = $self->{mux}) { foreach my $sock ( @{ $prop->{sock} } ){ $mux->remove($sock); } } return $self->SUPER::sig_hup(@_); } # This method instead of run_client_connection # because STDOUT should be tied correctly, # not just globbed onto the socket. This # tie is taken care of in the mux_connection # routine instead of within post_accept. # Also, the process_request stuff should never be # used since the request should be really processed # via mux_* methods. sub setup_client_connection { my ($self, $mux) = @_; my $prop = $self->{server}; ### Copied from Net::Server::post_accept... $prop->{requests} ++; if (! $prop->{no_client_stdout}) { *STDIN = \*{ $prop->{client} }; # *STDOUT = \*{ $prop->{client} }; # STDIN->autoflush(1); } ### Copied from Net::Server::run_client_connection... $self->get_client_info; # determines information about peer and local $self->post_accept_hook; # user customizable hook unless($self->allow_deny && # do allow/deny check on client info $self->allow_deny_hook ){ # user customizable hook $self->request_denied_hook; # user customizable hook # Flush output buffer and close connection since it should be denied. if (! $prop->{no_client_stdout}) { close (STDOUT); } return 0; } return 1; } # Compatibility interface for Net::Server sub run_dequeue { confess "&$Net::Server::Multiplex::MUX::ISA[0]\::run_dequeue never defined"; } sub mux_connection {} sub mux_input { confess "&$Net::Server::Multiplex::MUX::ISA[0]\::mux_input never defined"; } sub mux_eof {} sub mux_close {} sub mux_timeout { confess "&$Net::Server::Multiplex::MUX::ISA[0]\::mux_timeout never defined"; } package Net::Server::Multiplex::MUX; # Just a dumb module to be used for the # Multiplex callback_object hooks use strict; our $VERSION = $Net::Server::Multiplex::VERSION; # This temporary @ISA should always be overridden # at runtime when init() is called. This module should # really ISA whatever module ISA Net::Server::Multiplex. our @ISA = qw(Net::Server::Multiplex); # This subroutine is meant to create the main callback # object to be used for all listen file descriptors. # It just needs to make sure the {net_server} property # is set. sub init { my $package = shift; my $net_server= shift; # On-the-fly runtime molymorphism hack # to ISA the same type of thing passed. @ISA = (ref $net_server); my $self = bless { net_server => $net_server, } => $package; return $self; } # The new() routine is passed the Net::Server object. It # is meant to create the client specific callback object. # Note that the $net_server->{server} property hash may be # modified by future connections through Net::Server. # Any values within it that this object may need to use # later must be copied within itself. sub new { my $package = shift; my $net_server = shift; my $self = bless { # Some nice values to remember for this client net_server => $net_server, peeraddr => $net_server->{server}->{peeraddr}, connected => time, }, $package; return $self; } sub log { shift->{net_server}->log(@_) } # This subroutine is only used by the listen callback object. sub mux_connection { my ($self, $mux, $fh) = @_; my $net_server = $self->{net_server}; $net_server->{server}->{client} = $fh; $self->_link_stdout($mux, $fh); if ($net_server->setup_client_connection($mux)) { # Create client specific callback object my $client_object = Net::Server::Multiplex::MUX->new($net_server, $fh); # Set this as the callback object for this client $mux->set_callback_object($client_object, $fh); # Finally call the clients real mux_connection routine, # if any. This allows all the mux_* routines to be # called from the same type of object. $client_object->SUPER::mux_connection($mux, $fh); #$client_object->mux_connection($mux, $fh); } $self->_unlink_stdout(); return; } sub mux_input { my ($self, $mux, $fh, $in_ref) = @_; $self->_link_stdout($mux, $fh); $self->SUPER::mux_input($mux, $fh, $in_ref); $self->_unlink_stdout(); return; } sub mux_eof { my ($self, $mux, $fh, $in_ref) = @_; $self->_link_stdout($mux, $fh); $self->SUPER::mux_eof($mux, $fh, $in_ref); $self->_unlink_stdout(); $mux->shutdown($fh, 1); return; } sub mux_close { my ($self, $mux, $fh) = @_; $self->{net_server}->post_process_request_hook; $self->SUPER::mux_close($mux, $fh); return; } sub mux_timeout { my ($self, $mux, $fh) = @_; if ( my $check = $self->{net_server}->{server}->{check_for_dequeue} ) { $self->{net_server}->run_dequeue(); $mux->set_timeout( $fh, $check ); } else { $self->_link_stdout($mux, $fh); $self->SUPER::mux_timeout($mux, $fh); $self->_unlink_stdout(); } return; } sub _link_stdout { my ($self, $mux, $fh) = @_; return if $self->{net_server}->{server}->{no_client_stdout}; # Hook up STDOUT to the correct socket if (tied *$fh) { # Make sure STDOUT is tied however $fh is tie (*STDOUT, (ref tied *$fh), $mux, $fh); } else { *STDOUT = *$fh; } } sub _unlink_stdout { my $self = shift; return if $self->{net_server}->{server}->{no_client_stdout}; my $x = tied *STDOUT; if ($x) { undef $x; untie *STDOUT; } } 1; __END__ =head1 NAME Net::Server::Multiplex - Multiplex several connections within one process =head1 SYNOPSIS package MyPlexer; use base qw(Net::Server::Multiplex); sub mux_input { #...code... } __PACKAGE__->run(); =head1 DESCRIPTION This personality is designed to handle multiple connections all within one process. It should only be used with protocols that are guaranteed to be able to respond quickly on a packet by packet basis. If determining a response could take a while or an unknown period of time, all other connections established will block until the response completes. If this condition might ever occur, this personality should probably not be used. This takes some nice features of Net::Server (like the server listen socket setup, configuration file processing, safe signal handling, convenient inet style STDIN/STDOUT handling, logging features, deamonization and pid tracking, and restartability -SIGHUP) and some nice features of IO::Multiplex (automatic buffered IO and per-file-handle objects) and combines them for an easy-to-use interace. See examples/samplechat.pl distributed with Net::Server for a simple chat server that uses several of these features. =head1 PROCESS FLOW The process flow is written in an open, easy to override, easy to hook, fashion. The basic flow is shown below. $self->configure_hook; $self->configure(@_); $self->post_configure; $self->post_configure_hook; $self->pre_bind; $self->bind; if (Restarting server) { $self->restart_open_hook(); } $self->post_bind_hook; $self->post_bind; $self->pre_loop_hook; $self->loop; # This basically just runs IO::Multiplex::loop # For routines inside a $self->loop # See CLIENT PROCESSING below $self->pre_server_close_hook; $self->post_child_cleanup_hook; $self->server_close; if (Restarting server) { $self->restart_close_hook(); $self->hup_server; # Redo process again starting with configure_hook } The server then exits. =head1 CLIENT PROCESSING The following represents the client processing program flow: $self->{server}->{client} = Net::Server::Proto::TCP->accept(); # NOTE: Multiplexed with mux_input() below if (check_for_dequeue seconds have passed) { $self->run_dequeue(); } $self->get_client_info; $self->post_accept_hook; # Net::Server style if ($self->allow_deny && $self->allow_deny_hook) { # (Net::Server style $self->process_request() is never called.) # A unique client specific object is created # for all mux_* methods from this point on. $self = __PACKAGE__->new($self, client); $self->mux_connection; # IO::Multiplex style for (every packet received) { $self->mux_input; # NOTE: Multiplexed with accept() above } } else { $self->request_denied_hook; # Notice that if either allow_deny or allow_deny_hook fails, then # new(), mux_connection(), and mux_input() will never be called. # mux_eof() and mux_close() will still be called, but using a # common listen socket callback object instead of a unique client # specific object. } $self->mux_eof; $self->post_process_request_hook; $self->mux_close; This process then loops multiplexing between the accept() for the next connection and mux_input() when input arrives to avoid blocking either one. =head1 HOOKS The *_hook methods mentioned above are meant to be overridden with your own subroutines if you desire to provide additional functionality. The loop() method of Net::Server has been overridden to run the loop routine of IO::Multiplex instead. The Net::Server methods may access the IO::Multiplex object at C<$self-E{mux}> if desired. The IO::Multiplex methods may access the Net::Server object at C<$self-E{net_server}> if desired. The process_request() method is never used with this personality. The other Net::Server hooks and methods should work the same. =over 4 =item C<$self-Erun_dequeue()> This hook only gets called in conjunction with the check_for_dequeue setting. It will run every check_for_dequeue seconds. Since no forking is done, this hook should run fast in order to prevent blocking the rest of the processing. =back =head1 TIMEOUTS =head2 set_timeout To utilize the optional timeout feature of IO::Multiplex, you need to specify a timeout by using the set_timeout method. $self->{net_server}->{mux}->set_timeout($fh, $seconds_from_now); $fh may be either a client socket or a listen socket file descriptor within the mux. $seconds_from_now may be fractional to achieve more precise timeouts. This is used in conjunction with mux_timeout, which you should define yourself. =head2 mux_timeout The main loop() routine will call $obj->mux_timeout($mux, $fh) when the timeout specified in set_timeout is reached where $fh is the same as the one specified in set_timeout() and $obj is its corresponding object (either the unique client specific object or the main listen callback object) and $mux is the main IO::Multiplex object itself. =head1 CALLBACK INTERFACE Callback objects should support the following interface. You do not have to provide all of these methods, just provide the ones you are interested in. These are just like the IO::Multiplex hooks except that STDOUT is tied to the corresponding client socket handle for your convenience and to more closely emulate the Net::Server model. However, unlike some other Net::Server personalities, you should never read directly from STDIN yourself. You should define one or more of the following methods: =head2 mux_connection ($mux,$fh) (OPTIONAL) Run once when the client first connects if the allow_deny passes. Note that the C<$self-E{net_server}-E{server}> property hash may be modified by future connections through Net::Server. Any values within it that this object may need to use later should be copied within its own object at this point. Example: $self->{peerport} = $self->{net_server}->{server}->{peerport}; =head2 mux_input ($mux,$fh,\$data) (REQUIRED) Run each time a packet is read. It should consume $data starting at the left and leave unconsumed data in the scalar for future calls to mux_input. =head2 mux_eof ($mux,$fh,\$data) (OPTIONAL) Run once when the client is done writing. It should consume the rest of $data since mux_input() will never be run again. =head2 mux_close ($mux,$fh) (OPTIONAL) Run after the entire client socket has been closed. No more attempts should be made to read or write to the client or to STDOUT. =head2 mux_timeout ($mux,$fh) (OPTIONAL) Run once when the set_timeout setting expires as explained above. =head1 BUGS This is only known to work with TCP servers. If you need to use the IO::Multiplex style set_timeout / mux_timeout interface, you cannot use the Net::Server style check_for_dequeue / run_dequeue interface. It will not work if the check_for_dequeue option is specified. The run_dequeue method is just a compatibility interface to comply with the Net::Server::Fork style run_dequeue but is implemented in terms of the IO::Multiplex style set_timeout and mux_timeout methods. =head1 AUTHOR Rob Brown =head1 MAINTAINER Paul Seamons =head1 LICENSE This package may be distributed under the terms of either the GNU General Public License or the Perl Artistic License All rights reserved. =head1 SEE ALSO L by Paul Seamons , L by Bruce Keeler . =cut Net-Server-2.008/lib/Net/Server/PreForkSimple.pm0000644000175000017500000004241112331755703020033 0ustar paulpaul# -*- perl -*- # # Net::Server::PreForkSimple - Net::Server personality # # $Id$ # # Copyright (C) 2001-2012 # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # # All rights reserved. # ################################################################ package Net::Server::PreForkSimple; use strict; use base qw(Net::Server); use Net::Server::SIG qw(register_sig check_sigs); use POSIX qw(WNOHANG EINTR); use Fcntl (); sub net_server_type { __PACKAGE__ } sub options { my $self = shift; my $ref = $self->SUPER::options(@_); my $prop = $self->{'server'}; $ref->{$_} = \$prop->{$_} for qw(max_servers max_requests max_dequeue check_for_dead check_for_dequeue lock_file serialize); $ref->{'sig_passthrough'} = $prop->{'sig_passthrough'} = []; return $ref; } sub post_configure { my $self = shift; my $prop = $self->{'server'}; $self->SUPER::post_configure; ### some default values to check for my $d = { max_servers => 50, # max num of servers to run max_requests => 1000, # num of requests for each child to handle check_for_dead => 30, # how often to see if children are alive }; foreach (keys %$d){ $prop->{$_} = $d->{$_} unless defined($prop->{$_}) && $prop->{$_} =~ /^\d+$/; } $prop->{'ppid'} = $$; } sub post_bind { my $self = shift; my $prop = $self->{'server'}; $self->SUPER::post_bind; if ($prop->{'multi_port'} && $prop->{'serialize'} && $prop->{'serialize'} eq 'none') { $self->log(2, "Passed serialize value of none is incompatible with multiple ports - using default serialize"); delete $prop->{'serialize'}; } if (!$prop->{'serialize'} || $prop->{'serialize'} !~ /^(flock|semaphore|pipe|none)$/i) { $prop->{'serialize'} = ($^O eq 'MSWin32') ? 'pipe' : 'flock'; } $prop->{'serialize'} =~ tr/A-Z/a-z/; if ($prop->{'serialize'} eq 'flock') { $self->log(3, "Setting up serialization via flock"); if (defined $prop->{'lock_file'}) { $prop->{'lock_file_unlink'} = undef; } else { $prop->{'lock_file'} = eval { require File::Temp } ? File::Temp::tmpnam() : POSIX::tmpnam(); $prop->{'lock_file_unlink'} = 1; } } elsif ($prop->{'serialize'} eq 'semaphore') { $self->log(3, "Setting up serialization via semaphore"); require IPC::SysV; require IPC::Semaphore; my $s = IPC::Semaphore->new(IPC::SysV::IPC_PRIVATE(), 1, IPC::SysV::S_IRWXU() | IPC::SysV::IPC_CREAT()) or $self->fatal("Semaphore error [$!]"); $s->setall(1) or $self->fatal("Semaphore create error [$!]"); $prop->{'sem'} = $s; } elsif ($prop->{'serialize'} eq 'pipe') { $self->log(3, "Setting up serialization via pipe"); pipe(my $waiting, my $ready); $ready->autoflush(1); $waiting->autoflush(1); $prop->{'_READY'} = $ready; $prop->{'_WAITING'} = $waiting; print $ready "First\n"; } elsif ($prop->{'serialize'} eq 'none') { $self->log(3, "Using no serialization"); } else { $self->fatal("Unknown serialization type \"$prop->{'serialize'}\""); } } sub loop { my $self = shift; my $prop = $self->{'server'}; $prop->{'children'} = {}; if ($ENV{'HUP_CHILDREN'}) { my %children = map {/^(\w+)$/; $1} split(/\s+/, $ENV{'HUP_CHILDREN'}); $children{$_} = {status => $children{$_}, hup => 1} foreach keys %children; $prop->{'children'} = \%children; } $self->log(3, "Beginning prefork ($prop->{'max_servers'} processes)"); $self->run_n_children($prop->{'max_servers'}); $self->run_parent; } sub run_n_children { my ($self, $n) = @_; return if $n <= 0; my $prop = $self->{'server'}; $self->run_n_children_hook; $self->log(3, "Starting \"$n\" children"); for (1 .. $n) { $self->pre_fork_hook; local $!; my $pid = fork; $self->fatal("Bad fork [$!]") if ! defined $pid; if ($pid) { $prop->{'children'}->{$pid}->{'status'} = 'processing'; } else { $self->run_child; } } } sub run_n_children_hook {} sub run_child { my $self = shift; my $prop = $self->{'server'}; $SIG{'INT'} = $SIG{'TERM'} = $SIG{'QUIT'} = sub { $self->child_finish_hook; exit; }; $SIG{'PIPE'} = 'IGNORE'; $SIG{'CHLD'} = 'DEFAULT'; $SIG{'HUP'} = sub { if (! $prop->{'connected'}) { $self->child_finish_hook; exit; } $prop->{'SigHUPed'} = 1; }; my $needs_lock = ($prop->{'serialize'} eq 'flock') ? 1 : 0; if ($needs_lock) { open($prop->{'lock_fh'}, ">", $prop->{'lock_file'}) or $self->fatal("Couldn't open lock file \"$prop->{'lock_file'}\"[$!]"); } $self->log(4, "Child Preforked ($$)"); delete $prop->{'children'}; $self->child_init_hook; while ($self->accept()) { $prop->{'connected'} = 1; $self->run_client_connection; $prop->{'connected'} = 0; last if $self->done; } $self->child_finish_hook; close($prop->{'lock_fh'}) if $needs_lock && $prop->{'lock_fh'}; $self->log(4, "Child leaving ($prop->{'max_requests'})"); exit; } sub is_prefork { 1 } ### We can only let one process do the selecting at a time ### this override makes sure that nobody else can do it ### while we are. We do this either by opening a lock file ### and getting an exclusive lock (this will block all others ### until we release it) or by using semaphores to block sub accept { my $self = shift; my $prop = $self->{'server'}; if ($prop->{'serialize'} eq 'flock') { while (! flock $prop->{'lock_fh'}, Fcntl::LOCK_EX()) { next if $! == EINTR; $self->fatal("Couldn't get lock on file \"$prop->{'lock_file'}\" [$!]"); } my $v = $self->SUPER::accept(); flock $prop->{'lock_fh'}, Fcntl::LOCK_UN(); return $v; } elsif ($prop->{'serialize'} eq 'semaphore') { $prop->{'sem'}->op(0, -1, IPC::SysV::SEM_UNDO()) or $self->fatal("Semaphore Error [$!]"); my $v = $self->SUPER::accept(); $prop->{'sem'}->op(0, 1, IPC::SysV::SEM_UNDO()) or $self->fatal("Semaphore Error [$!]"); return $v; } elsif ($prop->{'serialize'} eq 'pipe') { my $waiting = $prop->{'_WAITING'}; scalar <$waiting>; # read one line - kernel says who gets it my $v = $self->SUPER::accept(); print { $prop->{'_READY'} } "Next!\n"; return $v; } else { my $v = $self->SUPER::accept(); return $v; } } sub done { my $self = shift; my $prop = $self->{'server'}; $prop->{'done'} = shift if @_; return 1 if $prop->{'done'}; return 1 if $prop->{'requests'} >= $prop->{'max_requests'}; return 1 if $prop->{'SigHUPed'}; if (! kill 0, $prop->{'ppid'}) { $self->log(3, "Parent process gone away. Shutting down"); return 1; } } sub run_parent { my $self=shift; my $prop = $self->{'server'}; $self->log(4, "Parent ready for children."); $prop->{'last_checked_for_dead'} = $prop->{'last_checked_for_dequeue'} = time(); register_sig( PIPE => 'IGNORE', INT => sub { $self->server_close() }, TERM => sub { $self->server_close() }, HUP => sub { $self->sig_hup() }, CHLD => sub { while (defined(my $chld = waitpid(-1, WNOHANG))) { last unless $chld > 0; $self->delete_child($chld); } }, QUIT => sub { $self->{'server'}->{'kind_quit'} = 1; $self->server_close() }, TTIN => sub { $self->{'server'}->{'max_servers'}++; $self->log(3, "Increasing max server count ($self->{'server'}->{'max_servers'})") }, TTOU => sub { $self->{'server'}->{'max_servers'}--; $self->log(3, "Decreasing max server count ($self->{'server'}->{'max_servers'})"); if (defined(my $pid = each %{ $prop->{'children'} })) { $self->delete_child($pid) if ! kill('HUP', $pid); } }, ); $self->register_sig_pass; if ($ENV{'HUP_CHILDREN'}) { while (defined(my $chld = waitpid(-1, WNOHANG))) { last unless $chld > 0; $self->delete_child($chld); } } while (1) { select undef, undef, undef, 10; if (check_sigs()){ last if $prop->{'_HUP'}; } $self->idle_loop_hook(); # periodically make sure children are alive my $time = time(); if ($time - $prop->{'last_checked_for_dead'} > $prop->{'check_for_dead'}) { $prop->{'last_checked_for_dead'} = $time; foreach (keys %{ $prop->{'children'} }) { kill(0,$_) or $self->delete_child($_); } } # make sure we always have max_servers my $total_n = 0; my $total_d = 0; foreach (values %{ $prop->{'children'} }){ if( $_->{'status'} eq 'dequeue' ){ $total_d ++; }else{ $total_n ++; } } if( $prop->{'max_servers'} > $total_n ){ $self->run_n_children( $prop->{'max_servers'} - $total_n ); } # periodically check to see if we should clear the queue if( defined $prop->{'check_for_dequeue'} ){ if( $time - $prop->{'last_checked_for_dequeue'} > $prop->{'check_for_dequeue'} ){ $prop->{'last_checked_for_dequeue'} = $time; if( defined($prop->{'max_dequeue'}) && $total_d < $prop->{'max_dequeue'} ){ $self->run_dequeue(); } } } } } sub idle_loop_hook {} sub close_children { my $self = shift; $self->SUPER::close_children(@_); check_sigs(); # since we have captured signals - make sure we handle them register_sig(PIPE => 'DEFAULT', INT => 'DEFAULT', TERM => 'DEFAULT', QUIT => 'DEFAULT', HUP => 'DEFAULT', CHLD => 'DEFAULT', TTIN => 'DEFAULT', TTOU => 'DEFAULT', ); } 1; __END__ =head1 NAME Net::Server::PreForkSimple - Net::Server personality =head1 SYNOPSIS use base qw(Net::Server::PreForkSimple); sub process_request { #...code... } __PACKAGE__->run(); =head1 DESCRIPTION Please read the pod on Net::Server first. This module is a personality, or extension, or sub class, of the Net::Server module. This personality binds to one or more ports and then forks C child processes. The server will make sure that at any given time there are always C available to receive a client request. Each of these children will process up to C client connections. This type is good for a heavily hit site that can keep C processes dedicated to the serving. (Multi port accept defaults to using flock to serialize the children). At this time, it does not appear that this module will pass tests on Win32 systems. Any ideas or patches for making the tests pass would be welcome. =head1 SAMPLE CODE Please see the sample listed in Net::Server. =head1 COMMAND LINE ARGUMENTS In addition to the command line arguments of the Net::Server base class, Net::Server::PreFork contains several other configurable parameters. Key Value Default max_servers \d+ 50 max_requests \d+ 1000 serialize (flock|semaphore |pipe|none) undef # serialize defaults to flock on multi_port or on Solaris lock_file "filename" File::Temp::tempfile or POSIX::tmpnam check_for_dead \d+ 30 max_dequeue \d+ undef check_for_dequeue \d+ undef =over 4 =item max_servers The maximum number of child servers to start and maintain. This does not apply to dequeue processes. =item max_requests The number of client connections to receive before a child terminates. =item serialize Determines whether the server serializes child connections. Options are undef, flock, semaphore, pipe, or none. Default is undef. On multi_port servers or on servers running on Solaris, the default is flock. The flock option uses blocking exclusive flock on the file specified in I (see below). The semaphore option uses IPC::Semaphore (thanks to Bennett Todd) for giving some sample code. The pipe option reads on a pipe to choose the next. the flock option should be the most bulletproof while the pipe option should be the most portable. (Flock is able to reliquish the block if the process dies between accept on the socket and reading of the client connection - semaphore and pipe do not). An option of none will not perform any serialization. If "none" is passed and there are multiple ports then a the default serialization will be used insted of "none." =item lock_file Filename to use in flock serialized accept in order to serialize the accept sequece between the children. This will default to a generated temporary filename. If default value is used the lock_file will be removed when the server closes. =item check_for_dead Seconds to wait before checking to see if a child died without letting the parent know. =item max_dequeue The maximum number of dequeue processes to start. If a value of zero or undef is given, no dequeue processes will be started. The number of running dequeue processes will be checked by the check_for_dead variable. =item check_for_dequeue Seconds to wait before forking off a dequeue process. The run_dequeue hook must be defined when using this setting. It is intended to use the dequeue process to take care of items such as mail queues. If a value of undef is given, no dequeue processes will be started. =back =head1 CONFIGURATION FILE C allows for the use of a configuration file to read in server parameters. The format of this conf file is simple key value pairs. Comments and white space are ignored. #-------------- file test.conf -------------- ### server information max_servers 80 max_requests 1000 ### user and group to become user somebody group everybody ### logging ? log_file /var/log/server.log log_level 3 pid_file /tmp/server.pid ### access control allow .+\.(net|com) allow domain\.com deny a.+ ### background the process? background 1 ### ports to bind host 127.0.0.1 port localhost:20204 port 20205 ### reverse lookups ? # reverse_lookups on #-------------- file test.conf -------------- =head1 PROCESS FLOW Process flow follows Net::Server until the loop phase. At this point C are forked and wait for connections. When a child accepts a connection, finishs processing a client, or exits, it relays that information to the parent, which keeps track and makes sure there are always C running. =head1 HOOKS The PreForkSimple server has the following hooks in addition to the hooks provided by the Net::Server base class. See L =over 4 =item C<$self-Erun_n_children_hook()> This hook occurs at the top of run_n_children which is called each time the server goes to start more child processes. This gives the parent to do a little of its own accountting (as desired). Idea for this hook came from James FitzGibbon. =item C<$self-Echild_init_hook()> This hook takes place immeditately after the child process forks from the parent and before the child begins accepting connections. It is intended for any addiotional chrooting or other security measures. It is suggested that all perl modules be used by this point, so that the most shared memory possible is used. =item C<$self-Echild_finish_hook()> This hook takes place immediately before the child tells the parent that it is exiting. It is intended for saving out logged information or other general cleanup. =item C<$self-Erun_dequeue()> This hook only gets called in conjunction with the check_for_dequeue setting. =item C<$self-Eidle_loop_hook()> This hook is called in every pass through the main process wait loop. =back =head1 HOT DEPLOY Since version 2.000, the PreForkSimple server has accepted the TTIN and TTOU signals. When a TTIN is received, the max_servers is increased by 1. If a TTOU signal is received the max_servers is decreased by 1. This allows for adjusting the number of handling processes without having to restart the server. =head1 BUGS Tests don't seem to work on Win32. Any ideas or patches would be welcome. =head1 TO DO See L =head1 AUTHOR Paul T. Seamons paul@seamons.com =head1 THANKS See L =head1 SEE ALSO Please see also L, L, L, L, L L L L =cut Net-Server-2.008/lib/Net/Server/SIG.pm0000644000175000017500000001153012331755703015731 0ustar paulpaul# -*- perl -*- # # Net::Server::SIG - Safer signals # # $Id$ # # Copyright (C) 2001-2012 # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # # All rights reserved. # ################################################################ package Net::Server::SIG; use strict; use vars qw($VERSION @ISA @EXPORT_OK %_SIG %_SIG_SUB); use Exporter (); $VERSION = '0.03'; @ISA = qw(Exporter); @EXPORT_OK = qw(register_sig unregister_sig check_sigs); sub register_sig { die 'Usage: register_sig( SIGNAME => \&code_ref )' if @_ % 2; if (@_ > 2) { register_sig(shift(),shift()) while @_; return; } my $sig = shift; my $code_ref = shift; my $ref = ref($code_ref); if (! $ref) { if ($code_ref eq 'DEFAULT') { delete $_SIG{$sig}; delete $_SIG_SUB{$sig}; $SIG{$sig} = 'DEFAULT'; } elsif ($code_ref eq 'IGNORE') { delete $_SIG{$sig}; delete $_SIG_SUB{$sig}; $SIG{$sig} = 'IGNORE'; } else { die "Scalar argument limited to \"DEFAULT\" and \"IGNORE\"."; } } elsif ($ref eq 'CODE') { $_SIG{$sig} = 0; $_SIG_SUB{$sig} = $code_ref; $SIG{$sig} = sub{ $Net::Server::SIG::_SIG{$sig} = 1 }; } else { die "Unsupported sig type -- must be 'DEFAULT' or a code ref."; } } sub unregister_sig { register_sig(shift(), 'DEFAULT') } sub check_sigs { my @found; foreach my $sig (keys %_SIG){ next if ! $_SIG{$sig}; $_SIG{$sig} = 0; push @found, $sig; $_SIG_SUB{$sig}->($sig); } return @found; } sub sig_is_registered { my $sig = shift; return $_SIG_SUB{$sig}; } 1; =head1 NAME Net::Server::SIG - adpf - Safer signal handling =head1 SYNOPSIS use Net::Server::SIG qw(register_sig check_sigs); use IO::Select (); use POSIX qw(WNOHANG); my $select = IO::Select->new(); register_sig(PIPE => 'IGNORE', HUP => 'DEFAULT', USR1 => sub { print "I got a SIG $_[0]\n"; }, USR2 => sub { print "I got a SIG $_[0]\n"; }, CHLD => sub { 1 while waitpid(-1, WNOHANG) > 0; }, ); # add some handles to the select $select->add(\*STDIN); # loop forever trying to stay alive while (1) { # do a timeout to see if any signals got passed us # while we were processing another signal my @fh = $select->can_read(10); my $key; my $val; # this is the handler for safe (fine under unsafe also) if (check_sigs()) { # or my @sigs = check_sigs(); next unless @fh; } my $handle = $fh[@fh]; # do something with the handle } =head1 DESCRIPTION Signals prior in Perl prior to 5.7 were unsafe. Since then signals have been implemented in a more safe algorithm. Net::Server::SIG provides backwards compatibility, while still working reliably with newer releases. Using a property of the select() function, Net::Server::SIG attempts to fix the unsafe problem. If a process is blocking on select() any signal will short circuit the select. Using this concept, Net::Server::SIG does the least work possible (changing one bit from 0 to 1). And depends upon the actual processing of the signals to take place immediately after the the select call via the "check_sigs" function. See the example shown above and also see the sigtest.pl script located in the examples directory of this distribution. =head1 FUNCTIONS =over 4 =item C \&code_ref)> Takes key/value pairs where the key is the signal name, and the argument is either a code ref, or the words 'DEFAULT' or 'IGNORE'. The function register_sig must be used in conjunction with check_sigs, and with a blocking select() function call -- otherwise, you will observe the registered signal mysteriously vanish. =item C Takes the name of a signal as an argument. Calls register_sig with a this signal name and 'DEFAULT' as arguments (same as register_sig(SIG,'DEFAULT') =item C Checks to see if any registered signals have occured. If so, it will play the registered code ref for that signal. Return value is array containing any SIGNAL names that had occured. =item C Takes a signal name and returns any registered code_ref for that signal. =back =head1 AUTHORS Paul Seamons (paul@seamons.com) Rob B Brown (rob@roobik.com) - Provided a sounding board and feedback in creating Net::Server::SIG and sigtest.pl. =head1 LICENSE This package may be distributed under the terms of either the GNU General Public License or the Perl Artistic License All rights reserved. =cut Net-Server-2.008/lib/Net/Server/Single.pm0000644000175000017500000000212612331755703016531 0ustar paulpaul# -*- perl -*- # # Net::Server::Single - Net::Server personality # # $Id$ # # Copyright (C) 2001-2012 # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # # All rights reserved. # ################################################################ package Net::Server::Single; use strict; use base qw(Net::Server); sub net_server_type { __PACKAGE__ } ### this module is simple a place holder so that ### Net::Server::MultiType can ask for Single as one of ### the fall back methods (which it does any way). ### Essentially all we are doing here is providing parallelism. 1; __END__ =head1 NAME Net::Server::Single - Net::Server personality =head1 SYNOPSIS use base qw(Net::Server::Single); sub process_request { #...code... } =head1 DESCRIPTION This module offers no functionality beyond the Net::Server base class. This modules only purpose is to provide parallelism for the MultiType personality. See L =cut Net-Server-2.008/lib/Net/Server/Proto.pm0000644000175000017500000006354612331755703016430 0ustar paulpaul# -*- perl -*- # # Net::Server::Proto - Net::Server Protocol compatibility layer # # $Id$ # # Copyright (C) 2001-2012 # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # # All rights reserved. # ################################################################ package Net::Server::Proto; use strict; use warnings; use Socket (); my $requires_ipv6 = 0; sub parse_info { my ($class, $port, $host, $proto, $ipv, $server) = @_; my $info; if (ref($port) eq 'HASH') { die "Missing port in hashref passed in port argument.\n" if ! $port->{'port'}; $info = $port; } else { $info = {}; $info->{'unix_type'} = $1 if $port =~ s{ (?<=[\w*\]]) [,|\s:/]+ (sock_stream|sock_dgram) \b }{}x; # legacy /some/path|sock_dgram $ipv = $1 if $port =~ s{ (?<=[\w*\]]) [,|\s:/]+ IPv([*\d]+) }{}xi; # allow for 80|IPv* $ipv .= $1 if $port =~ s{ (?<=[\w*\]]) [,|\s:/]+ IPv([*\d]+) }{}xi; # allow for 80|IPv4|IPv6 stacked $proto = $1 if $port =~ s{ (?<=[\w*\]]) [,|\s:/]+ (tcp|udp|ssl|ssleay|unix|unixdgram|\w+(?: ::\w+)+) $ }{}xi # allow for 80/tcp or 200/udb or 90/Net::Server::Proto::TCP || $port =~ s{ / (\w+) $ }{}x; # legacy 80/MyTcp support $host = $1 if $port =~ s{ ^ (.*?) [,|\s:]+ (?= \w+ $) }{}x; # allow localhost:80 $info->{'port'} = $port; } $info->{'port'} ||= 0; $info->{'host'} ||= (defined($host) && length($host)) ? $host : '*'; $ipv = $1 if $info->{'host'} =~ s{ (?<=[\w*\]]) [,|\s:/]+ IPv([*\d]+) }{}xi; # allow for 80|IPv* $ipv .= $1 if $info->{'host'} =~ s{ (?<=[\w*\]]) [,|\s:/]+ IPv([*\d]+) }{}xi; # allow for 80|IPv4|IPv6 stacked if ( $info->{'host'} =~ m{^ \[ ([\w/.\-:]+ | \*?) \] $ }x) { # allow for [::1] or [host.example.com] $info->{'host'} = length($1) ? $1 : '*'; } elsif ($info->{'host'} =~ m{^ ([\w/.\-:]+ | \*?) $ }x) { $info->{'host'} = $1; # untaint } else { $server->fatal("Could not determine host from \"$info->{'host'}\""); } $info->{'proto'} ||= $proto || 'tcp'; $ipv = $1 if $info->{'proto'} =~ s{ (?<=[\w*\]]) [,|\s:/]+ IPv([*\d]+) }{}xi; # allow for 80|IPv* $ipv .= $1 if $info->{'proto'} =~ s{ (?<=[\w*\]]) [,|\s:/]+ IPv([*\d]+) }{}xi; # allow for 80|IPv4|IPv6 stacked if ($info->{'proto'} =~ /^(\w+ (?:::\w+)*)$/x) { $info->{'proto'} = $1; } else { $server->fatal("Could not determine proto from \"$proto\""); } $proto = lc $info->{'proto'}; if ($info->{'proto'} =~ /^UNIX/i) { return ({%$info, ipv => '*'}); } $ipv = $info->{'ipv'} || $ipv || $ENV{'IPV'} || ''; $ipv = join '', @$ipv if ref($ipv) eq 'ARRAY'; $server->fatal("Invalid ipv parameter - must contain 4, 6, or *") if $ipv && $ipv !~ /[46*]/; my @_info; if (!$ipv || $ipv =~ /[*]/) { my @rows = eval { $class->get_addr_info(@$info{qw(host port proto)}) }; $server->fatal($@ || "Could not find valid addresses for [$info->{'host'}]:$info->{'port'} with ipv set to '*'") if ! @rows; foreach my $row (@rows) { my ($host, $port, $ipv, $warn) = @$row; push @_info, {host => $host, port => $port, ipv => $ipv, proto => $info->{'proto'}, $warn ? (warn => $warn) : ()}; $requires_ipv6++ if $ipv ne '4' && $proto ne 'ssl'; # we need to know if Proto::TCP needs to reparent as a child of IO::Socket::INET6 } if (@rows > 1 && $rows[0]->[1] == 0) { $server->log(2, "Determining auto-assigned port (0) for host $info->{'host'} (prebind)"); my $sock = $class->object($_info[-1], $server); $sock->connect($server); @$_{qw(port orig_port)} = ($sock->NS_port, 0) for @_info; } foreach my $_info (@_info) { $server->log(2, "Resolved [$info->{'host'}]:$info->{'port'} to [$_info->{'host'}]:$_info->{'port'}, IPv$_info->{'ipv'}") if $_info->{'host'} ne $info->{'host'} || $_info->{'port'} ne $info->{'port'}; $server->log(2, delete $_info->{'warn'}) if $_info->{'warn'}; } } elsif ($ipv =~ /6/ || $info->{'host'} =~ /:/) { push @_info, {%$info, ipv => '6'}; $requires_ipv6++ if $proto ne 'ssl'; # IO::Socket::SSL does its own determination push @_info, {%$info, ipv => '4'} if $ipv =~ /4/ && $info->{'host'} !~ /:/; } else { push @_info, {%$info, ipv => '4'}; } return @_info; } sub get_addr_info { my ($class, $host, $port, $proto) = @_; $host = '*' if ! defined $host; $port = 0 if ! defined $port; $proto = 'tcp' if ! defined $proto; return ([$host, $port, '*']) if $proto =~ /UNIX/i; $port = (getservbyname($port, $proto))[2] or die "Could not determine port number from host [$host]:$_[2]\n" if $port =~ /\D/; my @info; if ($host =~ /^\d+(?:\.\d+){3}$/) { my $addr = Socket::inet_aton($host) or die "Unresolveable host [$host]:$port: invalid ip\n"; push @info, [Socket::inet_ntoa($addr), $port, 4] } elsif (!$ENV{'NO_IPV6'} && eval { require Socket6; require IO::Socket::INET6 }) { my $proto_id = getprotobyname(lc($proto) eq 'udp' ? 'udp' : 'tcp'); my $socktype = lc($proto) eq 'udp' ? Socket::SOCK_DGRAM() : Socket::SOCK_STREAM(); my @res = Socket6::getaddrinfo($host eq '*' ? '' : $host, $port, Socket::AF_UNSPEC(), $socktype, $proto_id, Socket6::AI_PASSIVE()); die "Unresolveable [$host]:$port: $res[0]\n" if @res < 5; while (@res >= 5) { my ($afam, $socktype, $proto, $saddr, $canonname) = splice @res, 0, 5; my @res2 = Socket6::getnameinfo($saddr, Socket6::NI_NUMERICHOST() | Socket6::NI_NUMERICSERV()); die "getnameinfo failed on [$host]:$port: $res2[0]\n" if @res2 < 2; my ($ip, $port) = @res2; my $ipv = ($afam == Socket6::AF_INET6()) ? 6 : ($afam == Socket::AF_INET()) ? 4 : '*'; push @info, [$ip, $port, $ipv]; } my %ipv6mapped = map {$_->[0] eq '::' ? ('0.0.0.0' => $_) : $_->[0] =~ /^::ffff:(\d+(?:\.\d+){3})$/ ? ($1 => $_) : ()} @info; if ((scalar(keys %ipv6mapped) && grep {$ipv6mapped{$_->[0]}} @info) && not my $only = $class->_bindv6only) { for my $i4 (@info) { my $i6 = $ipv6mapped{$i4->[0]} || next; if ($host eq '*' && $i6->[0] eq '::' && !length($only) && !eval{IO::Socket::INET6->new->configure({LocalAddr => '', LocalPort => 0, Listen => 1, ReuseAddr => 1, Domain => Socket6::AF_INET6()}) or die $!}) { $i4->[3] = "Host [*] resolved to IPv6 address [::] but IO::Socket::INET6->new fails: $@"; $i6->[0] = ''; } else { $i6->[3] = "Not including resolved host [$i4->[0]] IPv4 because it ".(length($only) ? 'will' : 'should')." be handled by [$i6->[0]] IPv6"; $i4->[0] = ''; } } @info = grep {length $_->[0]} @info; } } elsif ($host =~ /:/) { die "Unresolveable host [$host]:$port - could not load IO::Socket::INET6: $@"; } else { my @addr; if ($host eq '*') { push @addr, Socket::INADDR_ANY(); } else { (undef, undef, undef, undef, @addr) = gethostbyname($host); die "Unresolveable host [$host]:$port via IPv4 gethostbyname\n" if !@addr; } push @info, [Socket::inet_ntoa($_), $port, 4] for @addr } return @info; } sub _bindv6only { my $class = shift; my $val = $class->_sysctl('net.ipv6.bindv6only'); # linux $val = $class->_sysctl('net.inet6.ip6.v6only') if ! length($val); # bsd return $val; } sub _sysctl { my ($class, $key) = @_; (my $file = "/proc/sys/$key") =~ y|.|/|; if (-e $file) { open my $fh, "<", $file or return ''; my $val = <$fh> || return ''; chomp $val; return $val; } elsif (-x "/sbin/sysctl") { my $val = (split /\s+/, `/sbin/sysctl -n $key 2>/dev/null`)[0]; return defined($val) ? $val : ''; } return ''; } sub object { my ($class, $info, $server) = @_; my $proto_class = $info->{'proto'}; if ($proto_class !~ /::/) { $server->fatal("Invalid proto class \"$proto_class\"") if $proto_class !~ /^\w+$/; $proto_class = "Net::Server::Proto::" .uc($proto_class); } (my $file = "${proto_class}.pm") =~ s|::|/|g; $server->fatal("Unable to load module for proto \"$proto_class\": $@") if ! eval { require $file }; return $proto_class->object($info, $server); } sub requires_ipv6 { my ($class, $server) = @_; return if ! $requires_ipv6; if (! $INC{'IO/Socket/INET6.pm'}) { eval { require Socket6; require IO::Socket::INET6; } or $server->fatal("Port configuration using IPv6 could not be started becauses of Socket6 library issues: $@"); } return 1; } 1; __END__ =head1 NAME Net::Server::Proto - Net::Server Protocol compatibility layer =head1 SYNOPSIS NOTE: beginning in Net::Server 2.005, the default value for ipv is IPv* meaning that if no host is passed, or a hostname is past, all available socket types will be bound. You can force IPv4 only by adding an ipv => 4 configuration in any of the half dozen ways we let you specify it. # Net::Server::Proto and its accompanying modules are not # intended to be used outside the scope of Net::Server. # That being said, here is how you use them. This is # only intended for anybody wishing to extend the # protocols to include some other set (ie maybe a # database connection protocol) use Net::Server::Proto; my @info = Net::Server::Proto->parse_info( $port, # port to connect to $default_host, # host to use if none found in port $default_proto, # proto to use if none found in port $default_ipv, # default of IPv6 or IPv4 if none found in port $server_obj, # Net::Server object ); my @raw_info = Net::Server::Proto->get_addr_info($host, $port, $proto); # returns arrayref of resolved ips, ports, and ipv values my $sock = Net::Server::Proto->object({ port => $port, host => $host, proto => $proto, ipv => $ipv, # * (IPv*) if false (default false) }, $server); # Net::Server::Proto will attempt to interface with # sub modules named similar to Net::Server::Proto::TCP # Individual sub modules will be loaded by # Net::Server::Proto as they are needed. use Net::Server::Proto::TCP; # or UDP or UNIX etc # Return an object which is a sub class of IO::Socket # At this point the object is not connected. # The method can gather any other information that it # needs from the server object. my $sock = Net::Server::Proto::TCP->object({ port => $port, host => $host, proto => $proto, ipv => 6, # IPv6 - default is * - can also be '4' }, $server); # Log that a connection is about to occur. # Use the facilities of the passed Net::Server object. $sock->log_connect( $server ); # Actually bind to port or socket file. This # is typically done by calling the configure method. $sock->connect(); # Allow for rebinding to an already open fileno. # Typically will just do an fdopen. $sock->reconnect(); ### Return a unique identifying string for this sock that # can be used when reconnecting. my $str = $sock->hup_string(); # Return the proto that is being used by this module. my $proto = $sock->NS_proto(); =head1 DESCRIPTION Net::Server::Proto is an intermediate module which returns IO::Socket style objects blessed into its own set of classes (ie Net::Server::Proto::TCP, Net::Server::Proto::UNIX). Only three or four protocols come bundled with Net::Server. TCP, UDP, UNIX, UNIXDGRAM, and SSLEAY. TCP is an implementation of SOCK_STREAM across an INET socket. UDP is an implementation of SOCK_DGRAM across an INET socket. UNIX uses a unix style socket file with the SOCK_STREAM protocol. UNIXGRAM uses a unix style socket file with the SOCK_DGRAM protocol. SSLEAY is actually just a layer on top of TCP but uses Net::SSLeay to read and write from the stream. The protocol that is passed to Net::Server can be the name of another module which contains the protocol bindings. If a protocol of MyServer::MyTCP was passed, the socket would be blessed into that class. If Net::Server::Proto::TCP was passed, it would get that class. If a bareword, such as tcp, udp, unix, unixdgram or ssleay, is passed, the word is uppercased, and post pended to "Net::Server::Proto::" (ie tcp = Net::Server::Proto::TCP). =head1 METHODS Protocol names used by the Net::Server::Proto should be sub classes of IO::Socket. These classes should also contain, as a minimum, the following methods should be provided: =over 4 =item object Return an object which is a sub class of IO::Socket At this point the object is not connected. The method can gather any other information that it needs from the server object. Arguments are default_host, port, and a Net::Server style server object. =item log_connect Log that a connection is about to occur. Use the facilities of the passed Net::Server object. This should be an informative string explaining which properties are being used. =item connect Actually bind to port or socket file. This is typically done internally by calling the configure method of the IO::Socket super class. =item reconnect Allow for rebinding to an already open fileno. Typically will just do an fdopen using the IO::Socket super class. =item hup_string Return a unique identifying string for this sock that can be used when reconnecting. This is done to allow information including the file descriptor of the open sockets to be passed via %ENV during an exec. This string should always be the same based upon the configuration parameters. =item NS_port Net::Server protocol. Return the port that is being used by this module. If the underlying type is UNIX then port will actually be the path to the unix socket file. =item NS_host Net::Server protocol. Return the protocol that is being used by this module. This does not have to be a registered or known protocol. =item NS_proto Net::Server protocol. Return the protocol that is being used by this module. This does not have to be a registered or known protocol. =item show Similar to log_connect, but simply shows a listing of which properties were found. Can be used at any time. =back =head1 HOST The hostname may be either blank, '*', be an IPv4 address, an IPv6 address, a bare hostname, or a hostname with IPv* specifications. host => "127.0.0.1", # an IPv4 address host => "::1", # an IPv6 address host => 'localhost', # addresses returned by localhost (default IPv* - IPv4 and/or IPv6) host => 'localhost/IPv*', # same ipv => '*', host => 'localhost', # same ipv => 6, host => 'localhost', # addresses returned by localhost (IPv6) ipv => 'IPv4 IPv6', host => 'localhost', # addresses returned by localhost (requires IPv6 and IPv4) host => '*', # any local interfaces (default IPv*) ipv => '*', host => '*', # any local interfaces (any IPv6 or IPv4) host => '*/IPv*', # same =head1 IPV In addition to being able to specify IPV as a separate parameter, ipv may also be passed as a part of the host, as part of the port, as part of the protocol or may be specified via $ENV{'IPV'}. The order of precidence is as follows: 1) Explicit IPv4 or IPv6 address - wins 2) ipv specified in port 3) ipv specified in host 4) ipv specified in proto 5) ipv specified in default settings 6) ipv specified in $ENV{'IPV'} 7) default to IPv* =head1 PORT The port is the most important argument passed to the sub module classes and to Net::Server::Proto itself. For tcp, udp, and ssleay style ports, the form is generally host:port/protocol, [host]:port/protocol, host|port|protocol, host/port, or port. If I is a numerical IPv6 address it should be enclosed in square brackets to avoid ambiguity in parsing a port number, e.g.: "[::1]:80". Separating with spaces, commas, or pipes is also allowed, e.g. "::1, 80". For unix sockets the form is generally socket_file|unix or socket_file. To help overcome parsing ambiguity, it is also possible to pass port as a hashref (or as an array of hashrefs) of information such as: port => { host => "localhost", ipv => 6, # could also pass IPv6 (* is default) port => 20203, proto => 'tcp', } If a hashref does not include host, ipv, or proto - it will use the default value supplied by the general configuration. A socket protocol family PF_INET or PF_INET6 is derived from a specified address family of the binding address. A PF_INET socket can only accept IPv4 connections. A PF_INET6 socket accepts IPv6 connections, but may also accept IPv4 connections, depending on OS and its settings. For example, on FreeBSD systems setting a sysctl net.inet6.ip6.v6only to 0 will allow IPv4 connections to a PF_INET6 socket. By default on linux, binding to host [::] will accept IPv4 or IPv6 connections. The Net::Server::Proto::object method returns a list of objects corresponding to created sockets. For Unix and INET sockets the list typically contains just one element, but may return multiple objects when multiple protocol families are allowed or when a host name resolves to multiple local binding addresses. This is particularly true when an ipv value of '*' is passed in allowing hostname resolution. You can see what Net::Server::Proto parsed out by looking at the logs to see what log_connect said. You could also include a post_bind_hook similar to the following to debug what happened: sub post_bind_hook { my $self = shift; foreach my $sock ( @{ $self->{server}->{sock} } ){ $self->log(2,$sock->show); } } Rather than try to explain further, please look at the following examples: # example 1 #---------------------------------- $port = "20203"; $def_host = "default-domain.com"; $def_proto = undef; $def_ipv = undef; @info = Net::Server::Proto->parse_info($port,$def_host,$def_proto,$def_ipv); # @info = { # host => 'default-domain.com', # port => 20203, # proto => 'tcp', # will use Net::Server::Proto::TCP # ipv => *, # IPv* # }; # example 2 #---------------------------------- $port = "someother.com:20203"; $def_host = "default-domain.com"; $def_proto = "tcp"; $def_ipv = undef; @info = Net::Server::Proto->parse_info($port,$def_host,$def_proto,$def_ipv); # @info = { # host => 'someother.com', # port => 20203, # proto => 'tcp', # will use Net::Server::Proto::TCP # ipv => *, # }; # example 3 #---------------------------------- $port = "someother.com:20203/udp"; $def_host = "default-domain.com"; $def_proto = "tcp"; $def_ipv = undef; @info = Net::Server::Proto->parse_info($port,$def_host,$def_proto,$def_ipv); # @info = { # host => 'someother.com', # port => 20203, # proto => 'udp', # will use Net::Server::Proto::UDP # ipv => *, # }; # example 4 #---------------------------------- $port = "someother.com:20203/Net::Server::Proto::UDP"; $def_host = "default-domain.com"; $def_proto = "TCP"; $def_ipv = 4; @info = Net::Server::Proto->parse_info($port,$def_host,$def_proto,$def_ipv); # @info = { # host => 'someother.com', # port => 20203, # proto => 'Net::Server::Proto::UDP', # ipv => 4, # }; # example 5 #---------------------------------- $port = "someother.com:20203/MyObject::TCP"; $def_host = "default-domain.com"; $def_proto = "tcp"; @info = Net::Server::Proto->parse_info($port,$def_host,$def_proto); # @info = { # host => 'someother.com', # port => 20203, # proto => 'MyObject::TCP', # }; # example 6 #---------------------------------- $port = "/tmp/mysock.file|unix"; $def_host = "default-domain.com"; $def_proto = "tcp"; $def_ipv = undef; @info = Net::Server::Proto->parse_info($port,$def_host,$def_proto,$def_ipv); # @info = { # host => '*', # irrelevant for UNIX socket # port => '/tmp/mysock.file', # not really a port # proto => 'unix', # will use Net::Server::Proto::UNIX # ipv => '*', # irrelevant for UNIX socket # }; # example 7 #---------------------------------- $port = "/tmp/mysock.file|unixdgram"; $def_host = "default-domain.com"; $def_proto = "tcp"; $def_ipv = undef; @info = Net::Server::Proto->parse_info($port,$def_host,$def_proto,$def_ipv); # @info = { # host => '*', # irrelevant for UNIX socket # port => '/tmp/mysock.file', # not really a port # proto => 'unixdgram', # will use Net::Server::Proto::UNIXDGRAM # ipv => '*', # irrelevant for UNIX socket # }; # example 8 #---------------------------------- $port = "/tmp/mysock.file|SOCK_STREAM|unix"; # legacy $def_host = ""; $def_proto = "tcp"; $def_ipv = undef; @info = Net::Server::Proto->parse_info($port,$def_host,$def_proto,$def_ipv); # @info = { # host => '*', # irrelevant for UNIX socket # port => '/tmp/mysock.file', # not really a port # proto => 'unix', # will use Net::Server::Proto::UNIX # unix_type => 'SOCK_STREAM', # ipv => '*', # irrelevant for UNIX socket # }; # example 9 #---------------------------------- $port = "/tmp/mysock.file|SOCK_DGRAM|unix"; # legacy $def_host = ""; $def_proto = "tcp"; $def_ipv = undef; @info = Net::Server::Proto->parse_info($port,$def_host,$def_proto,$def_ipv); # @info = { # host => '*', # irrelevant for UNIX socket # port => '/tmp/mysock.file', # not really a port # proto => 'unix', # will use Net::Server::Proto::UNIXDGRAM # unix_type => 'SOCK_DGRAM', # ipv => '*', # irrelevant for UNIX socket # }; # example 10 #---------------------------------- $port = "someother.com:20203/ssleay"; $def_host = "default-domain.com"; $def_proto = "tcp"; $def_ipv = undef; @info = Net::Server::Proto->parse_info($port,$def_host,$def_proto,$def_ipv); # @info = { # host => 'someother.com', # port => 20203, # proto => 'ssleay', # will use Net::Server::Proto::SSLEAY # ipv => *, # }; # example 11 #---------------------------------- $port = "[::1]:20203 ipv6 tcp"; $def_host = "default-domain.com"; $def_proto = "tcp"; $def_ipv = undef; @info = Net::Server::Proto->parse_info($port,$def_host,$def_proto,$def_ipv); # @info = { # host => '::1', # port => 20203, # proto => 'tcp', # will use Net::Server::Proto::TCP # ipv => 6, # }; # example 12 #---------------------------------- $port = "[::1]:20203 tcp"; $def_host = "default-domain.com/IPv6"; $def_proto = "tcp"; $def_ipv = undef; @info = Net::Server::Proto->parse_info($port,$def_host,$def_proto,$def_ipv); # @info = { # host => '::1', # port => 20203, # proto => 'tcp', # will use Net::Server::Proto::TCP # ipv => 6, # }; # example 13 #---------------------------------- $port = "[someother.com]:20203 ipv6 ipv4 tcp"; $def_host = "default-domain.com"; $def_proto = "tcp"; $def_ipv = undef; @info = Net::Server::Proto->parse_info($port,$def_host,$def_proto,$def_ipv); # @info = ({ # host => 'someother.com', # port => 20203, # proto => 'tcp', # will use Net::Server::Proto::TCP # ipv => 4, # }, { # host => 'someother.com', # port => 20203, # proto => 'tcp', # will use Net::Server::Proto::TCP # ipv => 6, # }); # example 14 #---------------------------------- # depending upon your configuration $port = "localhost:20203"; $def_host = "default-domain.com"; $def_proto = "tcp"; $def_ipv = undef; @info = Net::Server::Proto->parse_info($port,$def_host,$def_proto,$def_ipv); # @info = ({ # host => '127.0.0.1', # port => 20203, # proto => 'tcp', # will use Net::Server::Proto::TCP # ipv => 4, # IPv4 # }, { # host => '::1', # port => 20203, # proto => 'tcp', # will use Net::Server::Proto::TCP # ipv => 6, # IPv6 # }); # example 15 #---------------------------------- # depending upon your configuration $port = "localhost:20203"; $def_host = "default-domain.com IPv*"; $def_proto = "tcp"; $def_ipv = undef; @info = Net::Server::Proto->parse_info($port,$def_host,$def_proto,$def_ipv); # @info = ({ # host => '127.0.0.1', # port => 20203, # proto => 'tcp', # will use Net::Server::Proto::TCP # ipv => 4, # IPv4 # }, { # host => '::1', # port => 20203, # proto => 'tcp', # will use Net::Server::Proto::TCP # ipv => 6, # IPv6 # }); # example 16 #---------------------------------- # depending upon your configuration $ENV{'IPV'} = '4'; $port = "localhost:20203"; $def_host = "default-domain.com"; $def_proto = "tcp"; $def_ipv = undef; @info = Net::Server::Proto->parse_info($port,$def_host,$def_proto,$def_ipv); # @info = ({ # host => '127.0.0.1', # port => 20203, # proto => 'tcp', # will use Net::Server::Proto::TCP # ipv => 4, # IPv4 # }); =head1 LICENCE Distributed under the same terms as Net::Server =cut Net-Server-2.008/lib/Net/Server/PreFork.pm0000644000175000017500000005253612331755703016672 0ustar paulpaul# -*- perl -*- # # Net::Server::PreFork - Net::Server personality # # $Id$ # # Copyright (C) 2001-2012 # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # # All rights reserved. # ################################################################ package Net::Server::PreFork; use strict; use base qw(Net::Server::PreForkSimple); use Net::Server::SIG qw(register_sig check_sigs); use POSIX qw(WNOHANG); use IO::Select (); use Time::HiRes qw(time); sub net_server_type { __PACKAGE__ } sub options { my $self = shift; my $ref = $self->SUPER::options(@_); my $prop = $self->{'server'}; $ref->{$_} = \$prop->{$_} for qw(min_servers min_spare_servers max_spare_servers spare_servers check_for_waiting child_communication check_for_spawn min_child_ttl); return $ref; } sub post_configure { my $self = shift; my $prop = $self->{'server'}; $self->SUPER::post_configure; my $d = { # max_servers is set in the PreForkSimple server and defaults to 50 min_servers => 5, # min num of servers to always have running min_spare_servers => 2, # min num of servers just sitting there max_spare_servers => 10, # max num of servers just sitting there check_for_waiting => 10, # how often to see if children laying around check_for_spawn => 30, # how often to see if more children are needed min_child_ttl => 10, # min time between starting a child and killing one }; $prop->{'min_servers'} = $prop->{'max_servers'} if !!defined($prop->{'min_servers'}) && $d->{'min_servers'} > $prop->{'max_servers'}; $prop->{'max_spare_servers'} = $prop->{'max_servers'} - 1 if !defined($prop->{'max_spare_servers'}) && $d->{'max_spare_servers'} >= $prop->{'max_servers'}; if (! defined $prop->{'min_spare_servers'}) { my $min = defined($prop->{'min_servers'}) ? $prop->{'min_servers'} : $d->{'min_servers'}; $prop->{'min_spare_servers'} = $min if $prop > $min; } foreach (keys %$d){ $prop->{$_} = $d->{$_} if !defined($prop->{$_}) || $prop->{$_} !~ /^\d+(?:\.\d+)?$/; } if( $prop->{'max_spare_servers'} >= $prop->{'max_servers'} ){ $self->fatal("Error: \"max_spare_servers\" must be less than \"max_servers\""); } if ($prop->{'min_spare_servers'}) { $self->fatal("Error: \"min_spare_servers\" ($prop->{'min_spare_servers'}) must be less than \"$_\" ($prop->{$_})") for grep {$prop->{'min_spare_servers'} > $prop->{$_}} qw(min_servers max_spare_servers); } } sub loop { my $self = shift; my $prop = $self->{'server'}; pipe(my $read, my $write); # get ready for child->parent communication $read->autoflush(1); $write->autoflush(1); $prop->{'_READ'} = $read; $prop->{'_WRITE'} = $write; # get ready for children $prop->{'child_select'} = IO::Select->new($read); $prop->{'children'} = {}; $prop->{'reaped_children'} = {}; if ($ENV{'HUP_CHILDREN'}) { foreach my $line (split /\n/, $ENV{'HUP_CHILDREN'}) { my ($pid, $status) = ($line =~ /^(\d+)\t(\w+)$/) ? ($1, $2) : next; $prop->{'children'}->{$pid} = {status => $status, hup => 1}; } } $prop->{'tally'} = { time => time(), waiting => scalar(grep {$_->{'status'} eq 'waiting'} values %{ $prop->{'children'} }), processing => scalar(grep {$_->{'status'} eq 'processing'} values %{ $prop->{'children'} }), dequeue => scalar(grep {$_->{'status'} eq 'dequeue'} values %{ $prop->{'children'} }), }; my $start = $prop->{'min_servers'}; $self->log(3, "Beginning prefork ($start processes)"); $self->run_n_children($start); $self->run_parent; } sub kill_n_children { my ($self, $n) = @_; my $prop = $self->{'server'}; return unless $n > 0; my $time = time; return unless $time - $prop->{'last_kill'} > 10; $prop->{'last_kill'} = $time; $self->log(3, "Killing \"$n\" children"); foreach my $pid (keys %{ $prop->{'children'} }){ # Only kill waiting children # XXX: This is race condition prone as the child may have # started handling a connection, but will have to do for now my $child = $prop->{'children'}->{$pid}; next if $child->{'status'} ne 'waiting'; $n--; if (! kill('HUP', $pid)) { $self->delete_child($pid); } last if $n <= 0; } } sub run_n_children { my ($self, $n) = @_; my $prop = $self->{'server'}; return unless $n > 0; $self->run_n_children_hook($n); my ($parentsock, $childsock); $self->log(3, "Starting \"$n\" children"); $prop->{'last_start'} = time(); for (1 .. $n) { if ($prop->{'child_communication'}) { require IO::Socket::UNIX; ($parentsock, $childsock) = IO::Socket::UNIX->socketpair(IO::Socket::AF_UNIX, IO::Socket::SOCK_STREAM, IO::Socket::PF_UNSPEC); } $self->pre_fork_hook; local $!; my $pid = fork; if (! defined $pid) { if ($prop->{'child_communication'}) { $parentsock->close(); $childsock->close(); } $self->fatal("Bad fork [$!]"); } if ($pid) { # parent if( $prop->{'child_communication'} ){ $prop->{'child_select'}->add($parentsock); $prop->{'children'}->{$pid}->{'sock'} = $parentsock; } $prop->{'children'}->{$pid}->{'status'} = 'waiting'; $prop->{'tally'}->{'waiting'} ++; } else { # child if ($prop->{'child_communication'}) { $prop->{'parent_sock'} = $childsock; } $self->run_child; } } } sub run_n_children_hook {} sub run_child { my $self = shift; my $prop = $self->{'server'}; $SIG{'INT'} = $SIG{'TERM'} = $SIG{'QUIT'} = sub { $self->child_finish_hook; exit; }; $SIG{'PIPE'} = 'IGNORE'; $SIG{'CHLD'} = 'DEFAULT'; $SIG{'HUP'} = sub { if (! $prop->{'connected'}) { $self->child_finish_hook; exit; } $prop->{'SigHUPed'} = 1; }; # Open in child at start if ($prop->{'serialize'} eq 'flock') { open $prop->{'lock_fh'}, ">", $prop->{'lock_file'} or $self->fatal("Couldn't open lock file \"$prop->{'lock_file'}\"[$!]"); } $self->log(4, "Child Preforked ($$)"); delete @{ $prop }{qw(children tally last_start last_process)}; $self->child_init_hook; my $write = $prop->{'_WRITE'}; while ($self->accept()) { $prop->{'connected'} = 1; print $write "$$ processing\n"; my $ok = eval { $self->run_client_connection; 1 }; if (! $ok) { print $write "$$ exiting\n"; die $@; } last if $self->done; $prop->{'connected'} = 0; print $write "$$ waiting\n"; } $self->child_finish_hook; print $write "$$ exiting\n"; exit; } sub run_parent { my $self = shift; my $prop = $self->{'server'}; my $id; $self->log(4, "Parent ready for children."); my $read_fh = $prop->{'_READ'}; @{ $prop }{qw(last_checked_for_dead last_checked_for_waiting last_checked_for_dequeue last_process last_kill)} = (time) x 5; register_sig( PIPE => 'IGNORE', INT => sub { $self->server_close() }, TERM => sub { $self->server_close() }, HUP => sub { $self->sig_hup() }, CHLD => sub { while (defined(my $chld = waitpid(-1, WNOHANG))) { last unless $chld > 0; $self->{'reaped_children'}->{$chld} = 1; # We'll deal with this in coordinate_children to avoid a race } }, QUIT => sub { $self->{'server'}->{'kind_quit'} = 1; $self->server_close() }, TTIN => sub { $self->{'server'}->{$_}++ for qw(min_servers max_servers); $self->log(3, "Increasing server count ($self->{'server'}->{'max_servers'})") }, TTOU => sub { $self->{'server'}->{$_}-- for qw(min_servers max_servers); $self->log(3, "Decreasing server count ($self->{'server'}->{'max_servers'})") }, ); $self->register_sig_pass; if ($ENV{'HUP_CHILDREN'}) { while (defined(my $chld = waitpid(-1, WNOHANG))) { last unless $chld > 0; $self->{'reaped_children'}->{$chld} = 1; } } while (1) { ### Wait to read. ## Normally it is not good to do selects with ## getline or <$fh> but this is controlled output ## where everything that comes through came from us. my @fh = $prop->{'child_select'}->can_read($prop->{'check_for_waiting'}); if (check_sigs()) { last if $prop->{'_HUP'}; } $self->idle_loop_hook(\@fh); if (! @fh) { $self->coordinate_children(); next; } foreach my $fh (@fh) { if ($fh != $read_fh) { # preforking server data $self->child_is_talking_hook($fh); next; } my $line = <$fh>; next if ! defined $line; last if $self->parent_read_hook($line); # optional test by user hook # child should say "$pid status\n" next if $line !~ /^(\d+)\ +(waiting|processing|dequeue|exiting)$/; my ($pid, $status) = ($1, $2); if (my $child = $prop->{'children'}->{$pid}) { if ($status eq 'exiting') { $self->delete_child($pid); } else { # Decrement tally of state pid was in (plus sanity check) my $old_status = $child->{'status'} || $self->log(2, "No status for $pid when changing to $status"); --$prop->{'tally'}->{$old_status} >= 0 || $self->log(2, "Tally for $status < 0 changing pid $pid from $old_status to $status"); $child->{'status'} = $status; ++$prop->{'tally'}->{$status}; $prop->{'last_process'} = time() if $status eq 'processing'; } } } $self->coordinate_children(); } } sub run_dequeue { my $self = shift; $self->SUPER::run_dequeue; $self->{'server'}->{'tally'}->{'dequeue'}++; } sub coordinate_children { my $self = shift; my $prop = $self->{'server'}; my $time = time(); # deleted SIG{'CHLD'} reaped children foreach my $pid (keys %{ $self->{'reaped_children'} }) { delete $self->{'reaped_children'}->{$pid}; # delete each pid one by one to avoid another race next if ! $prop->{'children'}->{$pid}; $self->delete_child($pid); } # re-tally the possible types (only twice a minute) # this might not be even necessary but is a nice sanity check my $tally = $prop->{'tally'} ||= {}; if ($time - $tally->{'time'} > $prop->{'check_for_spawn'}) { my $w = $tally->{'waiting'}; my $p = $tally->{'processing'}; $tally = $prop->{'tally'} = { time => $time, waiting => 0, processing => 0, dequeue => 0, }; foreach (values %{ $prop->{'children'} }) { $tally->{$_->{'status'}}++; } $w -= $tally->{'waiting'}; $p -= $tally->{'processing'}; $self->log(3, "Processing diff ($p), Waiting diff ($w)") if $p || $w; } my $total = $tally->{'waiting'} + $tally->{'processing'}; if ($total < $prop->{'min_servers'}) { $self->run_n_children($prop->{'min_servers'} - $total); # need more min_servers } elsif ($tally->{'waiting'} < $prop->{'min_spare_servers'} && $total < $prop->{'max_servers'}) { # need more min_spare_servers (up to max_servers) my $n1 = $prop->{'min_spare_servers'} - $tally->{'waiting'}; my $n2 = $prop->{'max_servers'} - $total; $self->run_n_children(($n2 > $n1) ? $n1 : $n2); } # check to see if we should kill off some children if ($time - $prop->{'last_checked_for_waiting'} > $prop->{'check_for_waiting'}) { $prop->{'last_checked_for_waiting'} = $time; # need fewer max_spare_servers (down to min_servers) if ($tally->{'waiting'} > $prop->{'max_spare_servers'} && $total > $prop->{'min_servers'}) { ### see if we haven't started any in the last ten seconds if ($time - $prop->{'last_start'} > $prop->{'min_child_ttl'}) { my $n1 = $tally->{'waiting'} - $prop->{'max_spare_servers'}; my $n2 = $total - $prop->{'min_servers'}; $self->kill_n_children(($n2 > $n1) ? $n1 : $n2); } } elsif ($total > $prop->{'max_servers'}) { # how did this happen? $self->kill_n_children($total - $prop->{'max_servers'}); } } # periodically make sure children are alive if ($time - $prop->{'last_checked_for_dead'} > $prop->{'check_for_dead'}) { $prop->{'last_checked_for_dead'} = $time; foreach my $pid (keys %{ $prop->{'children'} }) { kill(0, $pid) || $self->delete_child($pid); } } # take us down to min if we haven't had a request in a while if ($time - $prop->{'last_process'} > 30 && $tally->{'waiting'} > $prop->{'min_spare_servers'}) { my $n1 = $tally->{'waiting'} - $prop->{'min_spare_servers'}; my $n2 = $total - $prop->{'min_servers'}; $self->kill_n_children( ($n2 > $n1) ? $n1 : $n2 ); } # periodically check to see if we should clear the queue if (defined $prop->{'check_for_dequeue'}) { if ($time - $prop->{'last_checked_for_dequeue'} > $prop->{'check_for_dequeue'}) { $prop->{'last_checked_for_dequeue'} = $time; if (defined($prop->{'max_dequeue'}) && $tally->{'dequeue'} < $prop->{'max_dequeue'}) { $self->run_dequeue(); } } } } ### delete_child and other modifications contributed by Rob Mueller sub delete_child { my ($self, $pid) = @_; my $prop = $self->{'server'}; my $child = $prop->{'children'}->{$pid}; if (! $child) { $self->log(2, "Attempt to delete already deleted child $pid"); return; } return if ! exists $prop->{'children'}->{$pid}; # Already gone? my $status = $child->{'status'} || $self->log(2, "No status for $pid when deleting child"); --$prop->{'tally'}->{$status} >= 0 || $self->log(2, "Tally for $status < 0 deleting pid $pid"); $prop->{'tally'}->{'time'} = 0 if $child->{'hup'}; $self->SUPER::delete_child($pid); } sub parent_read_hook {} sub child_is_talking_hook {} 1; __END__ =head1 NAME Net::Server::PreFork - Net::Server personality =head1 SYNOPSIS use base qw(Net::Server::PreFork); sub process_request { #...code... } __PACKAGE__->run(); =head1 DESCRIPTION Please read the pod on Net::Server and Net::Server::PreForkSimple first. This module is a personality, or extension, or sub class, of the Net::Server::PreForkSimple class which is a sub class of Net::Server. See L. This personality binds to one or more ports and then forks C child process. The server will make sure that at any given time there are C available to receive a client request, up to C. Each of these children will process up to C client connections. This type is good for a heavily hit site, and should scale well for most applications. (Multi port accept is accomplished using flock to serialize the children). At this time, it does not appear that this module will pass tests on Win32 systems. Any ideas or patches for making the tests pass would be welcome. =head1 SAMPLE CODE Please see the sample listed in Net::Server. =head1 COMMAND LINE ARGUMENTS In addition to the command line arguments of the Net::Server base class and the Net::Server::PreForkSimple parent class, Net::Server::PreFork contains several other configurable parameters. You really should also see L. Key Value Default min_servers \d+ 5 min_spare_servers \d+ 2 max_spare_servers \d+ 10 max_servers \d+ 50 max_requests \d+ 1000 serialize (flock|semaphore |pipe|none) undef # serialize defaults to flock on multi_port or on Solaris lock_file "filename" File::Temp::tempfile or POSIX::tmpnam check_for_dead \d+ 30 check_for_waiting \d+ 10 max_dequeue \d+ undef check_for_dequeue \d+ undef child_communication 1 undef =over 4 =item min_servers The minimum number of servers to keep running. =item min_spare_servers The minimum number of servers to have waiting for requests. Minimum and maximum numbers should not be set to close to each other or the server will fork and kill children too often. =item max_spare_servers The maximum number of servers to have waiting for requests. See I. =item max_servers The maximum number of child servers to start. This does not apply to dequeue processes. =item check_for_waiting Seconds to wait before checking to see if we can kill off some waiting servers. =item check_for_spawn Seconds between checking to see if we need to spawn more children =item min_child_ttl Minimum number of seconds between starting children and killing a child process =item child_communication Enable child communication to parent via unix sockets. If set to true, will let children write to the socket contained in $self->{'server'}->{'parent_sock'}. The parent will be notified through child_is_talking_hook where the first argument is the socket to the child. The child's socket is stored in $self->{'server'}->{'children'}->{$child_pid}->{'sock'}. =item serialize See the documentation under L. =back =head1 CONFIGURATION FILE C allows for the use of a configuration file to read in server parameters. The format of this conf file is simple key value pairs. Comments and white space are ignored. #-------------- file test.conf -------------- ### server information min_servers 20 max_servers 80 min_spare_servers 10 min_spare_servers 15 max_requests 1000 ### user and group to become user somebody group everybody ### logging ? log_file /var/log/server.log log_level 3 pid_file /tmp/server.pid ### access control allow .+\.(net|com) allow domain\.com deny a.+ ### background the process? background 1 ### ports to bind host 127.0.0.1 port localhost:20204 port 20205 ### reverse lookups ? # reverse_lookups on ### enable child communication ? # child_communication #-------------- file test.conf -------------- =head1 PROCESS FLOW Process flow follows Net::Server until the loop phase. At this point C are forked and wait for connections. When a child accepts a connection, finishs processing a client, or exits, it relays that information to the parent, which keeps track and makes sure there are enough children to fulfill C, C, C, and C. =head1 HOOKS The PreFork server has the following hooks in addition to the hooks provided by PreForkSimple. See L. =over 4 =item C<$self-Erun_n_children_hook()> This hook occurs at the top of run_n_children which is called each time the server goes to start more child processes. This gives the parent to do a little of its own accountting (as desired). Idea for this hook came from James FitzGibbon. =item C<$self-Eparent_read_hook()> This hook occurs any time that the parent reads information from the child. The line from the child is sent as an argument. =item C<$self-Echild_is_talking_hook()> This hook occurs if child_communication is true and the child has written to $self->{'server'}->{'parent_sock'}. The first argument will be the open socket to the child. =item C<$self-Eidle_loop_hook()> This hook is called in every pass through the main process wait loop, every C seconds. The first argument is a reference to an array of file descriptors that can be read at the moment. =back =head1 HOT DEPLOY Since version 2.000, the PreFork server has accepted the TTIN and TTOU signals. When a TTIN is received, the min and max_servers are increased by 1. If a TTOU signal is received the min max_servers are decreased by 1. This allows for adjusting the number of handling processes without having to restart the server. =head1 BUGS Tests don't seem to work on Win32. Any ideas or patches would be welcome. =head1 TO DO See L =head1 AUTHOR Paul T. Seamons paul@seamons.com =head1 THANKS See L =head1 SEE ALSO Please see also L, L, L, L, L L L L =cut Net-Server-2.008/lib/Net/Server/Proto/0000755000175000017500000000000012334210320016034 5ustar paulpaulNet-Server-2.008/lib/Net/Server/Proto/UNIX.pm0000644000175000017500000001231312331755703017175 0ustar paulpaul# -*- perl -*- # # Net::Server::Proto::UNIX - Net::Server Protocol module # # $Id$ # # Copyright (C) 2001-2012 # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # # All rights reserved. # ################################################################ package Net::Server::Proto::UNIX; use strict; use base qw(IO::Socket::UNIX); use Socket qw(SOCK_STREAM SOCK_DGRAM); sub NS_proto { 'UNIX' } sub NS_port { my $sock = shift; ${*$sock}{'NS_port'} = shift if @_; return ${*$sock}{'NS_port'} } sub NS_host { '*' } sub NS_ipv { '*' } sub NS_listen { my $sock = shift; ${*$sock}{'NS_listen'} = shift if @_; return ${*$sock}{'NS_listen'} } sub NS_unix_type { 'SOCK_STREAM' } sub NS_unix_path { shift->NS_port } # legacy systems used this sub object { my ($class, $info, $server) = @_; if ($class eq __PACKAGE__) { $server->configure({ unix_type => \$server->{'server'}->{'unix_type'}, unix_path => \$server->{'server'}->{'unix_path'}, # I don't believe this ever worked since a valid port specification also has to exist }) if ! exists $server->{'server'}->{'unix_type'}; my $u_type = uc( defined($info->{'unix_type'}) ? $info->{'unix_type'} : defined($server->{'server'}->{'unix_type'}) ? $server->{'server'}->{'unix_type'} : 'SOCK_STREAM'); if ($u_type eq 'SOCK_DGRAM' || $u_type eq ''.SOCK_DGRAM()) { # allow for legacy invocations passing unix_type to UNIX - now just use proto UNIXDGRAM require Net::Server::Proto::UNIXDGRAM; return Net::Server::Proto::UNIXDGRAM->object($info, $server); } elsif ($u_type ne 'SOCK_STREAM' && $u_type ne ''.SOCK_STREAM()) { $server->fatal("Invalid type for UNIX socket ($u_type)... must be SOCK_STREAM or SOCK_DGRAM"); } $info->{'port'} ||= $info->{'unix_path'} = $server->{'server'}->{'unix_path'}; } my $sock = $class->SUPER::new(); my $port = $info->{'port'} =~ m{^ ([\w\.\-\*\/]+) $ }x ? $1 : $server->fatal("Insecure filename"); $sock->NS_port($port); $sock->NS_listen(defined($info->{'listen'}) ? $info->{'listen'} : defined($server->{'server'}->{'listen'}) ? $server->{'server'}->{'listen'} : Socket::SOMAXCONN()); return $sock; } sub connect { my ($sock, $server) = @_; my $path = $sock->NS_port; $server->fatal("Can't connect to UNIX socket at file $path [$!]") if -e $path && ! unlink $path; $sock->SUPER::configure({ Local => $path, Type => SOCK_STREAM, Listen => $sock->NS_listen, }) or $server->fatal("Can't connect to UNIX socket at file $path [$!]"); } sub log_connect { my ($sock, $server) = @_; $server->log(2, "Binding to ".$sock->NS_proto." socket file \"".$sock->NS_port."\""); } sub reconnect { # connect on a sig -HUP my ($sock, $fd, $server) = @_; $sock->fdopen($fd, 'w') or $server->fatal("Error opening to file descriptor ($fd) [$!]"); } # a string containing any information necessary for restarting the server # via a -HUP signal # a newline is not allowed # the hup_string must be a unique identifier based on configuration info sub hup_string { my $sock = shift; return join "|", $sock->NS_host, $sock->NS_port, $sock->NS_proto, $sock->NS_ipv; } sub show { my $sock = shift; return "Ref = \"".ref($sock). "\" (".$sock->hup_string.")\n"; } 1; __END__ =head1 NAME Net::Server::Proto::UNIX - Net::Server UNIX protocol. =head1 SYNOPSIS See L. =head1 DESCRIPTION Protocol module for Net::Server. This module implements the UNIX SOCK_STREAM socket type. See L. Any sockets created during startup will be chown'ed to the user and group specified in the starup arguments. =head1 PARAMETERS The following paramaters may be specified in addition to normal command line parameters for a Net::Server. See L for more information on reading arguments. =over 4 =item unix_type Can be either SOCK_STREAM or SOCK_DGRAM (default is SOCK_STREAM). This can also be passed on the port line (see L). However, this method is deprecated. If you want SOCK_STREAM - just use proto UNIX without any other arguments. If you'd like SOCK_DGRAM, use the new proto UNIXDGRAM. =back =head1 METHODS =over 4 =item NS_unix_path/NS_unix_type In addition to the standard NS_ methods of Net::Server::Proto classes, the UNIX types also have legacy calls to NS_unix_path and NS_unix_type. Since version 2.000, NS_unix_path is simply an alias to NS_port. NS_unix_type is now redundant with NS_proto. These methods were missing between version 2.000 and 2.003 but have been returned as legacy bridges. =back =head1 QUICK PARAMETER LIST Key Value Default # deprecated UNIX socket parameters unix_type (SOCK_STREAM|SOCK_DGRAM) SOCK_STREAM port "filename" undef # more recent usage port "filename / UNIX" port "filename / UNIXDGRAM" =head1 LICENCE Distributed under the same terms as Net::Server =cut Net-Server-2.008/lib/Net/Server/Proto/SSLEAY.pm0000644000175000017500000004334712331755703017425 0ustar paulpaul# -*- perl -*- # # Net::Server::Proto::SSLEAY - Net::Server Protocol module # # $Id$ # # Copyright (C) 2010-2012 # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # # All rights reserved. # ################################################################ package Net::Server::Proto::SSLEAY; use strict; use warnings; use IO::Socket::INET; use Fcntl (); use Errno (); use Socket (); BEGIN { eval { require Net::SSLeay; 1 } or warn "Module Net::SSLeay is required for SSLeay."; for my $sub (qw(load_error_strings SSLeay_add_ssl_algorithms ENGINE_load_builtin_engines ENGINE_register_all_complete randomize)) { Net::SSLeay->can($sub)->(); } eval { [Fcntl::F_GETFL(), Fcntl::F_SETFL(), Fcntl::O_NONBLOCK()] } || die "Could not access Fcntl constant while loading ".__PACKAGE__.": $@"; } our @ISA = qw(IO::Socket::INET); our $AUTOLOAD; my @ssl_args = qw( SSL_use_cert SSL_verify_mode SSL_key_file SSL_cert_file SSL_ca_path SSL_ca_file SSL_cipher_list SSL_passwd_cb SSL_max_getline_length SSL_error_callback ); sub NS_proto { 'SSLEAY' } sub NS_port { my $sock = shift; ${*$sock}{'NS_port'} = shift if @_; return ${*$sock}{'NS_port'} } sub NS_host { my $sock = shift; ${*$sock}{'NS_host'} = shift if @_; return ${*$sock}{'NS_host'} } sub NS_ipv { my $sock = shift; ${*$sock}{'NS_ipv'} = shift if @_; return ${*$sock}{'NS_ipv'} } sub NS_listen { my $sock = shift; ${*$sock}{'NS_listen'} = shift if @_; return ${*$sock}{'NS_listen'} } sub object { my ($class, $info, $server) = @_; my $ssl = $server->{'server'}->{'ssl_args'} ||= do { my %temp = map {$_ => undef} @ssl_args; $server->configure({map {$_ => \$temp{$_}} @ssl_args}); \%temp; }; # we cannot do this at compile time because we have not yet read the configuration then @ISA = qw(IO::Socket::INET6) if $ISA[0] eq 'IO::Socket::INET' && Net::Server::Proto->requires_ipv6($server); my @sock = $class->SUPER::new(); foreach my $sock (@sock) { $sock->NS_host($info->{'host'}); $sock->NS_port($info->{'port'}); $sock->NS_ipv( $info->{'ipv'} ); $sock->NS_listen(defined($info->{'listen'}) ? $info->{'listen'} : defined($server->{'server'}->{'listen'}) ? $server->{'server'}->{'listen'} : Socket::SOMAXCONN()); ${*$sock}{'NS_orig_port'} = $info->{'orig_port'} if defined $info->{'orig_port'}; for my $key (@ssl_args) { my $val = defined($info->{$key}) ? $info->{$key} : defined($ssl->{$key}) ? $ssl->{$key} : $server->can($key) ? $server->$key($info->{'host'}, $info->{'port'}, 'SSLEAY') : undef; next if ! defined $val; $sock->$key($val) if defined $val; } } return wantarray ? @sock : $sock[0]; } sub log_connect { my ($sock, $server) = @_; $server->log(2, "Binding to ".$sock->NS_proto." port ".$sock->NS_port." on host ".$sock->NS_host." with IPv".$sock->NS_ipv); } sub connect { # connect the first time my ($sock, $server) = @_; my $host = $sock->NS_host; my $port = $sock->NS_port; my $ipv = $sock->NS_ipv; my $lstn = $sock->NS_listen; $sock->SUPER::configure({ LocalPort => $port, Proto => 'tcp', Listen => $lstn, ReuseAddr => 1, Reuse => 1, (($host ne '*') ? (LocalAddr => $host) : ()), # * is all ($sock->isa("IO::Socket::INET6") ? (Domain => ($ipv eq '6') ? Socket6::AF_INET6() : ($ipv eq '4') ? Socket::AF_INET() : Socket::AF_UNSPEC()) : ()), }) || $server->fatal("Can't connect to SSLEAY port $port on $host [$!]"); if ($port eq '0' and $port = $sock->sockport) { $server->log(2, " Bound to auto-assigned port $port"); ${*$sock}{'NS_orig_port'} = $sock->NS_port; $sock->NS_port($port); } elsif ($port =~ /\D/ and $port = $sock->sockport) { $server->log(2, " Bound to service port ".$sock->NS_port()."($port)"); ${*$sock}{'NS_orig_port'} = $sock->NS_port; $sock->NS_port($port); } $sock->bind_SSL($server); } sub reconnect { # connect on a sig -HUP my ($sock, $fd, $server, $port) = @_; $server->log(3,"Reassociating file descriptor $fd with ".$sock->NS_proto." on [".$sock->NS_host."]:".$sock->NS_port.", using IPv".$sock->NS_ipv); my $resp = $sock->fdopen( $fd, 'w' ) or $server->fatal("Error opening to file descriptor ($fd) [$!]"); if ($sock->isa("IO::Socket::INET6")) { my $ipv = $sock->NS_ipv; ${*$sock}{'io_socket_domain'} = ($ipv eq '6') ? Socket6::AF_INET6() : ($ipv eq '4') ? Socket::AF_INET() : Socket::AF_UNSPEC(); } $sock->bind_SSL($server); if ($port ne $sock->NS_port) { $server->log(2, " Re-bound to previously assigned port $port"); ${*$sock}{'NS_orig_port'} = $sock->NS_port; $sock->NS_port($port); } return $resp; } sub bind_SSL { my ($sock, $server) = @_; my $ctx = Net::SSLeay::CTX_new(); $sock->SSLeay_check_fatal("SSLeay bind_SSL CTX_new"); Net::SSLeay::CTX_set_options($ctx, Net::SSLeay::OP_ALL()); $sock->SSLeay_check_fatal("SSLeay bind_SSL CTX_set_options"); # 0x1: SSL_MODE_ENABLE_PARTIAL_WRITE # 0x10: SSL_MODE_RELEASE_BUFFERS (ignored before OpenSSL v1.0.0) Net::SSLeay::CTX_set_mode($ctx, 0x11); $sock->SSLeay_check_fatal("SSLeay bind_SSL CTX_set_mode"); # Load certificate. This will prompt for a password if necessary. my $file_key = $sock->SSL_key_file || die "SSLeay missing SSL_key_file on ".$sock->hup_string.".\n"; my $file_cert = $sock->SSL_cert_file || die "SSLeay missing SSL_cert_file on ".$sock->hup_string>".\n"; Net::SSLeay::CTX_use_RSAPrivateKey_file($ctx, $file_key, Net::SSLeay::FILETYPE_PEM()); $sock->SSLeay_check_fatal("SSLeay bind_SSL CTX_use_RSAPrivateKey_file"); Net::SSLeay::CTX_use_certificate_file( $ctx, $file_cert, Net::SSLeay::FILETYPE_PEM()); $sock->SSLeay_check_fatal("SSLeay bind_SSL CTX_use_certificate_file"); $sock->SSLeay_context($ctx); } sub close { my $sock = shift; if ($sock->SSLeay_is_client) { Net::SSLeay::free($sock->SSLeay); } else { Net::SSLeay::CTX_free($sock->SSLeay_context); } $sock->SSLeay_check_fatal("SSLeay close free"); return $sock->SUPER::close(@_); } sub accept { my ($sock, $class) = (@_); my ($client, $peername); if (wantarray) { ($client, $peername) = $sock->SUPER::accept($class); } else { $client = $sock->SUPER::accept($class); } if (defined $client) { $client->NS_proto($sock->NS_proto); $client->NS_ipv( $sock->NS_ipv); $client->NS_host( $sock->NS_host); $client->NS_port( $sock->NS_port); $client->SSLeay_context($sock->SSLeay_context); $client->SSLeay_is_client(1); } return wantarray ? ($client, $peername) : $client; } sub post_accept { my $client = shift; $client->SSLeay; } sub SSLeay { my $client = shift; if (! exists ${*$client}{'SSLeay'}) { die "SSLeay refusing to accept on non-client socket" if !$client->SSLeay_is_client; $client->autoflush(1); my $f = fcntl($client, Fcntl::F_GETFL(), 0) || die "SSLeay - fcntl get: $!\n"; fcntl($client, Fcntl::F_SETFL(), $f | Fcntl::O_NONBLOCK()) || die "SSLeay - fcntl set: $!\n"; my $ssl = Net::SSLeay::new($client->SSLeay_context); $client->SSLeay_check_fatal("SSLeay new"); Net::SSLeay::set_fd($ssl, $client->fileno); $client->SSLeay_check_fatal("SSLeay set_fd"); Net::SSLeay::accept($ssl); $client->SSLeay_check_fatal("SSLeay accept"); ${*$client}{'SSLeay'} = $ssl; } return if ! defined wantarray; return ${*$client}{'SSLeay'}; } sub SSLeay_check_fatal { my ($client, $msg) = @_; if (my $err = $client->SSLeay_check_error($msg, 1)) { my ($file, $pkg, $line) = caller; die "$msg at $file line $line\n ".join(' ', @$err); } } sub SSLeay_check_error { my ($client, $msg, $fatal) = @_; my @err; while (my $n = Net::SSLeay::ERR_get_error()) { push @err, "$n. ". Net::SSLeay::ERR_error_string($n) ."\n"; } if (@err) { my $cb = $client->SSL_error_callback; $cb->($client, $msg, \@err, ($fatal ? 'is_fatal' : ())) if $cb; return \@err; } return; } ###----------------------------------------------------------------### sub read_until { my ($client, $bytes, $end_qr, $non_greedy) = @_; my $ssl = $client->SSLeay; my $content = ${*$client}{'SSLeay_buffer'}; $content = '' if ! defined $content; my $ok = 0; # the rough outline for this loop came from http://devpit.org/wiki/OpenSSL_with_nonblocking_sockets_%28in_Perl%29 OUTER: while (1) { if (!length($content)) { } elsif (defined($bytes) && length($content) >= $bytes) { ${*$client}{'SSLeay_buffer'} = substr($content, $bytes, length($content), ''); $ok = 2; last; } elsif (defined($end_qr) && $content =~ m/$end_qr/g) { my $n = pos($content); ${*$client}{'SSLeay_buffer'} = substr($content, $n, length($content), ''); $ok = 1; last; } # this select appears to only cause read issues - in some cases the underlying select of Net::SSLeay enters into a spinloop #vec(my $vec = '', $client->fileno, 1) = 1; #select($vec, undef, undef, undef); my $n_empty = 0; while (1) { # 16384 is the maximum amount read() can return my $n = 16384; $n -= ($bytes - length($content)) if $non_greedy && ($bytes - length($content)) < $n; my $buf = Net::SSLeay::read($ssl, 16384); # read the most we can - continue reading until the buffer won't read any more if ($client->SSLeay_check_error('SSLeay read_until read')) { last OUTER; } die "SSLeay read_until: $!\n" if ! defined($buf) && !$!{EAGAIN} && !$!{EINTR} && !$!{ENOBUFS}; last if ! defined($buf); if (!length($buf)) { last OUTER if !length($buf) && $n_empty++; } else { $content .= $buf; if ($non_greedy && length($content) == $bytes) { $ok = 3; last; } } } } return wantarray ? ($ok, $content) : $content; } sub read { my ($client, $buf, $size, $offset) = @_; my ($ok, $read) = $client->read_until($size, undef, 1); substr($_[1], $offset || 0, defined($buf) ? length($buf) : 0, $read); return length $read; } sub sysread { my ($client, $buf, $length, $offset) = @_; $length = length $buf unless defined $length; $offset = 0 unless defined $offset; my $ssl = $client->SSLeay; my $data = Net::SSLeay::read($ssl, $length); return if $!{EAGAIN} || $!{EINTR}; die "SSLeay print: $!\n" unless defined $data; $length = length($data); $$buf = '' if !defined $buf; if ($offset > length($$buf)) { $$buf .= "\0" x ($offset - length($buf)); } substr($$buf, $offset, length($$buf), $data); return $length; } sub error { my $client = shift; return ${*$client}{'_error'} } sub syswrite { my ($client, $buf, $length, $offset) = @_; delete ${*$client}{'_error'}; $length = length $buf unless defined $length; $offset = 0 unless defined $offset; my $ssl = $client->SSLeay; my $write = Net::SSLeay::write_partial($ssl, $offset, $length, $buf); return if $!{EAGAIN} || $!{EINTR}; if ($write < 0) { ${*$client}{'_error'} = "SSLeay print: $!\n"; return; } return $write; } sub getline { my $client = shift; my ($ok, $line) = $client->read_until($client->SSL_max_getline_length, $/); return $line; } sub getlines { my $client = shift; my @lines; while (1) { my ($ok, $line) = $client->read_until($client->SSL_max_getline_length, $/); push @lines, $line; last if $ok != 1; } return @lines; } sub print { my $client = shift; delete ${*$client}{'_error'}; my $buf = @_ == 1 ? $_[0] : join('', @_); my $ssl = $client->SSLeay; while (length $buf) { vec(my $vec = '', $client->fileno, 1) = 1; select(undef, $vec, undef, undef); my $write = Net::SSLeay::write($ssl, $buf); return 0 if $client->SSLeay_check_error('SSLeay write'); if ($write == -1 && !$!{EAGAIN} && !$!{EINTR} && !$!{ENOBUFS}) { ${*$client}{'_error'} = "SSLeay print: $!\n"; return; } substr($buf, 0, $write, "") if $write > 0; } return 1; } sub printf { my $client = shift; $client->print(sprintf(shift, @_)); } sub say { my $client = shift; $client->print(@_, "\n"); } sub write { my $client = shift; my $buf = shift; $buf = substr($buf, $_[1] || 0, $_[0]) if @_; $client->print($buf); } sub seek { my $client = shift; my ($pos, $whence) = @_; if ($whence) { $! = "Seek from $whence of non-zero is not supported."; return 0; } my $n = $client->read(my $buf, $pos); if ($n != $pos) { $| = "Couldn't seek to $pos ($n)\n"; return 0; } return 1; } sub poll_cb { # implemented for psgi compatibility - TODO - should poll appropriately for Multipex my ($self, $cb) = @_; return $cb->($self); } ###----------------------------------------------------------------### sub hup_string { my $sock = shift; return join "|", $sock->NS_host, $sock->NS_port, $sock->NS_proto, "ipv".$sock->NS_ipv, (defined(${*$sock}{'NS_orig_port'}) ? ${*$sock}{'NS_orig_port'} : ()); } sub show { my $sock = shift; my $t = "Ref = \"".ref($sock). "\" (".$sock->hup_string.")\n"; foreach my $prop (qw(SSLeay_context SSLeay_is_client)) { $t .= " $prop = \"" .$sock->$prop()."\"\n"; } return $t; } sub AUTOLOAD { my $sock = shift; my $prop = $AUTOLOAD =~ /::([^:]+)$/ ? $1 : die "Missing property in AUTOLOAD."; die "Unknown method or property [$prop]" if $prop !~ /^(SSLeay_context|SSLeay_is_client|SSL_\w+)$/; no strict 'refs'; *{__PACKAGE__."::${prop}"} = sub { my $sock = shift; if (@_) { ${*$sock}{$prop} = shift; return delete ${*$sock}{$prop} if ! defined ${*$sock}{$prop}; } else { return ${*$sock}{$prop}; } }; return $sock->$prop(@_); } sub tie_stdout { 1 } 1; __END__ =head1 NAME Net::Server::Proto::SSLEAY - Custom Net::Server SSL protocol handler based on Net::SSLeay. =head1 SYNOPSIS See L. use base qw(Net::Server::HTTP); main->run( proto => 'ssleay', SSL_key_file => "/path/to/my/file.key", SSL_cert_file => "/path/to/my/file.crt", ); # OR sub SSL_key_file { "/path/to/my/file.key" } sub SSL_cert_file { "/path/to/my/file.crt" } main->run(proto => 'ssleay'); # OR main->run( port => [443, 8443, "80/tcp"], # bind to two ssleay ports and one tcp proto => "ssleay", # use ssleay as the default ipv => "*", # bind both IPv4 and IPv6 interfaces SSL_key_file => "/path/to/my/file.key", SSL_cert_file => "/path/to/my/file.crt", ); # OR main->run(port => [{ port => "443", proto => "ssleay", # ipv => 4, # default - only do IPv4 SSL_key_file => "/path/to/my/file.key", SSL_cert_file => "/path/to/my/file.crt", }, { port => "8443", proto => "ssleay", ipv => "*", # IPv4 and IPv6 SSL_key_file => "/path/to/my/file2.key", # separate key SSL_cert_file => "/path/to/my/file2.crt", # separate cert }]); =head1 DESCRIPTION This module has reliably been used in situations receiving millions of hits on a single box per day. If anybody has any successes or ideas for improvment under SSLEAY, please email . Protocol module for Net::Server. This module implements a secure socket layer over tcp (also known as SSL). See L. If you need more customization of the SSL layer, you may want to investigate using SSL rather than SSLEAY as it uses the venerable(ish) IO::Socket::SSL. =head1 PARAMETERS Currently there is support for the following: =over 4 =item C Full path to the certificate file to be used for this server. Should be in PEM format. =item C Full path to the key file to be used for this server. Should be in PEM format. =item C Used during getline to only read until this many bytes are found. Default is undef which means unlimited. =item C Should be a code ref that will be called whenever error conditions are encountered. It passes a source message and an arrayref of the errors. =back =head1 METHODS This module implements most of the common file handle operations. There are some additions though: =over 4 =item C Takes bytes and match qr. If bytes is defined - it will read until that many bytes are found. If match qr is defined, it will read until the buffer matches that qr. If both are undefined, it will read until there is nothing left to read. =item C If an error occurred while writing, this method will return that error. =back =head1 BUGS There are probably many. =head1 LICENCE Distributed under the same terms as Net::Server =head1 THANKS Thanks to Bilbo at http://devpit.org/wiki/OpenSSL_with_nonblocking_sockets_%28in_Perl%29 for documenting a more reliable way of accepting and reading SSL connections. =cut Net-Server-2.008/lib/Net/Server/Proto/UNIXDGRAM.pm0000644000175000017500000000645412331755703017761 0ustar paulpaul# -*- perl -*- # # Net::Server::Proto::UNIXDGRAM - Net::Server Protocol module # # $Id$ # # Copyright (C) 2001-2012 # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # # All rights reserved. # ################################################################ package Net::Server::Proto::UNIXDGRAM; use strict; use base qw(Net::Server::Proto::UNIX); use Socket qw(SOCK_DGRAM); my @udp_args = qw( udp_recv_len udp_recv_flags udp_broadcast ); # we do broadcast just for cacheing parallelism with UDP.pm sub NS_proto { 'UNIXDGRAM' } sub NS_recv_len { my $sock = shift; ${*$sock}{'NS_recv_len'} = shift if @_; return ${*$sock}{'NS_recv_len'} } sub NS_recv_flags { my $sock = shift; ${*$sock}{'NS_recv_flags'} = shift if @_; return ${*$sock}{'NS_recv_flags'} } sub NS_unix_type { 'SOCK_DGRAM' } sub object { my ($class, $info, $server) = @_; my $udp = $server->{'server'}->{'udp_args'} ||= do { my %temp = map {$_ => undef} @udp_args; $server->configure({map {$_ => \$temp{$_}} @udp_args}); \%temp; }; my $len = defined($info->{'udp_recv_len'}) ? $info->{'udp_recv_len'} : defined($udp->{'udp_recv_len'}) ? $udp->{'udp_recv_len'} : 4096; $len = ($len =~ /^(\d+)$/) ? $1 : 4096; my $flg = defined($info->{'udp_recv_flags'}) ? $info->{'udp_recv_flags'} : defined($udp->{'udp_recv_flags'}) ? $udp->{'udp_recv_flags'} : 0; $flg = ($flg =~ /^(\d+)$/) ? $1 : 0; my $sock = $class->SUPER::new(); my $port = $info->{'port'} =~ m{^ ([\w\.\-\*\/]+) $ }x ? $1 : $server->fatal("Insecure filename"); $sock->NS_port($port); $sock->NS_recv_len($len); $sock->NS_recv_flags($flg); return $sock; } sub connect { my ($sock, $server) = @_; my $path = $sock->NS_port; $server->fatal("Can't connect to UNIXDGRAM socket at file $path [$!]") if -e $path && ! unlink $path; $sock->SUPER::configure({ Local => $path, Type => SOCK_DGRAM, }) or $server->fatal("Can't connect to UNIXDGRAM socket at file $path [$!]"); } 1; __END__ =head1 NAME Net::Server::Proto::UNIXDGRAM - Net::Server UNIXDGRAM protocol. =head1 SYNOPSIS See L. =head1 DESCRIPTION Protocol module for Net::Server. This module implements the UNIX SOCK_DGRAM socket type. See L. Any sockets created during startup will be chown'ed to the user and group specified in the starup arguments. =head1 PARAMETERS The following paramaters may be specified in addition to normal command line parameters for a Net::Server. See L for more information on reading arguments. =over 4 =item udp_recv_len Specifies the number of bytes to read from the SOCK_DGRAM connection handle. Data will be read into $self->{'server'}->{'udp_data'}. Default is 4096. See L and L. =item udp_recv_flags See L. Default is 0. =back =head1 QUICK PARAMETER LIST Key Value Default ## UNIXDGRAM socket parameters udp_recv_len \d+ 4096 udp_recv_flags \d+ 0 =head1 LICENCE Distributed under the same terms as Net::Server =cut Net-Server-2.008/lib/Net/Server/Proto/SSL.pm0000644000175000017500000002541412331755703017061 0ustar paulpaul# -*- perl -*- # # Net::Server::Proto::SSL - Net::Server Protocol module # # $Id$ # # Copyright (C) 2001-2012 # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # # All rights reserved. # ################################################################ package Net::Server::Proto::SSL; use strict; use warnings; BEGIN { # IO::Socket::SSL will automatically become IO::Socket::INET6 if it is available. # This is different from Net::Server::Proto::SSLEAY that only does it if IPv6 is requested. if (! eval { require IO::Socket::SSL }) { die "Module IO::Socket::SSL is required for SSL - you may alternately try SSLEAY. $@"; } } our @ISA = qw(IO::Socket::SSL); our $AUTOLOAD; my @ssl_args = qw( SSL_use_cert SSL_verify_mode SSL_key_file SSL_cert_file SSL_ca_path SSL_ca_file SSL_cipher_list SSL_passwd_cb SSL_max_getline_length SSL_error_callback ); sub NS_proto { 'SSL' } sub NS_port { my $sock = shift; ${*$sock}{'NS_port'} = shift if @_; return ${*$sock}{'NS_port'} } sub NS_host { my $sock = shift; ${*$sock}{'NS_host'} = shift if @_; return ${*$sock}{'NS_host'} } sub NS_ipv { my $sock = shift; ${*$sock}{'NS_ipv'} = shift if @_; return ${*$sock}{'NS_ipv'} } sub NS_listen { my $sock = shift; ${*$sock}{'NS_listen'} = shift if @_; return ${*$sock}{'NS_listen'} } sub object { my ($class, $info, $server) = @_; my $ssl = $server->{'server'}->{'ssl_args'} ||= do { my %temp = map {$_ => undef} @ssl_args; $server->configure({map {$_ => \$temp{$_}} @ssl_args}); \%temp; }; my @sock = $class->SUPER::new(); foreach my $sock (@sock) { $sock->NS_host($info->{'host'}); $sock->NS_port($info->{'port'}); $sock->NS_ipv( $info->{'ipv'} ); $sock->NS_listen(defined($info->{'listen'}) ? $info->{'listen'} : defined($server->{'server'}->{'listen'}) ? $server->{'server'}->{'listen'} : Socket::SOMAXCONN()); ${*$sock}{'NS_orig_port'} = $info->{'orig_port'} if defined $info->{'orig_port'}; my %seen; for my $key (grep {!$seen{$_}++} (@ssl_args, sort grep {/^SSL_/} keys %$info)) { # allow for any SSL_ arg to get passed in via my $val = defined($info->{$key}) ? $info->{$key} : defined($ssl->{$key}) ? $ssl->{$key} : $server->can($key) ? $server->$key($info->{'host'}, $info->{'port'}, 'SSL') : undef; next if ! defined $val; $sock->$key($val) if defined $val; } } return wantarray ? @sock : $sock[0]; } sub log_connect { my ($sock, $server) = @_; $server->log(2, "Binding to ".$sock->NS_proto." port ".$sock->NS_port." on host ".$sock->NS_host." with IPv".($sock->NS_ipv)); } sub connect { my ($sock, $server) = @_; my $host = $sock->NS_host; my $port = $sock->NS_port; my $ipv = $sock->NS_ipv; my $lstn = $sock->NS_listen; $sock->SUPER::configure({ LocalPort => $port, Proto => 'tcp', Listen => $lstn, ReuseAddr => 1, Reuse => 1, (($host ne '*') ? (LocalAddr => $host) : ()), # * is all ($sock->isa('IO::Socket::INET6') ? (Domain => ($ipv eq '6') ? Socket6::AF_INET6() : ($ipv eq '4') ? Socket::AF_INET() : Socket::AF_UNSPEC()) : ()), (map {$_ => $sock->$_();} grep {/^SSL_/} keys %{*$sock}), SSL_server => 1, }) or $server->fatal("Cannot connect to SSL port $port on $host [$!]"); if ($port eq '0' and $port = $sock->sockport) { $server->log(2, " Bound to auto-assigned port $port"); ${*$sock}{'NS_orig_port'} = $sock->NS_port; $sock->NS_port($port); } elsif ($port =~ /\D/ and $port = $sock->sockport) { $server->log(2, " Bound to service port ".$sock->NS_port()."($port)"); ${*$sock}{'NS_orig_port'} = $sock->NS_port; $sock->NS_port($port); } } sub reconnect { # after a sig HUP my ($sock, $fd, $server, $port) = @_; $server->log(3,"Reassociating file descriptor $fd with ".$sock->NS_proto." on [".$sock->NS_host."]:".$sock->NS_port.", using IPv".$sock->NS_ipv); $sock->configure_SSL({ (map {$_ => $sock->$_();} grep {/^SSL_/} keys %{*$sock}), SSL_server => 1, }); $sock->IO::Socket::INET::fdopen($fd, 'w') or $server->fatal("Error opening to file descriptor ($fd) [$!]"); if ($sock->isa("IO::Socket::INET6")) { my $ipv = $sock->NS_ipv; ${*$sock}{'io_socket_domain'} = ($ipv eq '6') ? Socket6::AF_INET6() : ($ipv eq '4') ? Socket::AF_INET() : Socket::AF_UNSPEC(); } if ($port ne $sock->NS_port) { $server->log(2, " Re-bound to previously assigned port $port"); ${*$sock}{'NS_orig_port'} = $sock->NS_port; $sock->NS_port($port); } } sub accept { my ($sock, $class) = @_; my ($client, $peername); my $code = $sock->isa('IO::Socket::INET6') ? 'IO::Socket::INET6'->can('accept') : 'IO::Socket::INET'->can('accept'); # TODO - cache this lookup if (wantarray) { ($client, $peername) = $code->($sock, $class || ref($sock)); } else { $client = $code->($sock, $class || ref($sock)); } ${*$client}{'_parent_sock'} = $sock; if (defined $client) { $client->NS_proto($sock->NS_proto); $client->NS_ipv( $sock->NS_ipv); $client->NS_host( $sock->NS_host); $client->NS_port( $sock->NS_port); } return wantarray ? ($client, $peername) : $client; } sub hup_string { my $sock = shift; return join "|", $sock->NS_host, $sock->NS_port, $sock->NS_proto, 'ipv'.$sock->NS_ipv, (defined(${*$sock}{'NS_orig_port'}) ? ${*$sock}{'NS_orig_port'} : ()); } sub show { my $sock = shift; my $t = "Ref = \"".ref($sock). "\" (".$sock->hup_string.")\n"; foreach my $prop (qw(SSLeay_context SSLeay_is_client)) { $t .= " $prop = \"" .$sock->$prop()."\"\n"; } return $t; } sub AUTOLOAD { my $sock = shift; my $prop = $AUTOLOAD =~ /::([^:]+)$/ ? $1 : die "Missing property in AUTOLOAD."; die "Unknown method or property [$prop]" if $prop !~ /^(SSL_\w+)$/; no strict 'refs'; *{__PACKAGE__."::${prop}"} = sub { my $sock = shift; if (@_) { ${*$sock}{$prop} = shift; return delete ${*$sock}{$prop} if ! defined ${*$sock}{$prop}; } else { return ${*$sock}{$prop}; } }; return $sock->$prop(@_); } sub tie_stdout { 1 } sub post_accept { my $client = shift; $client->_accept_ssl if !${*$client}{'_accept_ssl'}; } sub _accept_ssl { my $client = shift; ${*$client}{'_accept_ssl'} = 1; my $sock = delete(${*$client}{'_parent_sock'}) || die "Could not get handshake from accept\n"; $sock->accept_SSL($client) || die "Could not finalize SSL connection with client handle ($@)\n"; } sub read_until { # allow for an interface that can be tied to STDOUT my ($client, $bytes, $end_qr) = @_; die "One of bytes or end_qr should be defined for TCP read_until\n" if !defined($bytes) && !defined($end_qr); $client->_accept_ssl if !${*$client}{'_accept_ssl'}; my $content = ''; my $ok = 0; while (1) { $client->read($content, 1, length($content)); if (defined($bytes) && length($content) >= $bytes) { $ok = 2; last; } elsif (defined($end_qr) && $content =~ $end_qr) { $ok = 1; last; } } return wantarray ? ($ok, $content) : $content; } 1; =head1 NAME Net::Server::Proto::SSL - Net::Server SSL protocol. =head1 SYNOPSIS Until this release, it was preferrable to use the Net::Server::Proto::SSLEAY module. Recent versions include code that overcomes original limitations. See L. See L. use base qw(Net::Server::HTTP); main->run( proto => 'ssl', SSL_key_file => "/path/to/my/file.key", SSL_cert_file => "/path/to/my/file.crt", ); # OR sub SSL_key_file { "/path/to/my/file.key" } sub SSL_cert_file { "/path/to/my/file.crt" } main->run(proto = 'ssl'); # OR main->run( port => [443, 8443, "80/tcp"], # bind to two ssl ports and one tcp proto => "ssl", # use ssl as the default ipv => "*", # bind both IPv4 and IPv6 interfaces SSL_key_file => "/path/to/my/file.key", SSL_cert_file => "/path/to/my/file.crt", ); # OR main->run(port => [{ port => "443", proto => "ssl", # ipv => 4, # default - only do IPv4 SSL_key_file => "/path/to/my/file.key", SSL_cert_file => "/path/to/my/file.crt", }, { port => "8443", proto => "ssl", ipv => "*", # IPv4 and IPv6 SSL_key_file => "/path/to/my/file2.key", # separate key SSL_cert_file => "/path/to/my/file2.crt", # separate cert SSL_foo => 1, # Any key prefixed with SSL_ passed as a port hashref # key/value will automatically be passed to IO::Socket::SSL }]); =head1 DESCRIPTION Protocol module for Net::Server based on IO::Socket::SSL. This module implements a secure socket layer over tcp (also known as SSL) via the IO::Socket::SSL module. If this module does not work in your situation, please also consider using the SSLEAY protocol (Net::Server::Proto::SSLEAY) which interfaces directly with Net::SSLeay. See L. If you know that your server will only need IPv4 (which is the default for Net::Server), you can load IO::Socket::SSL in inet4 mode which will prevent it from using Socket6 and IO::Socket::INET6 since they would represent additional and unsued overhead. use IO::Socket::SSL qw(inet4); use base qw(Net::Server::Fork); __PACKAGE__->run(proto => "ssl"); =head1 PARAMETERS In addition to the normal Net::Server parameters, any of the SSL parameters from IO::Socket::SSL may also be specified. See L for information on setting this up. All arguments prefixed with SSL_ will be passed to the IO::Socket::SSL->configure method. =head1 BUGS Until version Net::Server version 2, Net::Server::Proto::SSL used the default IO::Socket::SSL::accept method. This old approach introduces a DDOS vulnerability into the server, where the socket is accepted, but the parent server then has to block until the client negotiates the SSL connection. This has now been overcome by overriding the accept method and accepting the SSL negotiation after the parent socket has had the chance to go back to listening. =head1 LICENCE Distributed under the same terms as Net::Server =head1 THANKS Thanks to Vadim for pointing out the IO::Socket::SSL accept was returning objects blessed into the wrong class. =cut Net-Server-2.008/lib/Net/Server/Proto/UDP.pm0000644000175000017500000001176412331755703017053 0ustar paulpaul# -*- perl -*- # # Net::Server::Proto::UDP - Net::Server Protocol module # # $Id$ # # Copyright (C) 2001-2012 # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # Modified 2005 by Timothy Watt # Added ability to deal with broadcast packets. # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # # All rights reserved. # ################################################################ package Net::Server::Proto::UDP; use strict; use base qw(Net::Server::Proto::TCP); my @udp_args = qw( udp_recv_len udp_recv_flags udp_broadcast ); sub NS_proto { 'UDP' } sub NS_recv_len { my $sock = shift; ${*$sock}{'NS_recv_len'} = shift if @_; return ${*$sock}{'NS_recv_len'} } sub NS_recv_flags { my $sock = shift; ${*$sock}{'NS_recv_flags'} = shift if @_; return ${*$sock}{'NS_recv_flags'} } sub NS_broadcast { my $sock = shift; ${*$sock}{'NS_broadcast'} = shift if @_; return ${*$sock}{'NS_broadcast'} } sub object { my ($class, $info, $server) = @_; # we cannot do this at compile time because we have not yet read the configuration then # (this is the height of rudeness changing another's class on their behalf) @Net::Server::Proto::TCP::ISA = qw(IO::Socket::INET6) if $Net::Server::Proto::TCP::ISA[0] eq 'IO::Socket::INET' && Net::Server::Proto->requires_ipv6($server); my $udp = $server->{'server'}->{'udp_args'} ||= do { my %temp = map {$_ => undef} @udp_args; $server->configure({map {$_ => \$temp{$_}} @udp_args}); \%temp; }; my $len = defined($info->{'udp_recv_len'}) ? $info->{'udp_recv_len'} : defined($udp->{'udp_recv_len'}) ? $udp->{'udp_recv_len'} : 4096; $len = ($len =~ /^(\d+)$/) ? $1 : 4096; my $flg = defined($info->{'udp_recv_flags'}) ? $info->{'udp_recv_flags'} : defined($udp->{'udp_recv_flags'}) ? $udp->{'udp_recv_flags'} : 0; $flg = ($flg =~ /^(\d+)$/) ? $1 : 0; my @sock = $class->SUPER::new(); # it is possible that multiple connections will be returned if INET6 is in effect foreach my $sock (@sock) { $sock->NS_host($info->{'host'}); $sock->NS_port($info->{'port'}); $sock->NS_ipv( $info->{'ipv'} ); $sock->NS_recv_len($len); $sock->NS_recv_flags($flg); $sock->NS_broadcast(exists($info->{'udp_broadcast'}) ? $info->{'udp_broadcast'} : $udp->{'upd_broadcast'}); ${*$sock}{'NS_orig_port'} = $info->{'orig_port'} if defined $info->{'orig_port'}; } return wantarray ? @sock : $sock[0]; } sub connect { my ($sock, $server) = @_; my $host = $sock->NS_host; my $port = $sock->NS_port; my $ipv = $sock->NS_ipv; $sock->SUPER::configure({ LocalPort => $port, Proto => 'udp', ReuseAddr => 1, Reuse => 1, # may not be needed on UDP (($host ne '*') ? (LocalAddr => $host) : ()), # * is all ($sock->isa("IO::Socket::INET6") ? (Domain => ($ipv eq '6') ? Socket6::AF_INET6() : ($ipv eq '4') ? Socket::AF_INET() : Socket::AF_UNSPEC()) : ()), ($sock->NS_broadcast ? (Broadcast => 1) : ()), }) or $server->fatal("Cannot bind to UDP port $port on $host [$!]"); if ($port eq 0 and $port = $sock->sockport) { $server->log(2, " Bound to auto-assigned port $port"); ${*$sock}{'NS_orig_port'} = $sock->NS_port; $sock->NS_port($port); } elsif ($port =~ /\D/ and $port = $sock->sockport) { $server->log(2, " Bound to service port ".$sock->NS_port()."($port)"); ${*$sock}{'NS_orig_port'} = $sock->NS_port; $sock->NS_port($port); } } 1; __END__ =head1 NAME Net::Server::Proto::UDP - Net::Server UDP protocol. =head1 SYNOPSIS See L. =head1 DESCRIPTION Protocol module for Net::Server. This module implements the SOCK_DGRAM socket type under INET (also known as UDP). See L. =head1 PARAMETERS The following paramaters may be specified in addition to normal command line parameters for a Net::Server. See L for more information on reading arguments. =over 4 =item udp_recv_len Specifies the number of bytes to read from the UDP connection handle. Data will be read into $self->{'server'}->{'udp_data'}. Default is 4096. See L and L. =item udp_recv_flags See L. Default is 0. =item udp_broadcast Default is undef. =back =head1 QUICK PARAMETER LIST Key Value Default ## UDP protocol parameters udp_recv_len \d+ 4096 udp_recv_flags \d+ 0 udp_broadcast bool undef =head1 INTERNAL METHODS =over 4 =item C Returns an object with parameters suitable for eventual creation of a IO::Socket::INET object listining on UDP. =item C Called when actually binding the port. Handles default parameters before calling parent method. =back =head1 LICENCE Distributed under the same terms as Net::Server =cut Net-Server-2.008/lib/Net/Server/Proto/TCP.pm0000644000175000017500000001570712331755703017052 0ustar paulpaul# -*- perl -*- # # Net::Server::Proto::TCP - Net::Server Protocol module # # $Id$ # # Copyright (C) 2001-2012 # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # # All rights reserved. # ################################################################ package Net::Server::Proto::TCP; use strict; use warnings; use IO::Socket::INET; use Net::Server::Proto; our @ISA = qw(IO::Socket::INET); # we may dynamically change this to INET6 based upon our server configuration sub NS_proto { 'TCP' } sub NS_port { my $sock = shift; ${*$sock}{'NS_port'} = shift if @_; return ${*$sock}{'NS_port'} } sub NS_host { my $sock = shift; ${*$sock}{'NS_host'} = shift if @_; return ${*$sock}{'NS_host'} } sub NS_ipv { my $sock = shift; ${*$sock}{'NS_ipv'} = shift if @_; return ${*$sock}{'NS_ipv'} } sub NS_listen { my $sock = shift; ${*$sock}{'NS_listen'} = shift if @_; return ${*$sock}{'NS_listen'} } sub object { my ($class, $info, $server) = @_; # we cannot do this at compile time because we have not yet read the configuration then @ISA = qw(IO::Socket::INET6) if $ISA[0] eq 'IO::Socket::INET' && Net::Server::Proto->requires_ipv6($server); my @sock = $class->SUPER::new(); foreach my $sock (@sock) { $sock->NS_host($info->{'host'}); $sock->NS_port($info->{'port'}); $sock->NS_ipv( $info->{'ipv'} ); $sock->NS_listen(defined($info->{'listen'}) ? $info->{'listen'} : defined($server->{'server'}->{'listen'}) ? $server->{'server'}->{'listen'} : Socket::SOMAXCONN()); ${*$sock}{'NS_orig_port'} = $info->{'orig_port'} if defined $info->{'orig_port'}; } return wantarray ? @sock : $sock[0]; } sub log_connect { my ($sock, $server) = @_; $server->log(2, "Binding to ".$sock->NS_proto." port ".$sock->NS_port." on host ".$sock->NS_host." with IPv".$sock->NS_ipv); } sub connect { my ($sock, $server) = @_; my $host = $sock->NS_host; my $port = $sock->NS_port; my $ipv = $sock->NS_ipv; my $lstn = $sock->NS_listen; $sock->SUPER::configure({ LocalPort => $port, Proto => 'tcp', Listen => $lstn, ReuseAddr => 1, Reuse => 1, (($host ne '*') ? (LocalAddr => $host) : ()), # * is all ($sock->isa("IO::Socket::INET6") ? (Domain => ($ipv eq '6') ? Socket6::AF_INET6() : ($ipv eq '4') ? Socket::AF_INET() : Socket::AF_UNSPEC()) : ()), }) || $server->fatal("Can't connect to TCP port $port on $host [$!]"); if ($port eq '0' and $port = $sock->sockport) { $server->log(2, " Bound to auto-assigned port $port"); ${*$sock}{'NS_orig_port'} = $sock->NS_port; $sock->NS_port($port); } elsif ($port =~ /\D/ and $port = $sock->sockport) { $server->log(2, " Bound to service port ".$sock->NS_port()."($port)"); ${*$sock}{'NS_orig_port'} = $sock->NS_port; $sock->NS_port($port); } } sub reconnect { # after a sig HUP my ($sock, $fd, $server, $port) = @_; $server->log(3,"Reassociating file descriptor $fd with ".$sock->NS_proto." on [".$sock->NS_host."]:".$sock->NS_port.", using IPv".$sock->NS_ipv); $sock->fdopen($fd, 'w') or $server->fatal("Error opening to file descriptor ($fd) [$!]"); if ($sock->isa("IO::Socket::INET6")) { my $ipv = $sock->NS_ipv; ${*$sock}{'io_socket_domain'} = ($ipv eq '6') ? Socket6::AF_INET6() : ($ipv eq '4') ? Socket::AF_INET() : Socket::AF_UNSPEC(); } if ($port ne $sock->NS_port) { $server->log(2, " Re-bound to previously assigned port $port"); ${*$sock}{'NS_orig_port'} = $sock->NS_port; $sock->NS_port($port); } } sub accept { my ($sock, $class) = (@_); my ($client, $peername); if (wantarray) { ($client, $peername) = $sock->SUPER::accept($class); } else { $client = $sock->SUPER::accept($class); } if (defined $client) { $client->NS_port($sock->NS_port); } return wantarray ? ($client, $peername) : $client; } sub poll_cb { # implemented for psgi compatibility - TODO - should poll appropriately for Multipex my ($self, $cb) = @_; return $cb->($self); } ###----------------------------------------------------------------### sub read_until { # only sips the data - but it allows for compatibility with SSLEAY my ($client, $bytes, $end_qr) = @_; die "One of bytes or end_qr should be defined for TCP read_until\n" if !defined($bytes) && !defined($end_qr); my $content = ''; my $ok = 0; while (1) { $client->read($content, 1, length($content)); if (defined($bytes) && length($content) >= $bytes) { $ok = 2; last; } elsif (defined($end_qr) && $content =~ $end_qr) { $ok = 1; last; } } return wantarray ? ($ok, $content) : $content; } ###----------------------------------------------------------------### ### a string containing any information necessary for restarting the server ### via a -HUP signal ### a newline is not allowed ### the hup_string must be a unique identifier based on configuration info sub hup_string { my $sock = shift; return join "|", $sock->NS_host, $sock->NS_port, $sock->NS_proto, 'ipv'.$sock->NS_ipv, (defined(${*$sock}{'NS_orig_port'}) ? ${*$sock}{'NS_orig_port'} : ()); } sub show { my $sock = shift; return "Ref = \"".ref($sock). "\" (".$sock->hup_string.")\n"; } 1; __END__ =head1 NAME Net::Server::Proto::TCP - Net::Server TCP protocol. =head1 SYNOPSIS See L. =head1 DESCRIPTION Protocol module for Net::Server. This module implements the SOCK_STREAM socket type under INET (also known as TCP). See L. =head1 PARAMETERS There are no additional parameters that can be specified. See L for more information on reading arguments. =head1 INTERNAL METHODS =over 4 =item C Returns an object with parameters suitable for eventual creation of a IO::Socket::INET object listining on UDP. =item C Called before binding the socket to provide useful information to the logs. =item C Called when actually binding the port. Handles default parameters before calling parent method. =item C Called instead of connect method during a server hup. =item C Override of the parent class to make sure necessary parameters are passed down to client sockets. =item C Allow for psgi compatible interface during HTTP server. =item C Takes a regular expression, reads from the socket until the regular expression is matched. =item C Returns a unique identifier that can be passed to the re-exec'ed process during HUP. =item C Basic dumper of properties stored in the glob. =item C Handle accessor methods. =back =head1 LICENCE Distributed under the same terms as Net::Server =cut Net-Server-2.008/lib/Net/Server/PSGI.pm0000644000175000017500000002333612333210255016046 0ustar paulpaul# -*- perl -*- # # Net::Server::PSGI - Extensible Perl HTTP PSGI base server # # $Id$ # # Copyright (C) 2011-2012 # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # ################################################################ package Net::Server::PSGI; use strict; use base qw(Net::Server::HTTP); sub net_server_type { __PACKAGE__ } sub options { my $self = shift; my $ref = $self->SUPER::options(@_); my $prop = $self->{'server'}; $ref->{$_} = \$prop->{$_} for qw(app); return $ref; } sub post_configure { my $self = shift; my $prop = $self->{'server'}; $prop->{'log_handle'} = IO::Handle->new; $prop->{'log_handle'}->fdopen(fileno(STDERR), "w"); $prop->{'no_client_stdout'} = 1; $self->SUPER::post_configure(@_); } sub _tie_client_stdout {} # the client should not print directly sub process_request { my $self = shift; local $SIG{'ALRM'} = sub { die "Server Timeout\n" }; my $ok = eval { alarm($self->timeout_header); $self->process_headers; alarm($self->timeout_idle); my $env = \%ENV; $env->{'psgi.version'} = [1, 0]; $env->{'psgi.url_scheme'} = ($ENV{'HTTPS'} && $ENV{'HTTPS'} eq 'on') ? 'https' : 'http'; $env->{'psgi.input'} = $self->{'server'}->{'client'}; $env->{'psgi.errors'} = $self->{'server'}->{'log_handle'}; $env->{'psgi.multithread'} = 1; $env->{'psgi.multiprocess'} = 1; $env->{'psgi.nonblocking'} = 1; # need to make this false if we aren't of a forking type server $env->{'psgi.streaming'} = 1; local %ENV; $self->process_psgi_request($env); alarm(0); 1; }; alarm(0); if (! $ok) { my $err = "$@" || "Something happened"; $self->send_500($err); die $err; } } sub process_psgi_request { my ($self, $env) = @_; my $app = $self->find_psgi_handler($env); my $resp = $app->($env); return $resp->(sub { my $resp = shift; $self->print_psgi_headers($resp->[0], $resp->[1]); return $self->{'server'}->{'client'} if @$resp == 2; return $self->print_psgi_body($resp->[2]); }) if ref($resp) eq 'CODE'; $self->print_psgi_headers($resp->[0], $resp->[1]); $self->print_psgi_body($resp->[2]); } sub find_psgi_handler { shift->app || \&psgi_echo_handler } sub app { my $self = shift; $self->{'server'}->{'app'} = shift if @_; my $app = $self->{'server'}->{'app'}; if (!ref($app) && $app) { $app = $self->{'server'}->{'app'} = eval { require CGI::Compile; CGI::Compile->compile($app) } || die "Failed to compile app with CGI::Compile"; } return $app; } sub print_psgi_headers { my ($self, $status, $headers) = @_; $self->send_status($status); my $request_info = $self->{'request_info'}; my $out = ''; for my $i (0 .. @{ $headers || [] } / 2 - 1) { my $key = "\u\L$headers->[$i*2]"; my $val = $headers->[$i*2 + 1]; $key =~ y/_/-/; $out .= "$key: $val\015\012"; push @{ $request_info->{'response_headers'} }, [$key, $val]; } $out .= "\015\012"; $request_info->{'response_header_size'} += length $out; $self->{'server'}->{'client'}->print($out); $request_info->{'headers_sent'} = 1; } sub print_psgi_body { my ($self, $body) = @_; my $client = $self->{'server'}->{'client'}; my $request_info = $self->{'request_info'}; if (ref $body eq 'ARRAY') { for my $chunk (@$body) { $client->print($chunk); $request_info->{'response_size'} += length $chunk; } } elsif (blessed($body) && $body->can('getline')) { while (defined(my $chunk = $body->getline)) { $client->print($chunk); $request_info->{'response_size'} += length $chunk; } } else { while (defined(my $chunk = <$body>)) { $client->print($chunk); $request_info->{'response_size'} += length $chunk; } } } sub psgi_echo_handler { my $env = shift; my $txt = qq{
\n}; if (eval { require Data::Dumper }) { local $Data::Dumper::Sortkeys = 1; my $form = {}; if (eval { require CGI::PSGI }) { my $q = CGI::PSGI->new($env); $form->{$_} = $q->param($_) for $q->param; } $txt .= "
".Data::Dumper->Dump([$env, $form], ['env', 'form'])."
"; } return [200, ['Content-type', 'text/html'], [$txt]]; } sub exec_cgi { die "Not implemented" } sub exec_trusted_perl { die "Not implemented" } 1; __END__ =head1 NAME Net::Server::PSGI - basic Net::Server based PSGI HTTP server class =head1 TEST ONE LINER perl -e 'use base qw(Net::Server::PSGI); main->run(port => 8080, ipv => "*")' # runs a default echo server =head1 SYNOPSIS use base qw(Net::Server::PSGI); __PACKAGE__->run(app => \&my_echo_handler); # will bind IPv4 port 80 sub my_echo_handler { my $env = shift; my $txt = qq{
\n}; require Data::Dumper; local $Data::Dumper::Sortkeys = 1; require CGI::PSGI; my $form = {}; my $q = CGI::PSGI->new($env); $form->{$_} = $q->param($_) for $q->param; $txt .= "
".Data::Dumper->Dump([$env, $form], ['env', 'form'])."
"; return [200, ['Content-type', 'text/html'], [$txt]]; } =head1 DESCRIPTION If you want a more fully featured PSGI experience, it would be wise to look at the L and L set of modules. Net::Server::PSGI is intended as an easy gateway into PSGI. But to get the most out of all that PSGI has to offer, you should review the L and L. If you only need something a little more rudimentary, then Net::Server::PSGI may be good for you. Net::Server::PSGI takes Net::Server::HTTP one level farther. It begins with base type MultiType defaulting to Net::Server::Fork. It is easy to change it to any of the other Net::Server flavors by passing server_type => $other_flavor in the server configurtation. The port has also been defaulted to port 80 - but could easily be changed to another through the server configuration. You can also very easily add ssl by including, proto=>"ssl" and provide a SSL_cert_file and SSL_key_file. For example, here is a basic server that will bind to all interfaces, will speak both HTTP on port 8080 as well as HTTPS on 8443, and will speak both IPv4, as well as IPv6 if it is available. use base qw(Net::Server::PSGI); __PACKAGE__->run( port => [8080, "8443/ssl"], ipv => '*', # IPv6 if available SSL_key_file => '/my/key', SSL_cert_file => '/my/cert', ); =head1 METHODS =over 4 =item C This method has been overridden in Net::Server::PSGI - you should not use it while using Net::Server::PSGI. This overridden method parses the environment and sets up request alarms and handles dying failures. It calls process_psgi_request once the request is ready and headers have been parsed. =item C Used when psgi_enabled is true. During this method, find_psgi_handler will be called to return the appropriate psgi response handler. Once finished, print_psgi_headers and print_psgi_body are used to print out the response. See L. Typically this method should not be overridden. Instead, an appropriate method for finding the app should be given to find_psgi_handler or app. =item C Used to lookup the appropriate PSGI handler. A reference to the already parsed $env hashref is passed. PATH_INFO will be initialized to the full path portion of the URI. SCRIPT_NAME will be initialized to the empty string. This handler should set the appropriate values for SCRIPT_NAME and PATH_INFO depending upon the path matched. A code reference for the handler should be returned. The default find_psgi_handler will call the C method. If that fails a reference to the psgi_echo_handler is returned as the default application. sub find_psgi_handler { my ($self, $env) = @_; if ($env->{'PATH_INFO'} && $env->{'PATH_INFO'} =~ s{^ (/foo) (?= $ | /) }{}x) { $env->{'SCRIPT_NAME'} = $1; return \&foo_app; } return $self->SUPER::find_psgi_handler($env); } =item C Return a reference to the application being served. This should be a valid PSGI application. See L. By default it will look at the value of the C configuration option. The C method may also be used to set the C configuration option. package MyApp; use base qw(Net::Server::PSGI); sub default_server_type { 'Prefork' } sub my_app { my $env = shift; return [200, ['Content-type', 'text/html'], ["Hello world"]]; } MyApp->run(app => \&my_app); # OR sub app { \&my_app } MyApp->run; # OR my $server = MyApp->new; $server->app(\&my_app); $server->run; =back =head1 OPTIONS In addition to the command line arguments of the Net::Server::HTTP base classes you can also set the following options. =over 4 =item app Should return a coderef of the PSGI application. Is returned by the app method. =back =head1 AUTHOR Paul T. Seamons paul@seamons.com =head1 SEE ALSO Please see also L, L, L, L, L, L, L, L L L L L =cut Net-Server-2.008/lib/Net/Server/Log/0000755000175000017500000000000012334210320015452 5ustar paulpaulNet-Server-2.008/lib/Net/Server/Log/Log/0000755000175000017500000000000012334210320016173 5ustar paulpaulNet-Server-2.008/lib/Net/Server/Log/Log/Log4perl.pm0000644000175000017500000000720712331755703020247 0ustar paulpaul# -*- perl -*- # # Net::Server::Log::Log::Log4perl - Net::Server Logging module # # $Id$ # # Copyright (C) 2012 # # Paul Seamons # paul@seamons.com # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # ################################################################ package Net::Server::Log::Log::Log4perl; use strict; use warnings; our %log4perl_map = (1 => "error", 2 => "warn", 3 => "info", 4 => "debug"); sub initialize { my ($class, $server) = @_; my $prop = $server->{'server'}; require Log::Log4perl; $server->configure({ log4perl_conf => \$prop->{'log4perl_conf'}, log4perl_logger => \$prop->{'log4perl_logger'}, log4perl_poll => \$prop->{'log4perl_poll'}, }); die "Must specify a log4perl_conf file" if ! $prop->{'log4perl_conf'}; my $poll = defined($prop->{'log4perl_poll'}) ? $prop->{'log4perl_poll'} : "0"; my $logger = $prop->{'log4perl_logger'} || "Net::Server"; if ($poll eq "0") { Log::Log4perl::init($prop->{'log4perl_conf'}); } else { Log::Log4perl::init_and_watch($prop->{'log4perl_conf'}, $poll); } my $l4p = Log::Log4perl->get_logger($logger); return sub { my ($level, $msg) = @_; $level = $log4perl_map{$level} || "error"; $l4p->$level($msg); }; } 1; __END__ =head1 NAME Net::Server::Log::Log::Log4perl - log via Log4perl =head1 SYNOPSIS use base qw(Net::Server::PreFork); __PACKAGE__->run( log_file => 'Log::Log4perl', log4perl_conf => '/path/to/my/log4perl.conf', log4perl_logger => 'myapp', ); =head1 DESCRIPTION This module provides Log::Log4perl style logging to the Net::Server system. =head1 CONFIGURATION =over 4 =item log_file To begin using Log::Log4perl logging, simply set the Net::Server log_file configuration parameter to "Log::Log4perl". If the magic name "Log::Log4perl" is used, all logging will be directed to the Log4perl system. If used, the C, C, C may also be defined. =item log4perl_conf Only available if C is equal to "Log::Log4perl". This is the filename of the log4perl configuration file - see L. If this is not set, will die on startup. If the file is not readable, will die. =item log4perl_poll If set to a value, will initialise with Log::Log4perl::init_and_watch with this polling value. This can also be the string "HUP" to re-read the log4perl_conf when a HUP signal is received. If set to 0, no polling is done. See L for more details. =item log4perl_logger This is the facility name. Defaults to "Net::Server". =back =head1 DEFAULT ARGUMENTS FOR Net::Server The following arguments are available in the default C or C modules. (Other personalities may use additional parameters and may optionally not use parameters from the base class.) Key Value Default ## log4perl parameters (if log_file eq Log::Log4perl) log4perl_conf "filename" will die if not set log4perl_poll number or HUP 0 (no polling) log4perl_logger "name" "Net::Server" =head1 METHODS =over 4 =item C This method is called during the initilize_logging method of Net::Server. It returns a single code ref that will be stored under the log_function property of the Net::Server object. That code ref takes log_level and message as arguments and calls the initialized log4perl system. =back =head1 LICENCE Distributed under the same terms as Net::Server =cut Net-Server-2.008/lib/Net/Server/Log/Sys/0000755000175000017500000000000012334210320016230 5ustar paulpaulNet-Server-2.008/lib/Net/Server/Log/Sys/Syslog.pm0000644000175000017500000001254012331755703020070 0ustar paulpaul# -*- perl -*- # # Net::Server::Log::Sys::Syslog - Net::Server Logging module # # $Id$ # # Copyright (C) 2012 # # Paul Seamons # paul@seamons.com # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # ################################################################ package Net::Server::Log::Sys::Syslog; use strict; use warnings; use Sys::Syslog qw(setlogsock openlog syslog);; our %syslog_map = (0 => 'err', 1 => 'warning', 2 => 'notice', 3 => 'info', 4 => 'debug'); sub initialize { my ($class, $server) = @_; my $prop = $server->{'server'}; $server->configure({ syslog_logsock => \$prop->{'syslog_logsock'}, syslog_ident => \$prop->{'syslog_ident'}, syslog_logopt => \$prop->{'syslog_logopt'}, syslog_facility => \$prop->{'syslog_facility'}, }); if (ref($prop->{'syslog_logsock'}) eq 'ARRAY') { # do nothing - assume they have what they want } else { if (! defined $prop->{'syslog_logsock'}) { $prop->{'syslog_logsock'} = ($Sys::Syslog::VERSION < 0.15) ? 'unix' : ''; } if ($prop->{'syslog_logsock'} =~ /^(|native|tcp|udp|unix|inet|stream|console)$/) { $prop->{'syslog_logsock'} = $1; } else { $prop->{'syslog_logsock'} = ($Sys::Syslog::VERSION < 0.15) ? 'unix' : ''; } } my $ident = defined($prop->{'syslog_ident'}) ? $prop->{'syslog_ident'} : 'net_server'; $prop->{'syslog_ident'} = ($ident =~ /^([\ -~]+)$/) ? $1 : 'net_server'; my $opt = defined($prop->{'syslog_logopt'}) ? $prop->{'syslog_logopt'} : $Sys::Syslog::VERSION ge '0.15' ? 'pid,nofatal' : 'pid'; $prop->{'syslog_logopt'} = ($opt =~ /^( (?: (?:cons|ndelay|nowait|pid|nofatal) (?:$|[,|]) )* )/x) ? $1 : 'pid'; my $fac = defined($prop->{'syslog_facility'}) ? $prop->{'syslog_facility'} : 'daemon'; $prop->{'syslog_facility'} = ($fac =~ /^((\w+)($|\|))*/) ? $1 : 'daemon'; if ($prop->{'syslog_logsock'}) { setlogsock($prop->{'syslog_logsock'}) || die "Syslog err [$!]"; } if (! openlog($prop->{'syslog_ident'}, $prop->{'syslog_logopt'}, $prop->{'syslog_facility'})) { die "Couldn't open syslog [$!]" if $prop->{'syslog_logopt'} ne 'ndelay'; } return sub { my ($level, $msg) = @_; $level = $syslog_map{$level} || $level if $level =~ /^\d+$/; syslog($level, '%s', $msg); }; } sub handle_log_error { my ($class, $server, $err, $info) = @_; return $server->handle_syslog_error($err, $info); } 1; __END__ =head1 NAME Net::Server::Log::Sys::Syslog - log via Syslog =head1 SYNOPSIS use base qw(Net::Server::PreFork); __PACKAGE__->run( log_file => 'Sys::Syslog', syslog_ident => 'myapp', ); =head1 DESCRIPTION This module provides Sys::Syslog logging to the Net::Server system. =head1 CONFIGURATION =over 4 =item log_file To begin using Sys::Syslog logging, simply set the Net::Server log_file configuration parameter to "Sys::Syslog". If the magic name "Sys::Syslog" is used, all logging will take place via the Sys::Syslog module. If syslog is used the parameters C, C, and C,and C may also be defined. =item syslog_logsock Only available if C is equal to "Sys::Syslog". May be either unix, inet, native, console, stream, udp, or tcp, or an arrayref of the types to try. Default is "unix" if the version of Sys::Syslog < 0.15 - otherwise the default is to not call setlogsock. See L. =item syslog_ident Only available if C is equal to "Sys::Syslog". Id to prepend on syslog entries. Default is "net_server". See L. =item syslog_logopt Only available if C is equal to "Sys::Syslog". May be either zero or more of "pid","cons","ndelay","nowait". Default is "pid". See L. =item syslog_facility Only available if C is equal to "Sys::Syslog". See L and L. Default is "daemon". =back =head1 DEFAULT ARGUMENTS FOR Net::Server The following arguments are available in the default C or C modules. (Other personalities may use additional parameters and may optionally not use parameters from the base class.) Key Value Default ## syslog parameters (if log_file eq Sys::Syslog) syslog_logsock (native|unix|inet|udp |tcp|stream|console) unix (on Sys::Syslog < 0.15) syslog_ident "identity" "net_server" syslog_logopt (cons|ndelay|nowait|pid) pid syslog_facility \w+ daemon =head1 METHODS =over 4 =item C This method is called during the initilize_logging method of Net::Server. It returns a single code ref that will be stored under the log_function property of the Net::Server object. That code ref takes log_level and message as arguments and calls the initialized log4perl system. =item C This method is called if the log_function fails for some reason. It is passed the Net::Server object, the error that occurred while logging and an arrayref containing the log level and the message. In turn, this calls the legacy Net::Server::handle_syslog_error method. =back =head1 LICENCE Distributed under the same terms as Net::Server =cut Net-Server-2.008/lib/Net/Server/INET.pm0000644000175000017500000001235012331755703016047 0ustar paulpaul# -*- perl -*- # # Net::Server::INET - Net::Server personality # # $Id$ # # Copyright (C) 2001-2012 # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # # All rights reserved. # ################################################################ package Net::Server::INET; use strict; use base qw(Net::Server); use Scalar::Util qw(blessed); sub net_server_type { __PACKAGE__ } sub post_configure { my $self = shift; $self->{'server'}->{'_is_inet'} = 1; $self->SUPER::post_configure(); delete $self->{'server'}->{'_is_inet'}; } sub pre_bind {} # no need to prepare bind sub bind {} # inet has no port to bind sub accept { # connection is already accepted my $self = shift; my $prop = $self->{'server'}; ### Net::Server::INET will not do any determination of TCP,UDP,Unix ### it is up to the programmer to keep these as separate processes delete $prop->{'udp_true'}; # not sure if we can do UDP on INET 1; } sub get_client_info { my $self = shift; my $prop = $self->{'server'}; my $sock = shift || $prop->{'client'}; if (blessed($sock) && $sock->can('NS_proto') && $sock->NS_proto eq 'UNIX') { $self->log(3, $self->log_time." CONNECT UNIX Socket: \"".$sock->NS_port."\""); return; } $prop->{'sockaddr'} = $ENV{'REMOTE_HOST'} || '0.0.0.0'; $prop->{'peeraddr'} = '0.0.0.0'; $prop->{'sockhost'} = $prop->{'peerhost'} = 'inetd.server'; $prop->{'sockport'} = $prop->{'peerport'} = 0; return; } sub done { 1 } # accept only one connection per process sub post_accept { # set up handles my $self = shift; ### STDIN and STDOUT are already bound ### create a handle for those who want to use ### an IO::Socket'ish handle - more portable ### to just use STDIN and STDOUT though $self->{'server'}->{'client'} = Net::Server::INET::Handle->new(); } ### can't hup single process sub hup_server {} ################################################################ ### the rest are methods to tie STDIN and STDOUT to a GLOB ### this most likely isn't necessary, but the methods are there ### support for this is experimental and may go away ################################################################ package Net::Server::INET::Handle; use base qw(IO::Handle); use strict; sub new { my $class = shift; local *HAND; STDIN->autoflush(1); STDOUT->autoflush(1); tie *HAND, $class, *STDIN, *STDOUT or die "can't tie *HAND: $!"; bless \*HAND, $class; return \*HAND; } sub NS_proto { '' } sub TIEHANDLE { my ($class, $in, $out) = @_; bless [ \$in, \$out ], $class; } sub PRINT { my $handle = shift()->[1]; local *FH = $$handle; CORE::print FH @_; } sub PRINTF { my $handle = shift()->[1]; local *FH = $$handle; CORE::printf FH @_; } sub WRITE { my $handle = shift()->[1]; local *FH = $$handle; local ($\) = ""; $_[1] = length($_[0]) unless defined $_[1]; CORE::print FH substr($_[0], $_[2] || 0, $_[1]); } sub READ { my $handle = shift()->[0]; local *FH = $$handle; CORE::read(FH, $_[0], $_[1], $_[2] || 0); } sub READLINE { my $handle = shift()->[0]; local *FH = $$handle; return scalar ; } sub GETC { my $handle = shift()->[0]; local *FH = $$handle; return CORE::getc(FH); } sub EOF { my $handle = shift()->[0]; local *FH = $$handle; return CORE::eof(FH); } sub OPEN {} sub CLOSE { my $self = shift; $self = undef; } sub BINMODE {} sub TELL {} sub SEEK {} sub DESTROY {} sub FILENO {} sub FETCH {} sub read_until { # only sips the data - but it allows for compatibility with SSLEAY my ($client, $bytes, $end_qr) = @_; die "One of bytes or end_qr should be defined for TCP read_until\n" if !defined($bytes) && !defined($end_qr); my $content = ''; my $ok = 0; while (1) { $client->read($content, 1, length($content)); if (defined($bytes) && length($content) >= $bytes) { $ok = 2; last; } elsif (defined($end_qr) && $content =~ $end_qr) { $ok = 1; last; } } return wantarray ? ($ok, $content) : $content; } 1; __END__ =head1 NAME Net::Server::INET - Net::Server personality =head1 SYNOPSIS use base qw(Net::Server::INET); sub process_request { #...code... } Net::Server::INET->run(); =head1 DESCRIPTION Please read the pod on Net::Server first. This module is a personality, or extension, or sub class, of the Net::Server module. This personality is intended for use with inetd. It offers no methods beyond the Net::Server base class. This module operates by overriding the pre_bind, bind, accept, and post_accept methods to let all socket processing to be done by inetd. =head1 CONFIGURATION FILE See L. =head1 PROCESS FLOW See L =head1 HOOKS There are no additional hooks in Net::Server::INET. =head1 TO DO See L =head1 AUTHOR Paul T. Seamons paul@seamons.com =head1 SEE ALSO Please see also L, L, L, L, L =cut Net-Server-2.008/lib/Net/Server/HTTP.pm0000644000175000017500000010721712331755703016076 0ustar paulpaul# -*- perl -*- # # Net::Server::HTTP - Extensible Perl HTTP base server # # $Id$ # # Copyright (C) 2010-2012 # # Paul Seamons # paul@seamons.com # http://seamons.com/ # # This package may be distributed under the terms of either the # GNU General Public License # or the # Perl Artistic License # ################################################################ package Net::Server::HTTP; use strict; use base qw(Net::Server::MultiType); use Scalar::Util qw(weaken blessed); use IO::Handle (); use re 'taint'; # most of our regular expressions setting ENV should not be clearing taint use POSIX (); use Time::HiRes qw(time); my $has_xs_parser; BEGIN {$has_xs_parser = $ENV{'USE_XS_PARSER'} && eval { require HTTP::Parser::XS } }; sub net_server_type { __PACKAGE__ } sub options { my $self = shift; my $ref = $self->SUPER::options(@_); my $prop = $self->{'server'}; $ref->{$_} = \$prop->{$_} for qw(timeout_header timeout_idle server_revision max_header_size access_log_format access_log_file enable_dispatch); return $ref; } sub timeout_header { shift->{'server'}->{'timeout_header'} } sub timeout_idle { shift->{'server'}->{'timeout_idle'} } sub server_revision { shift->{'server'}->{'server_revision'} } sub max_header_size { shift->{'server'}->{'max_header_size'} } sub default_port { 80 } sub default_server_type { 'PreFork' } sub post_configure { my $self = shift; $self->SUPER::post_configure(@_); my $prop = $self->{'server'}; # set other defaults my $d = { timeout_header => 15, timeout_idle => 60, server_revision => __PACKAGE__."/$Net::Server::VERSION", max_header_size => 100_000, access_log_format => '%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"', }; $prop->{$_} = $d->{$_} foreach grep {!defined($prop->{$_})} keys %$d; $self->_init_access_log; $self->_tie_client_stdout; } sub post_bind { my $self = shift; $self->SUPER::post_bind(@_); $self->_check_dispatch; } sub _init_access_log { my $self = shift; my $prop = $self->{'server'}; my $log = $prop->{'access_log_file'}; return if ! $log || $log eq '/dev/null'; return if ! $prop->{'access_log_format'}; $prop->{'access_log_format'} =~ s/\\([\\\"nt])/$1 eq 'n' ? "\n" : $1 eq 't' ? "\t" : $1/eg; if ($log eq 'STDERR') { $prop->{'access_log_function'} = sub { print STDERR @_,"\n" }; } else { open my $fh, '>>', $log or die "Could not open access_log_file \"$log\": $!"; $fh->autoflush(1); push @{ $prop->{'chown_files'} }, $log; $prop->{'access_log_function'} = sub { print $fh @_,"\n" }; } } sub _tie_client_stdout { my $self = shift; my $prop = $self->{'server'}; # install a callback that will handle our outbound header negotiation for the clients similar to what apache does for us my $copy = $self; $prop->{'tie_client_stdout'} = 1; $prop->{'tied_stdout_callback'} = sub { my $client = shift; my $method = shift; alarm($copy->timeout_idle); # reset timeout my $request_info = $copy->{'request_info'}; if ($request_info->{'headers_sent'}) { # keep track of how much has been printed my ($resp, $len); if ($method eq 'print') { $resp = $client->print(my $str = join '', @_); $len = length $str; } elsif ($method eq 'printf') { $resp = $client->print(my $str = sprintf(shift, @_)); $len = length $str; } elsif ($method eq 'say') { $resp = $client->print(my $str = join '', @_, "\n"); $len = length $str; } elsif ($method eq 'write') { my $buf = shift; $buf = substr($buf, $_[1] || 0, $_[0]) if @_; $resp = $client->print($buf); $len = length $buf; } elsif ($method eq 'syswrite') { $len = $resp = $client->syswrite(@_); } else { return $client->$method(@_); } $request_info->{'response_size'} = ($request_info->{'response_size'} || 0) + $len if defined $len; return $resp; } die "All headers must only be sent via print ($method)\n" if $method ne 'print'; my $headers = ${*$client}{'headers'} ||= {unparsed => '', parsed => ''}; $headers->{'unparsed'} .= join('', @_); while ($headers->{'unparsed'} =~ s/^(.*?)\015?\012//) { my $line = $1; if (!$headers->{'parsed'} && $line =~ m{^HTTP/(1.[01]) \s+ (\d+) (?: | \s+ .+)$ }x) { $headers->{'status'} = []; $headers->{'parsed'} .= "$line\015\012"; $prop->{'request_info'}->{'http_version'} = $1; $prop->{'request_info'}->{'response_status'} = $2; } elsif (! length $line) { my $s = $headers->{'status'} || die "Premature end of script headers\n"; delete ${*$client}{'headers'}; $copy->send_status(@$s) if @$s; $client->print($headers->{'parsed'}."\015\012"); $request_info->{'headers_sent'} = 1; $request_info->{'response_header_size'} += length($headers->{'parsed'})+2; $request_info->{'response_size'} = length($headers->{'unparsed'}); return $client->print($headers->{'unparsed'}); } elsif ($line !~ s/^(\w+(?:-(?:\w+))*):\s*//) { my $invalid = ($line =~ /(.{0,120})/) ? "$1..." : ''; $invalid =~ s/\n"; } else { my $key = "\u\L$1"; $key =~ y/_/-/; push @{ $request_info->{'response_headers'} }, [$key, $line]; if ($key eq 'Status' && $line =~ /^(\d+) (?:|\s+(.+?))$/ix) { $headers->{'status'} = [$1, $2 || '-']; } elsif ($key eq 'Location') { $headers->{'status'} = [302, 'bouncing']; } elsif ($key eq 'Content-type') { $headers->{'status'} ||= [200, 'OK']; } $headers->{'parsed'} .= "$key: $line\015\012"; } } }; weaken $copy; } sub _check_dispatch { my $self = shift; if (! $self->{'server'}->{'enable_dispatch'}) { return if __PACKAGE__->can('process_request') ne $self->can('process_request'); return if __PACKAGE__->can('process_http_request') ne $self->can('process_http_request'); } my $app = $self->{'server'}->{'app'}; if (! $app || (ref($app) eq 'ARRAY' && !@$app)) { $app = []; $self->configure({app => $app}); } my %dispatch; my $first; my @dispatch; foreach my $a (ref($app) eq 'ARRAY' ? @$app : $app) { next if ! $a; my @pairs = ref($a) eq 'ARRAY' ? @$a : ref($a) eq 'HASH' ? %$a : ref($a) eq 'CODE' ? ('/', $a) : $a =~ m{^(.+?)\s+(.+)$} ? ($1, $2) : $a =~ m{^(.+?)=(.+)$} ? ($1, $2) : ($a, $a); for (my $i = 0; $i < @pairs; $i+=2) { my ($key, $val) = ("/$pairs[$i]", $pairs[$i+1]); $key =~ s{/\./}{/}g; $key =~ s{(?:/[^/]+|)/\../}{/}g; $key =~ s{//+}{/}g; if ($dispatch{$key}) { $self->log(2, "Already found a path matching \"$key\" - skipping."); next; } $dispatch{$key} = $val; push @dispatch, $key; $first ||= $key; $self->log(2, " Dispatch: $key => $val"); } } if (@dispatch) { if (! $dispatch{'/'} && $first) { $dispatch{'/'} = $dispatch{$first}; push @dispatch, '/'; $self->log(2, " Dispatch: / => $dispatch{$first} (default)"); } $self->{'dispatch_qr'} = join "|", map {"\Q$_\E"} @dispatch; $self->{'dispatch'} = \%dispatch; } } sub http_base_headers { my $self = shift; return [ [Date => gmtime()." GMT"], [Connection => 'close'], [Server => $self->server_revision], ]; } sub send_status { my ($self, $status, $msg, $body) = @_; $msg ||= ($status == 200) ? 'OK' : '-'; my $request_info = $self->{'request_info'}; my $out = "HTTP/1.0 $status $msg\015\012"; foreach my $row (@{ $self->http_base_headers }) { $out .= "$row->[0]: $row->[1]\015\012"; push @{ $request_info->{'response_headers'} }, $row; } $self->{'server'}->{'client'}->print($out); $request_info->{'http_version'} = '1.0'; $request_info->{'response_status'} = $status; $request_info->{'response_header_size'} += length $out; if ($body) { push @{ $request_info->{'response_headers'} }, ['Content-type', 'text/html']; $out = "Content-type: text/html\015\012\015\012"; $request_info->{'response_header_size'} += length $out; $self->{'server'}->{'client'}->print($out); $request_info->{'headers_sent'} = 1; $self->{'server'}->{'client'}->print($body); $request_info->{'response_size'} += length $body; } } sub send_500 { my ($self, $err) = @_; $self->send_status(500, 'Internal Server Error', "

Internal Server Error

$err

"); } ###----------------------------------------------------------------### sub run_client_connection { my $self = shift; local $self->{'request_info'} = {}; return $self->SUPER::run_client_connection(@_); } sub get_client_info { my $self = shift; $self->SUPER::get_client_info(@_); $self->clear_http_env; } sub clear_http_env { my $self = shift; %ENV = (); } sub process_request { my $self = shift; my $client = shift || $self->{'server'}->{'client'}; my $ok = eval { local $SIG{'ALRM'} = sub { die "Server Timeout on headers\n" }; alarm($self->timeout_header); $self->process_headers($client); $SIG{'ALRM'} = sub { die "Server Timeout on process\n" }; alarm($self->timeout_idle); $self->process_http_request($client); alarm(0); 1; }; alarm(0); if (! $ok) { my $err = "$@" || "Something happened"; $self->log(1, $err); $self->send_500($err); } } sub script_name { shift->{'script_name'} || '' } sub process_headers { my $self = shift; my $client = shift || $self->{'server'}->{'client'}; $ENV{'REMOTE_PORT'} = $self->{'server'}->{'peerport'}; $ENV{'REMOTE_ADDR'} = $self->{'server'}->{'peeraddr'}; $ENV{'SERVER_PORT'} = $self->{'server'}->{'sockport'}; $ENV{'SERVER_ADDR'} = $self->{'server'}->{'sockaddr'}; $ENV{'HTTPS'} = 'on' if $self->{'server'}->{'client'}->NS_proto =~ /SSL/; my ($ok, $headers) = $client->read_until($self->max_header_size, qr{\n\r?\n}); my ($req, $len, @parsed); die "Could not parse http headers successfully\n" if $ok != 1; if ($has_xs_parser) { $len = HTTP::Parser::XS::parse_http_request($headers, \%ENV); die "Corrupt request" if $len == -1; die "Incomplete request" if $len == -2; $req = "$ENV{'REQUEST_METHOD'} $ENV{'REQUEST_URI'} $ENV{'SERVER_PROTOCOL'}"; } else { ($req, my @lines) = split /\r?\n/, $headers; die "Missing request\n" if ! defined $req; if (!defined($req) || $req !~ m{ ^\s*(GET|POST|PUT|DELETE|PUSH|HEAD|OPTIONS)\s+(.+)\s+(HTTP/1\.[01])\s*$ }ix) { die "Invalid request\n"; } $ENV{'REQUEST_METHOD'} = uc $1; $ENV{'REQUEST_URI'} = $2; $ENV{'SERVER_PROTOCOL'} = $3; $ENV{'QUERY_STRING'} = $1 if $ENV{'REQUEST_URI'} =~ m{ \?(.*)$ }x; $ENV{'PATH_INFO'} = $1 if $ENV{'REQUEST_URI'} =~ m{^([^\?]+)}; foreach my $l (@lines) { my ($key, $val) = split /\s*:\s*/, $l, 2; push @parsed, [$key, $val]; $key = uc($key); $key = 'COOKIE' if $key eq 'COOKIES'; $key =~ y/-/_/; $key =~ s/^\s+//; $key = "HTTP_$key" if $key !~ /^CONTENT_(?:LENGTH|TYPE)$/; $val =~ s/\s+$//; if (exists $ENV{$key}) { $ENV{$key} .= ", $val"; } else { $ENV{$key} = $val; } } $len = length $headers; } $ENV{'SCRIPT_NAME'} = $self->script_name($ENV{'PATH_INFO'}) || ''; my $type = $Net::Server::HTTP::ISA[0]; $type = $Net::Server::MultiType::ISA[0] if $type eq 'Net::Server::MultiType'; $ENV{'NET_SERVER_TYPE'} = $type; $ENV{'NET_SERVER_SOFTWARE'} = $self->server_revision; $self->_init_http_request_info($req, \@parsed, $len); } sub http_request_info { shift->{'request_info'} } sub _init_http_request_info { my ($self, $req, $parsed, $len) = @_; my $prop = $self->{'server'}; my $info = $self->{'request_info'}; @$info{qw(sockaddr sockport peeraddr peerport)} = @$prop{qw(sockaddr sockport peeraddr peerport)}; $info->{'peerhost'} = $prop->{'peerhost'} || $info->{'peeraddr'}; $info->{'begin'} = time; $info->{'request'} = $req; $info->{'request_headers'} = $parsed; $info->{'query_string'} = "?$ENV{'QUERY_STRING'}" if defined $ENV{'QUERY_STRING'}; $info->{'request_protocol'} = $ENV{'HTTPS'} ? 'https' : 'http'; $info->{'request_method'} = $ENV{'REQUEST_METHOD'}; $info->{'request_path'} = $ENV{'PATH_INFO'}; $info->{'request_header_size'} = $len; $info->{'request_size'} = $ENV{'CONTENT_LENGTH'} || 0; # we might not actually read entire request $info->{'remote_user'} = '-'; } sub http_note { my ($self, $key, $val) = @_; return $self->{'request_info'}->{'notes'}->{$key} = $val if @_ >= 3; return $self->{'request_info'}->{'notes'}->{$key}; } sub http_dispatch { my ($self, $dispatch_qr, $dispatch_table) = @_; $ENV{'PATH_INFO'} =~ s{^($dispatch_qr)(?=/|$|(?<=/))}{} or die "Dispatch not found\n"; $ENV{'SCRIPT_NAME'} = $1; if ($ENV{'PATH_INFO'}) { $ENV{'PATH_INFO'} = "/$ENV{'PATH_INFO'}" if $ENV{'PATH_INFO'} !~ m{^/}; $ENV{'PATH_INFO'} =~ s/%([a-fA-F0-9]{2})/chr(hex $1)/eg; } my $code = $self->{'dispatch'}->{$1}; return $self->$code() if ref $code; $self->exec_cgi($code); } sub process_http_request { my ($self, $client) = @_; if (my $table = $self->{'dispatch'}) { my $qr = $self->{'dispatch_qr'} or die "Dispatch was not correctly setup\n"; return $self->http_dispatch($qr, $table) } return $self->http_echo; } sub http_echo { my $self = shift; print "Content-type: text/html\n\n"; print "
\n"; if (eval { require Data::Dumper }) { local $Data::Dumper::Sortkeys = 1; my $form = {}; if (eval { require CGI }) { my $q = CGI->new; $form->{$_} = $q->param($_) for $q->param; } print "
".Data::Dumper->Dump([\%ENV, $form], ['*ENV', 'form'])."
"; } } sub post_process_request { my $self = shift; my $info = $self->{'request_info'}; $info->{'begin'} = time unless defined $info->{'begin'}; $info->{'elapsed'} = time - $info->{'begin'}; $self->SUPER::post_process_request(@_); $self->log_http_request($info); } ###----------------------------------------------------------------### sub log_http_request { my ($self, $info) = @_; my $prop = $self->{'server'}; my $fmt = $prop->{'access_log_format'} || return; my $log = $prop->{'access_log_function'} || return; $log->($self->http_log_format($fmt, $info)); } my %fmt_map = qw( a peeraddr A sockaddr B response_size f filename h peerhost H request_protocol l remote_logname m request_method p sockport q query_string r request s response_status u remote_user U request_path ); my %fmt_code = qw( C http_log_cookie e http_log_env i http_log_header_in n http_log_note o http_log_header_out P http_log_pid t http_log_time v http_log_vhost V http_log_vhost X http_log_constat ); sub http_log_format { my ($self, $fmt, $info, $orig) = @_; $fmt =~ s{ % ([<>])? # 1 (!? \d\d\d (?:,\d\d\d)* )? # 2 (?: \{ ([^\}]+) \} )? # 3 ([aABDfhHmpqrsTuUvVhblPtIOCeinoPtX%]) # 4 }{ $info = $orig if $1 && $orig && $1 eq '<'; my $v = $2 && (substr($2,0,1) eq '!' ? index($2, $info->{'response_status'})!=-1 : index($2, $info->{'response_status'})==-1) ? '-' : $fmt_map{$4} ? $info->{$fmt_map{$4}} : $fmt_code{$4} ? do { my $m = $fmt_code{$4}; $self->$m($info, $3, $1, $4) } : $4 eq 'b' ? $info->{'response_size'} || '-' # B can be 0, b cannot : $4 eq 'I' ? $info->{'request_size'} + $info->{'request_header_size'} : $4 eq 'O' ? $info->{'response_size'} + $info->{'response_header_size'} : $4 eq 'T' ? sprintf('%d', $info->{'elapsed'}) : $4 eq 'D' ? sprintf('%d', $info->{'elapsed'}/.000_001) : $4 eq '%' ? '%' : '-'; $v = '-' if !defined($v) || !length($v); $v =~ s/([^\ -\!\#-\[\]-\~])/$1 eq "\n" ? '\n' : $1 eq "\t" ? '\t' : sprintf('\x%02X', ord($1))/eg; # escape non-printable or " or \ $v; }gxe; return $fmt; } sub http_log_time { my ($self, $info, $fmt) = @_; return '['.POSIX::strftime($fmt || '%d/%b/%Y:%T %z', localtime($info->{'begin'})).']'; } sub http_log_env { $ENV{$_[2]} } sub http_log_cookie { my ($self, $info, $var) = @_; my @c; for my $cookie (map {$_->[1]} grep {$_->[0] eq 'Cookie' } @{ $info->{'request_headers'} || [] }) { push @c, $1 if $cookie =~ /^\Q$var\E=(.*)/; } return join ', ', @c; } sub http_log_header_in { my ($self, $info, $var) = @_; return join ', ', map {$_->[1]} grep {$_->[0] eq $var} @{ $info->{'request_headers'} || [] }; } sub http_log_note { my ($self, $info, $var) = @_; return $self->http_note($var); } sub http_log_header_out { my ($self, $info, $var) = @_; return join ', ', map {$_->[1]} grep {$_->[0] eq $var} @{ $info->{'response_headers'} || [] }; } sub http_log_pid { $_[1]->{'pid'} || $$ } # we do not support tid yet sub http_log_vhost { my ($self, $info, $fmt, $f_l, $type) = @_; return $self->http_log_header_in($info, 'Host') || $self->{'server'}->{'client'}->NS_host || $self->{'server'}->{'sockaddr'}; } sub http_log_constat { my ($self, $info) = @_; return $info->{'headers_sent'} ? '-' : 'X'; } ###----------------------------------------------------------------### sub exec_fork_hook {} sub exec_trusted_perl { my ($self, $file) = @_; die "File $file is not executable\n" if ! -x $file; local $!; my $pid = fork; die "Could not spawn child process: $!\n" if ! defined $pid; $self->exec_fork_hook($pid, $file, 1); if (!$pid) { if (!eval { require $file }) { my $err = "$@" || "Error while running trusted perl script\n"; $err =~ s{\s*Compilation failed in require at lib/Net/Server/HTTP\.pm line \d+\.\s*\z}{\n}; die $err if !$self->{'request_info'}->{'headers_sent'}; warn $err; } exit; } else { waitpid $pid, 0; return; } } sub exec_cgi { my ($self, $file) = @_; my $done = 0; my $pid; Net::Server::SIG::register_sig(CHLD => sub { while (defined(my $chld = waitpid(-1, POSIX::WNOHANG()))) { $done = ($? >> 8) || -1 if $pid == $chld; last unless $chld > 0; } }); require IPC::Open3; require Symbol; my $in; my $out; my $err = Symbol::gensym(); local $!; $pid = eval { IPC::Open3::open3($in, $out, $err, $file) } or die "Could not run external script $file: $!\n"; $self->exec_fork_hook($pid, $file); # won't occur for the child my $len = $ENV{'CONTENT_LENGTH'} || 0; my $s_in = $len ? IO::Select->new($in) : undef; my $s_out = IO::Select->new($out, $err); my $printed; while (!$done) { my ($o, $i, $e) = IO::Select->select($s_out, $s_in, undef); Net::Server::SIG::check_sigs(); for my $fh (@$o) { read($fh, my $buf, 4096) || next; if ($fh == $out) { print $buf; $printed ||= 1; } else { print STDERR $buf; } } if (@$i) { my $bytes = read(STDIN, my $buf, $len); print $in $buf if $bytes; $len -= $bytes; $s_in = undef if $len <= 0; } } if (!$self->{'request_info'}->{'headers_sent'}) { if (!$printed) { $self->send_500("Premature end of script headers"); } elsif ($done > 0) { $self->send_500("Script exited unsuccessfully"); } } Net::Server::SIG::unregister_sig('CHLD'); } 1; __END__ =head1 NAME Net::Server::HTTP - very basic Net::Server based HTTP server class =head1 TEST ONE LINER perl -e 'use base qw(Net::Server::HTTP); main->run(port => 8080)' # will start up an echo server =head1 SYNOPSIS use base qw(Net::Server::HTTP); __PACKAGE__->run; sub process_http_request { my $self = shift; print "Content-type: text/html\n\n"; print "
\n"; require Data::Dumper; local $Data::Dumper::Sortkeys = 1; require CGI; my $form = {}; my $q = CGI->new; $form->{$_} = $q->param($_) for $q->param; print "
".Data::Dumper->Dump([\%ENV, $form], ['*ENV', 'form'])."
"; } =head1 DESCRIPTION Even though Net::Server::HTTP doesn't fall into the normal parallel of the other Net::Server flavors, handling HTTP requests is an often requested feature and is a standard and simple protocol. Net::Server::HTTP begins with base type MultiType defaulting to Net::Server::Fork. It is easy to change it to any of the other Net::Server flavors by passing server_type => $other_flavor in the server configurtation. The port has also been defaulted to port 80 - but could easily be changed to another through the server configuration. You can also very easily add ssl by including, proto=>"ssl" and provide a SSL_cert_file and SSL_key_file. For example, here is a basic server that will bind to all interfaces, will speak both HTTP on port 8080 as well as HTTPS on 8443, and will speak both IPv4, as well as IPv6 if it is available. use base qw(Net::Server::HTTP); __PACKAGE__->run( port => [8080, "8443/ssl"], ipv => '*', # IPv6 if available SSL_key_file => '/my/key', SSL_cert_file => '/my/cert', ); =head1 METHODS =over 4 =item C<_init_access_log> Used to open and initialize any requested access_log (see access_log_file and access_log_format). =item C<_tie_client_stdout> Used to initialize automatic response header parsing. =item C Will be passed the client handle, and will have STDOUT and STDIN tied to the client. During this method, the %ENV will have been set to a standard CGI style environment. You will need to be sure to print the Content-type header. This is one change from the other standard Net::Server base classes. During this method you can read from %ENV and STDIN just like a normal HTTP request in other web servers. You can print to STDOUT and Net::Server will handle the header negotiation for you. Note: Net::Server::HTTP has no concept of document root or script aliases or default handling of static content. That is up to the consumer of Net::Server::HTTP to work out. Net::Server::HTTP comes with a basic %ENV display installed as the default process_http_request method. =item C This method has been overridden in Net::Server::HTTP - you should not use it while using Net::Server::HTTP. This overridden method parses the environment and sets up request alarms and handles dying failures. It calls process_http_request once the request is ready and headers have been parsed. =item C Used to read in the incoming headers and set the ENV. =item C<_init_http_request_info> Called at the end of process_headers. Initializes the contents of http_request_info. =item C Returns a hashref of information specific to the current request. This information will be used for logging later on. =item C Takes an HTTP status and a message. Sends out the correct headers. =item C Calls send_status with 500 and the argument passed to send_500. =item c Called at the end of post_process_request. The default method looks for the default access_log_format and checks if logging was initilized during _init_access_log. If both of these exist, the http_request_info is formatted using http_log_format and the result is logged. =item C Takes a format string, and request_info and returns a formatted string. The format should follow the apache mod_log_config specification. As in the mod_log_config specification, backslashes, quotes should be escaped with backslashes and you may also include \n and \t characters as well. The following is a listing of the available parameters as well as sample output based on a very basic HTTP server. %% % # a percent %a ::1 # remote ip %A ::1 # local ip %b 83 # response size (- if 0) Common Log Format %B 83 # response size %{bar}C baz # value of cookie by that name %D 916 # elapsed in microseconds %{HTTP_COOKIE}e bar=baz # value of %ENV by that name %f - # filename - unused %h ::1 # remote host if lookups are on, remote ip otherwise %H http # request protocol %{Host}i localhost:8080 # request header by that name %I 336 # bytes received including headers %l - # remote logname - unsused %m GET # request method %n Just a note # http_note by that name %{Content-type}o text/html # output header by that name %O 189 # response size including headers %p 8080 # server port %P 22999 # pid - does not support %{tid}P q ?hello=there # query_string including ? (- otherwise) r GET /bam?hello=there HTTP/1.1 # the first line of the request %s 200 # response status %u - # remote user - unused %U /bam # request path (no query string) %t [06/Jun/2012:12:14:21 -0600] # http_log_time standard format %t{%F %T %z}t [2012-06-06 12:14:21 -0600] # http_log_time with format %T 0 # elapsed time in seconds %v localhost:8080 # http_log_vhost - partial implementation %V localhost:8080 # http_log_vhost - partial implementation %X - # Connection completed and is 'close' (-) Additionally, the log parsing allows for the following formats. %>s 200 # status of last request % and < # There is no internal redirection %I # The answer to this is based on header size and Content-length instead of the more correct actual number of bytes read though in common cases those would be the same. %X # There is no Connection keepalive in the default server. %v and %V # There are no virtual hosts in the default HTTP server. %{tid}P # The default servers are not threaded. See the C option for how to set a different format as well as to see the default string. =item C Allow for calling an external script as a CGI. This will use IPC::Open3 to fork a new process and read/write from it. use base qw(Net::Server::HTTP); __PACKAGE__->run; sub process_http_request { my $self = shift; if ($ENV{'PATH_INFO'} && $ENV{'PATH_INFO'} =~ s{^ (/foo) (?= $ | /) }{}x) { $ENV{'SCRIPT_NAME'} = $1; my $file = "/var/www/cgi-bin/foo"; # assuming this exists return $self->exec_cgi($file); } print "Content-type: text/html\n\n"; print "Foo"; } At this first release, the parent server is not tracking the child script which may cause issues if the script is running when a HUP is received. =item C Used to implement the %t format. =item C Used to implement the %e format. =item C Used to implement the %C format. =item C used to implement the %i format. =item C Used to implement the %n format. =item C Takes a key and an optional value. If passed a key and value, sets the note for that key. Always returns the value. These notes currently only are used for %{key}n output format. =item C Used to implement the %o format. =item C Used to implement the %P format. =item C Used to implement the %v and %V formats. =item C Used to implement the %X format. =item C Allow for calling an external perl script. This method will still fork, but instead of using IPC::Open3, it simply requires the perl script. That means that the running script will be able to make use of any shared memory. It also means that the STDIN/STDOUT/STDERR handles the script is using are those directly bound by the server process. use base qw(Net::Server::HTTP); __PACKAGE__->run; sub process_http_request { my $self = shift; if ($ENV{'PATH_INFO'} && $ENV{'PATH_INFO'} =~ s{^ (/foo) (?= $ | /) }{}x) { $ENV{'SCRIPT_NAME'} = $1; my $file = "/var/www/cgi-bin/foo"; # assuming this exists return $self->exec_trusted_perl($file); } print "Content-type: text/html\n\n"; print "Foo"; } At this first release, the parent server is not tracking the child script which may cause issues if the script is running when a HUP is received. =item C This method is called after the fork of exec_trusted_perl and exec_cgi hooks. It is passed the pid (0 if the child) and the file being ran. Note, that the hook will not be called from the child during exec_cgi. =item C Called if the default process_http_request and process_request methods have not been overridden and C configuration parameters have been passed. In this case this replaces the default echo server. You can also enable this subsystem for your own direct use by setting enable_dispatch to true during configuration. See the C configuration item. It will be passed a dispatch qr (regular expression) generated during _check_dispatch, and a dispatch table. The qr will be applied to path_info. This mechanism could be used to augment Net::Server::HTTP with document root and virtual host capabilities. =back =head1 OPTIONS In addition to the command line arguments of the Net::Server base classes you can also set the following options. =over 4 =item max_header_size Defaults to 100_000. Maximum number of bytes to read while parsing headers. =item server_revision Defaults to Net::Server::HTTP/$Net::Server::VERSION. =item timeout_header Defaults to 15 - number of seconds to wait for parsing headers. =item timeout_idle Defaults to 60 - number of seconds a request can be idle before the request is closed. =item access_log_file Defaults to undef. If true, this represents the location of where the access log should be written to. If a special value of STDERR is passed, the access log entry will be writing to the same location as the ERROR log. =item access_log_format Should be a valid apache log format that will be passed to http_log_format. See the http_log_format method for more information. The default value is the NCSA extended/combined log format: '%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"' =item app Takes one or more items and registers them for dispatch. Arguments may be supplied as an arrayref containing a location/target pairs, a hashref containing a location/target pairs, a bare code ref that will use "/" as the location and the codref as the target, a string with a space indicating "location target", a string containing "location=target", or finally a string that will be used as both location and target. For items passed as an arrayref or hashref, the target may be a coderef which will be called and should handle the request. In all other cases the target should be a valid executable suitable for passing to exec_cgi. The locations will be added in the order that they are configured. They will be added to a regular expression which will be applied to the incoming PATH_INFO string. If the match is successful, the $ENV{'SCRIPT_NAME'} will be set to the matched portion and the matched portion will be removed from $ENV{'PATH_INFO'}. Once an app has been passed, it is necessary for the server to listen on /. Therefore if "/" has not been specifically configured for dispatch, the first found dispatch target will also be used to handle "/". For convenience, if the log_level is 2 or greater, the dispatch table is output to the log. This mechanism is left as a generic mechanism suitable for overriding by servers meant to handle more complex dispatch. At the moment there is no handling of virtual hosts. At some point we will add in the default ability to play static content and likely for the ability to configure virtual hosts - or that may have to wait for a third party module. app => "/home/paul/foo.cgi", # Dispatch: /home/paul/foo.cgi => home/paul/foo.cgi # Dispatch: / => home/paul/foo.cgi (default) app => "../../foo.cgi", app => "./bar.cgi", app => "baz ./bar.cgi", app => "bim=./bar.cgi", # Dispatch: /foo.cgi => ../../foo.cgi # Dispatch: /bar.cgi => ./bar.cgi # Dispatch: /baz => ./bar.cgi # Dispatch: /bim => ./bar.cgi # Dispatch: / => ../../foo.cgi (default) app => "../../foo.cgi", app => "/=./bar.cgi", # Dispatch: /foo.cgi => ../../foo.cgi # Dispatch: / => ./bar.cgi # you could also do this on the commandline net-server HTTP app ../../foo.cgi app /=./bar.cgi # extended options when configured from code Net::Server::HTTP->run(app => { # loses order of matching '/' => sub { ... }, '/foo' => sub { ... }, '/bar' => '/path/to/some.cgi', }); Net::Server::HTTP->run(app => [ '/' => sub { ... }, '/foo' => sub { ... }, '/bar' => '/path/to/some.cgi', ]); =back =head1 TODO Add support for writing out HTTP/1.1. =head1 AUTHOR Paul T. Seamons paul@seamons.com =head1 THANKS See L =head1 SEE ALSO Please see also L, L, L, L, L, L L L L =cut Net-Server-2.008/META.json0000664000175000017500000000175212334210320013617 0ustar paulpaul{ "abstract" : "Extensible, general Perl server engine", "author" : [ "Paul Seamons and Rob Brown " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 6.66, CPAN::Meta::Converter version 2.133380", "license" : [ "unknown" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Net-Server", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "IO::Socket" : "0", "POSIX" : "0", "Socket" : "0", "Time::HiRes" : "0" } } }, "release_status" : "stable", "version" : "2.008" } Net-Server-2.008/README0000644000175000017500000000052712334165251013067 0ustar paulpaul This archive contains the distribution Net-Server. Extensible Perl internet server This software is copyright (c) 2014 by Paul Seamons. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Instructions for installing the module can be found in INSTALL. Net-Server-2.008/t/0000755000175000017500000000000012334210320012432 5ustar paulpaulNet-Server-2.008/t/Port_Configuration.t0000644000175000017500000004304712331755703016462 0ustar paulpaul# -*- Mode: Perl; -*- =head1 NAME Port_Configuration.t - Test different ways of specifying the port =cut package FooServer; use strict; use FindBin qw($Bin); use lib $Bin; use NetServerTest qw(prepare_test ok is use_ok diag skip); prepare_test({ n_tests => 51, plan_only => 1, hostname => 'localhost', # passing in an explicit one keeps it from doing IPv* resolution }); #use CGI::Ex::Dump qw(debug); use_ok('Net::Server'); @FooServer::ISA = qw(Net::Server); ### override these to make run not run ### this will allow all configuration cycles to be run sub bind {} sub post_bind {} sub loop {} sub log {} sub server_close { my $self = shift; return $self; } sub fatal { my ($self, $msg) = @_; die $msg; } sub SSL_cert_file { 'somecert' } my $dump; # poormans dumper - concise but not full bore $dump = sub { my $ref = shift; my $ind = shift || ''; return (!defined $ref) ? 'undef' : ($ref eq '0') ? 0 : ($ref=~/^[1-9]\d{0,12}$/) ? $ref : "'$ref'" if ! ref $ref; return "[".join(', ',map {$dump->($_)} @$ref).']' if ref $ref eq 'ARRAY'; return "{".join(',',map {"\n$ind $_ => ".$dump->($ref->{$_},"$ind ")} sort keys %$ref)."\n$ind}"; }; sub p_c { # port check my ($pkg, $file, $line) = caller; my ($args, $hash, $args_to_new) = @_; my $prop = eval { ($args_to_new ? FooServer->new(@$args)->run : FooServer->run(@$args))->{'server'} } || do { diag "$@ at line $line"; {} }; # use CGI::Ex::Dump qw(debug); # debug $prop; my $got = {bind => $prop->{'_bind'}}; if ($hash->{'sock'}) { push @{ $got->{'sock'} }, NS_props($_) for @{ $prop->{'sock'} || [] }; } my $result = $dump->($got); my $test = $dump->($hash); (my $str = $dump->({ref($args->[0]) eq 'HASH' ? %{$args->[0]} : @$args})) =~ s/\s*\n\s*/ /g; $str =~ s/^\{/(/ && $str =~ s/\}$/)/ if ref($args->[0]) ne 'HASH'; $str .= " ==> [ '".join("', '", map {$_->hup_string} @{ $prop->{'sock'} || [] })."' ]"; $str = ($args_to_new ? 'new' : 'run')." $str"; if ($result eq $test && $str !~ /\|\|/) { ok(1, "$str"); } else { diag "Failed at line $line"; is($result, $test, "$str"); exit; } } my %class_m; sub NS_props { no strict 'refs'; my $sock = shift || return {}; my $pkg = ref($sock); my $m = $class_m{$pkg} ||= {map {$_ => 1} qw(NS_port NS_host NS_proto NS_ipv), grep {/^(?:SSL|NS)_\w+$/ && defined(&{"${pkg}::$_"})} keys %{"${pkg}::"}}; return {map {$_ => $sock->$_()} keys %$m}; } ###----------------------------------------------------------------### # tcp, udp if (!eval { IO::Socket::INET->new->configure({LocalPort => 20203, Proto => 'tcp', Listen => 1, ReuseAddr => 1}) or die; }) { chomp(my $err = $@); SKIP: { skip "Cannot load Socket6 libraries - skipping IPv6 proto tests ($err)", 25; }; } else { local $ENV{'IPV'} = 4; # pretend to be on a system without IPv6 p_c([], { bind => [{ host => '*', port => Net::Server::default_port(), ipv => '4', proto => 'tcp', }], sock => [{ NS_host => '*', NS_port => Net::Server::default_port(), NS_ipv => '4', NS_proto => 'TCP', NS_listen => eval { Socket::SOMAXCONN() }, }], }); p_c([port => 20201], { bind => [{host => '*', port => 20201, proto => 'tcp', ipv => '4'}], }); p_c([port => "localhost:20202"], { bind => [{host => 'localhost', port => 20202, proto => 'tcp', ipv => '4'}], }); p_c([port => ["localhost:20202/tcp"]], { bind => [{host => 'localhost', port => 20202, proto => 'tcp', ipv => '4'}], }); p_c([port => "localhost:20202/ipv4"], { bind => [{host => 'localhost', port => 20202, proto => 'tcp', ipv => '4'}], }); p_c([port => ["localhost:20201/ipv4/tcp", "localhost:20202/tcp/IPv4"]], { bind => [{host => 'localhost', port => 20201, proto => 'tcp', ipv => '4'}, {host => 'localhost', port => 20202, proto => 'tcp', ipv => '4'}], }); p_c([port => ["localhost|20201|ipv4|tcp", "localhost,20202,tcp,IPv4"]], { bind => [{host => 'localhost', port => 20201, proto => 'tcp', ipv => '4'}, {host => 'localhost', port => 20202, proto => 'tcp', ipv => '4'}], }); p_c([port => ["localhost 20201 ipv4 tcp", "localhost, 20202, tcp, IPv4"]], { bind => [{host => 'localhost', port => 20201, proto => 'tcp', ipv => '4'}, {host => 'localhost', port => 20202, proto => 'tcp', ipv => '4'}], }); p_c([port => "localhost:20202/udp"], { bind => [{host => 'localhost', port => 20202, proto => 'udp', ipv => '4'}], sock => [{ NS_broadcast => undef, NS_host => 'localhost', NS_port => 20202, NS_ipv => '4', NS_proto => 'UDP', NS_recv_flags => 0, NS_recv_len => 4096, }], }); p_c([port => 20202, listen => 5], { bind => [{host => '*', port => 20202, proto => 'tcp', ipv => '4'}], sock => [{ NS_host => '*', NS_port => 20202, NS_proto => 'TCP', NS_listen => 5, NS_ipv => '4', }], }); p_c([port => ["bar.com:20201/udp", "foo.com:20202/tcp"]], {bind => [ {host => 'bar.com', port => 20201, proto => 'udp', ipv => '4'}, {host => 'foo.com', port => 20202, proto => 'tcp', ipv => '4'}, ]}); p_c([port => 20201, host => 'bar.com', proto => 'UDP'], { bind => [{host => 'bar.com', port => 20201, proto => 'UDP', ipv => '4'}], }); p_c([{port => 20201, host => 'bar.com', proto => 'UDP', udp_recv_len => 400}], { bind => [{host => 'bar.com', port => 20201, proto => 'UDP', ipv => '4'}], sock => [{NS_host => 'bar.com', NS_port => 20201, NS_proto => 'UDP', NS_ipv => '4', NS_recv_len => 400, NS_recv_flags => 0, NS_broadcast => undef}], }); p_c([port => 20201, host => 'bar.com', proto => 'UDP'], { bind => [{host => 'bar.com', port => 20201, proto => 'UDP', ipv => 4}], }, 'new'); p_c([{port => 20201, host => 'bar.com', proto => 'UDP'}], { bind => [{host => 'bar.com', port => 20201, proto => 'UDP', ipv => 4}], }, 'new'); p_c([port => [20201, "foo.com:20202/tcp"], host => 'bar.com', proto => 'UDP'], {bind => [ {host => 'bar.com', port => 20201, proto => 'UDP', ipv => 4}, {host => 'foo.com', port => 20202, proto => 'tcp', ipv => 4}, ]}); p_c([port => ["localhost|20202|tcp"]], { bind => [{host => 'localhost', port => 20202, proto => 'tcp', ipv => 4}], }); p_c([port => ["localhost,20202,tcp"]], { bind => [{host => 'localhost', port => 20202, proto => 'tcp', ipv => 4}], }); p_c([port => ["[localhost]:20202/tcp"]], { bind => [{host => 'localhost', port => 20202, proto => 'tcp', ipv => 4}], }); p_c([port => ["localhost,20202,Net::Server::Proto::TCP"]], { bind => [{host => 'localhost', port => 20202, proto => 'Net::Server::Proto::TCP', ipv => 4}], }); p_c([port => {port => 20201}], { bind => [{host => '*', port => 20201, proto => 'tcp', ipv => 4}], }); p_c([port => [{port => 20201}]], { bind => [{host => '*', port => 20201, proto => 'tcp', ipv => 4}], }); p_c([port => [{port => 20201, host => 'foo.com', proto => 'udp'}]], { bind => [{host => 'foo.com', port => 20201, proto => 'udp', ipv => 4}], }); p_c([port => [{port => 20201}], host => 'foo.com', proto => 'udp'], { bind => [{host => 'foo.com', port => 20201, proto => 'udp', ipv => 4}], }); p_c([port => [{port => 20202, listen => 6}]], { bind => [{host => '*', port => 20202, proto => 'tcp', listen => 6, ipv => 4}], sock => [{ NS_host => '*', NS_port => 20202, NS_proto => 'TCP', NS_listen => 6, NS_ipv => 4, }], }); } ###----------------------------------------------------------------### # unix, unixdgram if (!eval { require IO::Socket::UNIX }) { my $err = $@; SKIP: { skip "Cannot load IO::Socket::UNIX - skipping UNIX proto tests", 8; }; } else { p_c([port => 'foo/bar/unix'], { bind => [{host => '*', port => 'foo/bar', proto => 'unix', ipv => '*'}], }); p_c([port => '/foo/bar|unix', udp_recv_len => 500], { bind => [{host => '*', port => '/foo/bar', proto => 'unix', ipv => '*'}], sock => [{NS_host => '*', NS_port => '/foo/bar', NS_proto => 'UNIX', NS_ipv => '*', NS_listen => Socket::SOMAXCONN(), NS_unix_type => 'SOCK_STREAM', NS_unix_path => '/foo/bar'}], }); p_c([port => '/foo/bar|unixdgram', udp_recv_len => 500], { bind => [{host => '*', port => '/foo/bar', proto => 'unixdgram', ipv => '*'}], sock => [{NS_host => '*', NS_port => '/foo/bar', NS_proto => 'UNIXDGRAM', NS_recv_len => 500, NS_recv_flags => 0, NS_unix_type => 'SOCK_DGRAM', NS_ipv => '*'}], }); p_c([port => 'foo/bar|sock_dgram|unix'], { bind => [{host => '*', port => 'foo/bar', proto => 'unix', unix_type => 'sock_dgram', ipv => '*'}], }); p_c([port => {port => '/foo/bar', proto => 'unix', unix_type => 'sock_stream', listen => 7}], { bind => [{host => '*', port => '/foo/bar', proto => 'unix', unix_type => 'sock_stream', listen => 7, ipv => '*'}], sock => [{NS_host => '*', NS_port => '/foo/bar', NS_proto => 'UNIX', NS_unix_type => 'SOCK_STREAM', NS_listen => 7, NS_ipv => '*', NS_unix_path => '/foo/bar'}], }); p_c([port => {port => '/foo/bar', proto => 'unix', unix_type => 'sock_dgram'}], { bind => [{host => '*', port => '/foo/bar', proto => 'unix', unix_type => 'sock_dgram', ipv => '*'}], }); p_c([port => {port => '/foo/bar', proto => 'unixdgram'}], { bind => [{host => '*', port => '/foo/bar', proto => 'unixdgram', ipv => '*'}], }); p_c([port => 'foo/bar/unix', ipv => "*"], { bind => [{host => '*', port => 'foo/bar', proto => 'unix', ipv => '*'}], }); } ###----------------------------------------------------------------### # ssl if (!eval { require Net::SSLeay; 1 }) { my $err = $@; SKIP: { skip "Cannot load Net::SSLeay - skipping SSLEAY proto tests", 3; }; } elsif (!eval { IO::Socket::INET->new->configure({LocalPort => 20203, Proto => 'tcp', Listen => 1, ReuseAddr => 1}) or die; }) { chomp(my $err = $@); SKIP: { skip "Cannot load Socket6 libraries - skipping IPv6 proto tests ($err)", 3; }; } else { local $ENV{'IPV'} = 4; # pretend to be on a system without IPv6 p_c([proto => 'ssleay'], { bind => [{host => '*', port => Net::Server::default_port(), proto => 'ssleay', ipv => 4}], sock => [{NS_host => '*', NS_port => 20203, NS_proto => 'SSLEAY', NS_ipv => 4, NS_listen => eval { Socket::SOMAXCONN() }, SSL_cert_file => FooServer::SSL_cert_file()}], }); %class_m = (); # setting SSL_key_file may dynamically change the package methods p_c([port => '20203/ssleay', listen => 4, SSL_key_file => "foo/bar"], { bind => [{host => '*', port => 20203, proto => 'ssleay', ipv => 4}], sock => [{NS_host => '*', NS_port => 20203, NS_proto => 'SSLEAY', NS_ipv => 4, NS_listen => 4, SSL_key_file => "foo/bar", SSL_cert_file => FooServer::SSL_cert_file()}], }); %class_m = (); # setting SSL_key_file may dynamically change the package methods p_c([port => {port => '20203', proto => 'ssleay', listen => 6, SSL_key_file => "foo/bar"}], { bind => [{host => '*', port => 20203, proto => 'ssleay', listen => 6, SSL_key_file => "foo/bar", ipv => 4}], sock => [{NS_host => '*', NS_port => 20203, NS_proto => 'SSLEAY', NS_ipv => 4, NS_listen => 6, SSL_key_file => "foo/bar", SSL_cert_file => FooServer::SSL_cert_file()}], }); } if (!eval { require IO::Socket::SSL }) { SKIP: { skip "Cannot load Net::SSLeay - skipping SSLEAY proto tests", 1; }; } elsif (!eval { IO::Socket::INET->new->configure({LocalPort => 20203, Proto => 'tcp', Listen => 1, ReuseAddr => 1}) or die; }) { chomp(my $err = $@); SKIP: { skip "Cannot load Socket6 libraries - skipping IPv6 proto tests ($err)", 1; }; } else { local $ENV{'IPV'} = 4; # pretend to be on a system without IPv6 p_c([proto => 'ssl'], { bind => [{host => '*', port => Net::Server::default_port(), proto => 'ssl', ipv => 4}], sock => [{NS_host => '*', NS_port => 20203, NS_proto => 'SSL', NS_ipv => 4, NS_listen => eval { Socket::SOMAXCONN() }, SSL_cert_file => FooServer::SSL_cert_file()}], }); } ###----------------------------------------------------------------### # ipv6 if (!eval { require Socket6; require IO::Socket::INET6; IO::Socket::INET6->new->configure({LocalPort => 20203, Proto => 'tcp', Listen => 1, ReuseAddr => 1, Domain => Socket6::AF_INET6()}) or die; IO::Socket::INET6->new->configure({LocalAddr => '::1', LocalPort => 20203, Proto => 'tcp', Listen => 1, ReuseAddr => 1, Domain => Socket6::AF_INET6()}) or die; IO::Socket::INET6->new->configure({LocalAddr => 'localhost', LocalPort => 20203, Proto => 'tcp', Listen => 1, ReuseAddr => 1, Domain => Socket6::AF_INET6()}) or die; }) { chomp(my $err = $@); SKIP: { skip "Cannot load Socket6 libraries - skipping IPv6 proto tests ($err)", 13; }; } else { local $ENV{'IPV'} = 4; # skew the default back to 4 for now p_c([port => 20201], { bind => [{host => '*', port => 20201, proto => 'tcp', ipv => 4}], # still defaults off even with library loaded sock => [{NS_host => '*', NS_port => 20201, NS_proto => 'TCP', NS_ipv => 4, NS_listen => eval { Socket::SOMAXCONN() }}], }); p_c([port => 20201, ipv => 6], { # explicit request bind => [{host => '*', port => 20201, proto => 'tcp', ipv => 6}], sock => [{NS_host => '*', NS_port => 20201, NS_proto => 'TCP', NS_ipv => 6, NS_listen => eval { Socket::SOMAXCONN() }}], }); p_c([port => [{port => 20201, ipv => 6}]], { bind => [{host => '*', port => 20201, proto => 'tcp', ipv => 6}], }); p_c([port => '[*]:20201:IPv6'], { bind => [{host => '*', port => 20201, proto => 'tcp', ipv => 6}], sock => [{NS_host => '*', NS_port => 20201, NS_proto => 'TCP', NS_ipv => 6, NS_listen => eval { Socket::SOMAXCONN() }}], }); p_c([port => ['[localhost]:IPv6:20201']], { bind => [{host => 'localhost', port => 20201, proto => 'tcp', ipv => 6}], sock => [{NS_host => 'localhost', NS_port => 20201, NS_proto => 'TCP', NS_ipv => 6, NS_listen => eval { Socket::SOMAXCONN() }}], }); p_c([port => 20201, host => 'localhost/IPv6'], { bind => [{host => 'localhost', port => 20201, proto => 'tcp', ipv => 6}], sock => [{NS_host => 'localhost', NS_port => 20201, NS_proto => 'TCP', NS_ipv => 6, NS_listen => eval { Socket::SOMAXCONN() }}], }); p_c([port => 20201, host => 'localhost', proto => 'udp IPv6'], { bind => [{host => 'localhost', port => 20201, proto => 'udp', ipv => 6}], }); p_c([port => ['[localhost]:20201:IPv4', 'localhost:20201:IPv6']], { bind => [{host => 'localhost', port => 20201, proto => 'tcp', ipv => 4}, {host => 'localhost', port => 20201, proto => 'tcp', ipv => 6}], sock => [{NS_host => 'localhost', NS_port => 20201, NS_proto => 'TCP', NS_ipv => 4, NS_listen => eval { Socket::SOMAXCONN() }}, {NS_host => 'localhost', NS_port => 20201, NS_proto => 'TCP', NS_ipv => 6, NS_listen => eval { Socket::SOMAXCONN() }}], }); p_c([port => 'localhost, 20201, IPv6, IPv4'], { bind => [{host => 'localhost', port => 20201, proto => 'tcp', ipv => 6}, {host => 'localhost', port => 20201, proto => 'tcp', ipv => 4}], }); p_c([port => [{port => '20201', host => 'localhost', ipv => [6, 4]}]], { bind => [{host => 'localhost', port => 20201, proto => 'tcp', ipv => 6}, {host => 'localhost', port => 20201, proto => 'tcp', ipv => 4}], }); p_c([port => 'localhost, 20201', ipv => 'IPv4, IPv6'], { bind => [{host => 'localhost', port => 20201, proto => 'tcp', ipv => 6}, {host => 'localhost', port => 20201, proto => 'tcp', ipv => 4}], }); p_c([port => [{port => '20201', host => 'localhost', ipv => 'IPv6, IPv4'}]], { bind => [{host => 'localhost', port => 20201, proto => 'tcp', ipv => 6}, {host => 'localhost', port => 20201, proto => 'tcp', ipv => 4}], }); p_c([port => 20201, host => '::1', ipv => '*'], { bind => [{host => '::1', port => 20201, proto => 'tcp', ipv => 6}], }); #p_c([port => 20201, host => 'localhost', ipv => '*'], { # bind => [{host => '::1', port => 20201, proto => 'tcp', ipv => 6}, {host => '127.0.0.1', port => 20201, proto => 'tcp', ipv => 4}], #}); # #p_c([port => 20201, host => 'localhost IPv*'], { # bind => [{host => '::1', port => 20201, proto => 'tcp', ipv => 6}, {host => '127.0.0.1', port => 20201, proto => 'tcp', ipv => 4}], #}); # #p_c([port => 20201, host => '*', ipv => '*'], { # BSD will have two by default, linux has 1 # bind => [{host => '::', port => 20201, proto => 'tcp', ipv => 6}], #}); # #delete $ENV{'IPV'}; #p_c([port => 20201], { # BSD will have two by default, linux has 1 # bind => [{host => '::', port => 20201, proto => 'tcp', ipv => 6}], #}); } Net-Server-2.008/t/Server_PreFork.t0000644000175000017500000000443212331755703015540 0ustar paulpaul#!/usr/bin/perl package Net::Server::Test; use strict; use FindBin qw($Bin); use lib $Bin; use NetServerTest qw(prepare_test ok use_ok diag); my $env = prepare_test({n_tests => 5, start_port => 20600, n_ports => 2}); # runs three of its own tests use_ok('Net::Server::PreFork'); @Net::Server::Test::ISA = qw(Net::Server::PreFork); sub accept { $env->{'signal_ready_to_test'}->(); return shift->SUPER::accept(@_); } my $ok = eval { local $SIG{'ALRM'} = sub { die "Timeout\n" }; alarm $env->{'timeout'}; my $ppid = $$; my $pid = fork; die "Trouble forking: $!" if ! defined $pid; ### parent does the client if ($pid) { local $SIG{'ALRM'} = sub { die "Timed out waiting for server\n" }; alarm $env->{'timeout'}; $env->{'block_until_ready_to_test'}->(); my $remote = NetServerTest::client_connect(PeerAddr => $env->{'hostname'}, PeerPort => $env->{'ports'}->[0]) || die "Couldn't open child to sock: $!"; my $line = <$remote>; die "Didn't get the type of line we were expecting: ($line)" if $line !~ /Net::Server/; print $remote "quit\n"; $remote = NetServerTest::client_connect(PeerAddr => $env->{'hostname'}, PeerPort => $env->{'ports'}->[1]) || die "Couldn't open child to sock: $!"; $line = <$remote>; die "Didn't get the type of line we were expecting: ($line)" if $line !~ /Net::Server/; print $remote "exit\n"; alarm 0; return 1; ### child does the server } else { eval { alarm $env->{'timeout'}; close STDERR; Net::Server::Test->run( port => $env->{'ports'}->[0], port => "$env->{'hostname'}:$env->{'ports'}->[1]", host => $env->{'hostname'}, ipv => $env->{'ipv'}, min_servers => 1, min_spare_servers => 0, max_requests => 2, child_communication => 1, background => 0, setsid => 0, ); } || do { diag("Trouble running server: $@"); kill(9, $ppid) && ok(0, "Failed during run of server"); }; exit; } alarm(0); }; alarm(0); ok($ok, "Got the correct output from the server") || diag("Error: $@"); Net-Server-2.008/t/Server_Fork.t0000644000175000017500000000333212331755703015067 0ustar paulpaul#!/usr/bin/perl package Net::Server::Test; use strict; use FindBin qw($Bin); use lib $Bin; use NetServerTest qw(prepare_test ok use_ok diag); my $env = prepare_test({n_tests => 5, start_port => 20200, n_ports => 1}); # runs three of its own tests use_ok('Net::Server::Fork'); @Net::Server::Test::ISA = qw(Net::Server::Fork); sub accept { my $self = shift; exit if $^O eq 'MSWin32' && $self->{'__one_accept_only'}++; $env->{'signal_ready_to_test'}->(); return $self->SUPER::accept(@_); } my $ok = eval { local $SIG{'ALRM'} = sub { die "Timeout\n" }; alarm $env->{'timeout'}; my $ppid = $$; my $pid = fork; die "Trouble forking: $!" if ! defined $pid; ### parent does the client if ($pid) { $env->{'block_until_ready_to_test'}->(); my $remote = NetServerTest::client_connect(PeerAddr => $env->{'hostname'}, PeerPort => $env->{'ports'}->[0]) || die "Couldn't open child to sock: $!"; my $line = <$remote>; die "Didn't get the type of line we were expecting: ($line)" if $line !~ /Net::Server/; print $remote "exit\n"; return 1; ### child does the server } else { eval { alarm $env->{'timeout'}; close STDERR; Net::Server::Test->run( port => $env->{'ports'}->[0], host => $env->{'hostname'}, ipv => $env->{'ipv'}, background => 0, setsid => 0, ); } || do { diag("Trouble running server: $@"); kill(9, $ppid) && ok(0, "Failed during run of server"); }; exit; } alarm(0); }; alarm(0); ok($ok, "Got the correct output from the server") || diag("Error: $@"); Net-Server-2.008/t/UNIX_test.t0000644000175000017500000000453612331755703014471 0ustar paulpaul#!/usr/bin/perl package Net::Server::Test; use strict; use POSIX qw(tmpnam); use English qw($UID $GID); use FindBin qw($Bin); use lib $Bin; use NetServerTest qw(prepare_test ok use_ok diag skip); my $env = prepare_test({n_tests => 5, start_port => 20800, n_ports => 1}); # runs three of its own tests if ($^O eq 'MSWin32') { SKIP: { skip("UNIX Sockets will not work on Win32", 2) }; exit; } use_ok('Net::Server'); @Net::Server::Test::ISA = qw(Net::Server); sub accept { $env->{'signal_ready_to_test'}->(); return shift->SUPER::accept(@_); } my $socket_file = tmpnam; # must do before fork my $ok = eval { local $SIG{'ALRM'} = sub { die "Timeout\n" }; alarm $env->{'timeout'}; my $ppid = $$; my $pid = fork; die "Trouble forking: $!" if ! defined $pid; ### parent does the client if ($pid) { $env->{'block_until_ready_to_test'}->(); ### connect to child under unix my $remote = IO::Socket::UNIX->new(Peer => $socket_file); die "No socket returned [$!]" if ! defined $remote; my $line = <$remote>; die "Didn't get the type of line we were expecting: ($line)" if $line !~ /Net::Server/; print $remote "quite\n"; ### connect to child under tcp $remote = NetServerTest::client_connect( PeerAddr => $env->{'hostname'}, PeerPort => $env->{'ports'}->[0], Proto => 'tcp') || die "Couldn't open to sock: $!"; $line = <$remote>; die "Didn't get the type of line we were expecting: ($line)" if $line !~ /Net::Server/; print $remote "exit\n"; return 1; ### child does the server } else { eval { close STDERR; Net::Server::Test->run( port => "$env->{'ports'}->[0]/tcp", port => "$socket_file|unix", user => $UID, # user accepts id as well group => $GID, # group accepts id as well host => $env->{'hostname'}, ipv => $env->{'ipv'}, background => 0, setsid => 0, ); } || do { diag("Trouble running server: $@"); kill(9, $ppid) && ok(0, "Failed during run of server"); }; exit; } alarm(0); }; alarm(0); ok($ok, "Got the correct output from the server") || diag("Error: $@"); Net-Server-2.008/t/SSL_test.t0000644000175000017500000001041312332711132014323 0ustar paulpaul#!/usr/bin/perl package Net::Server::Test; use strict; use FindBin qw($Bin); use lib $Bin; use NetServerTest qw(prepare_test ok use_ok diag skip); my $env = prepare_test({n_tests => 5, start_port => 20200, n_ports => 1}); # runs three of its own tests if (! eval { require File::Temp } || ! eval { require IO::Socket::SSL } ) { SKIP: { skip("Cannot load IO::Socket::SSL libraries to test Socket SSL server: $@", 2); }; exit; } my $pem = << 'PEM'; # this certificate is invalid, please only use for testing -----BEGIN CERTIFICATE----- MIICKTCCAZICCQDFxHnOjdmTTjANBgkqhkiG9w0BAQUFADBZMQswCQYDVQQGEwJB VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 cyBQdHkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMTIwMTE0MTgzMjMwWhcN NzUxMTE0MTIwNDE0WjBZMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0 ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRIwEAYDVQQDDAls b2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKLGfQantHdi/0cd eoOHRbWKChpI/g84hU8SnwmrSMZR0x76vDLKMDYohISoKxRPx6j2M2x3P4K+kEJm C5H9iGdD9p9ljGnRdkGp5yYeuwWfePRb4AOwP5qgQtEb0OctFIMjcAIIAw/lsnUs hGnom0+uA9W2H63PgO0o4qiVAn7NAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEATDGA dYRl5wpsYcpLgNzu0M4SENV0DAE2wNTZ4LIR1wxHbcxdgzMhjp0wwfVQBTJFNqWu DbeIFt4ghPMsUQKmMc4+og2Zyll8qev8oNgWQneKjDAEKKpzdvUoRZyGx1ZocGzi S4LDiMd4qhD+GGePcHwmR8x/okoq58xZO/+Qygc= -----END CERTIFICATE----- -----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQCixn0Gp7R3Yv9HHXqDh0W1igoaSP4POIVPEp8Jq0jGUdMe+rwy yjA2KISEqCsUT8eo9jNsdz+CvpBCZguR/YhnQ/afZYxp0XZBqecmHrsFn3j0W+AD sD+aoELRG9DnLRSDI3ACCAMP5bJ1LIRp6JtPrgPVth+tz4DtKOKolQJ+zQIDAQAB AoGASXDmvhbyfJ8k8HAjc66XzBWxAzUFs9Zbh1aufM1UM259o8+bFAtXf0f+ql+5 uBtaySf0Aa8374SNT/f8pmzOmpiXMvYRz8Z5Gc6JYpYd/PrCoSCGtP+NdCvk7Y5c eUmmpiEto4+fgCAKrtqc5jm8eBWn/yNhQNDBVJ9qX+kXQOECQQDVBLvBZaECSMTm djKuPlZ93cmyI7g+TURTl2N08fz4xQVVbo5+AV0GsEZupBpTgrHpLTk8gKP/nfdR 9KWZldbZAkEAw55+SqrVTv4cI0fMvC0t8Wl46zTkY9tK65TGnbO1DbTQh9qs+NwH +v3uu47ef5w/73xLtDjQouz//0z5rgF3FQJAfrmOKQOYwY8g9CmlBNu5ALAM6Zku ZoH4//G0DUJYyHYNMkHPK08MVIpRnEisELpTtPBeeIvfBJapJ2xvh+sIIQJASeY4 I5EB4EOS8akQKQ6QSqDjs0dZ+HdBiFm95pmbDkB+frQXoDPPN/xyEZzZZS/r31b/ amgEOWh7FUFJGXkoOQJBALfOgsiss0lASlOXAg1rwO4m2OaDiaEde01PLcSjIaKl Qfbzc7ZYF+fGDsHHlD5Kgj1CGaWCVVHqCv4UHSrA/gM= -----END RSA PRIVATE KEY----- PEM my ($pem_fh, $pem_filename) = File::Temp::tempfile(SUFFIX => '.pem', UNLINK => 1); print $pem_fh $pem; $pem_fh->close; use_ok qw(Net::Server::Proto::SSL) or exit; require Net::Server; @Net::Server::Test::ISA = qw(Net::Server); sub accept { my $self = shift; exit if $^O eq 'MSWin32' && $self->{'__one_accept_only'}++; $env->{'signal_ready_to_test'}->(); return $self->SUPER::accept(@_); } my $ok = eval { local $SIG{'ALRM'} = sub { die "Timeout\n" }; alarm $env->{'timeout'}; my $ppid = $$; my $pid = fork; die "Trouble forking: $!" if ! defined $pid; ### parent does the client if ($pid) { $env->{'block_until_ready_to_test'}->(); my $mode = eval { IO::Socket::SSL_VERIFY_NONE() }; $mode = 0 if ! defined $mode; my $remote = IO::Socket::SSL->new( PeerAddr => $env->{'hostname'}, PeerPort => $env->{'ports'}->[0], SSL_verify_mode => $mode, ) || die "Couldn't open child to sock: $!"; my $line = <$remote>; die "Didn't get the type of line we were expecting: ($line)" if $line !~ /Net::Server/; diag $line; print $remote "exit\n"; my $line2 = <$remote>; diag $line2; return 1; ### child does the server } else { eval { alarm $env->{'timeout'}; close STDERR; my $s = Net::Server::Test->run( host => $env->{'hostname'}, port => $env->{'ports'}->[0], proto => 'ssl', ipv => '*', # $env->{'ipv'}, # IO::Socket::SSL always tries INET6 if it is available so we should listen on 6 if it is available SSL_cert_file => $pem_filename, SSL_key_file => $pem_filename, background => 0, setsid => 0, ); } || do { diag("Trouble running server: $@"); kill(9, $ppid) && ok(0, "Failed during run of server"); }; exit; } alarm(0); }; alarm(0); ok($ok, "Got the correct output from the server") || diag("Error: $@"); Net-Server-2.008/t/Server_Multiplex.t0000644000175000017500000000502112331755703016146 0ustar paulpaul#!/usr/bin/perl package Net::Server::Test; use strict; use FindBin qw($Bin); use lib $Bin; use NetServerTest qw(prepare_test ok use_ok diag skip); my $env = prepare_test({n_tests => 5, start_port => 20200, n_ports => 1}); if (! eval{ require IO::Multiplex; }) { diag("Error loading IO::Multiplex: $@"); SKIP: { skip("No IO::Multiplex installed\n", 2) }; exit; } use_ok('Net::Server::Multiplex'); @Net::Server::Test::ISA = qw(Net::Server::Multiplex); ### Make post_bind_hook notify the client that ### the server is ready to accept connections. sub post_bind_hook { $env->{'signal_ready_to_test'}->() } sub mux_connection { my $self = shift; shift; shift; # These two args are boring print "Welcome to \"".ref($self)."\" ($$)\n"; } sub mux_input { my $self = shift; my $mux = shift; my $fh = shift; my $data = shift; # Scalar reference to the input # Process each line in the input, leaving partial lines # in the input buffer while ($$data =~ s/^(.*?\n)//) { $_ = $1; s/\r?\n$//; print ref($self),":$$: You said \"$_\"\r\n"; $self->log(5,$_); # very verbose log if( /get (\w+)/ ){ print "$1: $self->{net_server}->{server}->{$1}\r\n"; } if( /exit/ ){ $self->{net_server}->{mux}->endloop; } } } my $ok = eval { local $SIG{'ALRM'} = sub { die "Timeout\n" }; alarm $env->{'timeout'}; my $ppid = $$; my $pid = fork; die "Trouble forking: $!" if ! defined $pid; ### parent does the client if ($pid) { $env->{'block_until_ready_to_test'}->(); my $remote = NetServerTest::client_connect(PeerAddr => $env->{'hostname'}, PeerPort => $env->{'ports'}->[0]) || die "Couldn't open child to sock: $!"; my $line = <$remote>; die "Didn't get the type of line we were expecting: ($line)" if $line !~ /Net::Server/; print $remote "exit\n"; return 1; ### child does the server } else { eval { alarm $env->{'timeout'}; close STDERR; Net::Server::Test->run( port => $env->{'ports'}->[0], host => $env->{'hostname'}, ipv => $env->{'ipv'}, background => 0, setsid => 0, ); } || do { diag("Trouble running server: $@"); kill(9, $ppid) && ok(0, "Failed during run of server"); }; exit; } alarm(0); }; alarm(0); ok($ok, "Got the correct output from the server") || diag("Error: $@"); Net-Server-2.008/t/UDP_test.t0000644000175000017500000000431012331755703014324 0ustar paulpaul#!/usr/bin/perl package Net::Server::Test; use strict; use FindBin qw($Bin); use lib $Bin; use NetServerTest qw(prepare_test ok use_ok diag); my $env = prepare_test({n_tests => 5, start_port => 20700, n_ports => 2}); # runs three of its own tests use_ok('Net::Server'); @Net::Server::Test::ISA = qw(Net::Server); sub accept { $env->{'signal_ready_to_test'}->(); return shift->SUPER::accept(@_); } my $ok = eval { local $SIG{'ALRM'} = sub { die "Timeout\n" }; alarm $env->{'timeout'}; my $ppid = $$; my $pid = fork; die "Trouble forking: $!" if ! defined $pid; ### parent does the client if ($pid) { $env->{'block_until_ready_to_test'}->(); ### connect to child under udp my $remote = NetServerTest::client_connect( PeerAddr => $env->{'hostname'}, PeerPort => $env->{'ports'}->[0], Proto => 'udp'); ### send a packet, get a packet $remote->send("Are you there?",0); my $data = undef; $remote->recv($data, 4096, 0); die "No data returned" if ! defined $data; die "Didn't get the data we wanted" if $data !~ /Are you there/; ### connect to child under tcp $remote = NetServerTest::client_connect( PeerAddr => $env->{'hostname'}, PeerPort => $env->{'ports'}->[0], Proto => 'tcp') || die "Couldn't open to sock: $!"; my $line = <$remote>; die "Didn't get the type of line we were expecting: ($line)" if $line !~ /Net::Server/; print $remote "exit\n"; return 1; ### child does the server } else { eval { close STDERR; Net::Server::Test->run( port => "$env->{'ports'}->[0]/tcp", port => "$env->{'ports'}->[0]/udp", host => $env->{'hostname'}, ipv => $env->{'ipv'}, background => 0, setsid => 0, ); } || do { diag("Trouble running server: $@"); kill(9, $ppid) && ok(0, "Failed during run of server"); }; exit; } alarm(0); }; alarm(0); ok($ok, "Got the correct output from the server") || diag("Error: $@"); Net-Server-2.008/t/Options.t0000644000175000017500000002457112331755703014303 0ustar paulpaul#!/usr/bin/perl =head1 NAME Options.t - Test commandline options and such =cut package FooServer; use strict; use FindBin qw($Bin); use lib $Bin; use NetServerTest qw(prepare_test ok is use_ok skip like); prepare_test({n_tests => 73, plan_only => 1}); use_ok('Net::Server'); @FooServer::ISA = qw(Net::Server); ### override-able options for this package sub options { my $self = shift; my $prop = $self->{'server'}; my $template = shift; ### setup options in the parent classes $self->SUPER::options($template); $template->{'my_option'} = \$prop->{'my_option'}; $prop->{'an_arrayref_item'} ||= []; $template->{'an_arrayref_item'} = $prop->{'an_arrayref_item'}; } ### provide default values sub default_values { return { group => 'defaultgroup', allow => ['127.0.0.1', '192.169.0.1'], }; } #sub proto_object { # my ($self, $host, $port, $proto) = @_; # #debug $host, $port, $proto; # #return $self->SUPER::proto_object($host, $port, $proto); # return "Blah"; #} ### override these to make run not run ### this will allow all configuration cycles to be run sub bind {} sub post_bind {} sub loop {} sub log {} sub server_close { my $self = shift; return $self; } ###----------------------------------------------------------------### my $obj = eval { FooServer->new }; ok($obj, "Got an object ($@)"); my $server = eval { FooServer->run }; ok($server, "Got a server ($@)"); my $prop = eval { $server->{'server'} } || {}; is($prop->{'log_level'}, 2, "Correct default log_level"); is($prop->{'log_file'}, "", "Correct default log_file"); ok(! $prop->{'user'}, "Correct default user"); my $configured_ports = scalar(@{ $prop->{'_bind'} }); ok($configured_ports == 1 || $configured_ports == 2, "Had correct configured ports ($configured_ports)"); my @socks = @{ $prop->{'sock'} }; is(scalar(@socks), scalar(@{ $prop->{'_bind'} }), "Sockets matched ports"); my $sock = $socks[0]; if ($sock->NS_ipv == 4) { is(eval { $sock->NS_host }, '0.0.0.0', "Right host"); } else { is(eval { $sock->NS_host }, '::', "Right host"); } is(eval { $sock->NS_port }, 20203, "Right port"); is(eval { $sock->NS_proto }, 'TCP', "Right proto"); ###----------------------------------------------------------------### $prop = eval { FooServer->run(port => 2201)->{'server'} }; ok($prop, "Loaded server"); $prop ||= {}; is(scalar(@{ $prop->{'port'} }), 1, "Had 1 configured ports"); is($prop->{'port'}->[0], 2201, "Right port"); ###----------------------------------------------------------------### $prop = eval { FooServer->run(port => [2201, 2202])->{'server'} }; ok($prop, "Loaded server"); $prop ||= {}; is(scalar(@{ $prop->{'port'} }), 2, "Had 1 configured ports"); is($prop->{'port'}->[0], 2201, "Right port"); is($prop->{'port'}->[1], 2202, "Right port"); ###----------------------------------------------------------------### $prop = eval { FooServer->run({port => 2201})->{'server'} }; ok($prop, "Loaded server"); $prop ||= {}; is(scalar(@{ $prop->{'port'} }), 1, "Had 1 configured ports"); is($prop->{'port'}->[0], 2201, "Right port"); ###----------------------------------------------------------------### $prop = eval { FooServer->new(port => 2201)->run->{'server'} }; ok($prop, "Loaded server"); $prop ||= {}; is(scalar(@{ $prop->{'port'} }), 1, "Had 1 configured ports"); is($prop->{'port'}->[0], 2201, "Right port"); ###----------------------------------------------------------------### $prop = eval { FooServer->new({port => 2201})->run->{'server'} }; ok($prop, "Loaded server"); $prop ||= {}; is(scalar(@{ $prop->{'port'} }), 1, "Had 1 configured ports"); is($prop->{'port'}->[0], 2201, "Right port"); ###----------------------------------------------------------------### $prop = eval { local @ARGV = ('--port', '2201'); FooServer->run->{'server'} }; ok($prop, "Loaded server"); $prop ||= {}; is(scalar(@{ $prop->{'port'} }), 1, "Had 1 configured ports"); is($prop->{'port'}->[0], 2201, "Right port"); ###----------------------------------------------------------------### $prop = eval { local @ARGV = ('--port', '2201', '--port=2202'); FooServer->run->{'server'} }; ok($prop, "Loaded server"); $prop ||= {}; is(scalar(@{ $prop->{'port'} }), 2, "Had 1 configured ports"); is($prop->{'port'}->[0], 2201, "Right port"); is($prop->{'port'}->[1], 2202, "Right port"); ###----------------------------------------------------------------### $prop = eval { FooServer->run(conf_file => __FILE__.'.conf')->{'server'} }; ok($prop, "Loaded server"); $prop ||= {}; is(scalar(@{ $prop->{'port'} }), 3, "Had 1 configured ports"); is($prop->{'port'}->[0], 5401, "Right port"); is($prop->{'port'}->[1], 5402, "Right port"); is($prop->{'port'}->[2], 5403, "Right port"); is($prop->{'user'}, 'foo', "Right user"); ###----------------------------------------------------------------### $prop = eval { local @ARGV = ('--user=cmdline'); FooServer->run(conf_file => __FILE__.'.conf', user => 'runargs')->{'server'} }; ok($prop, "Loaded server"); $prop ||= {}; is($prop->{'user'}, 'cmdline', "Right user \"$prop->{'user'}\""); ###----------------------------------------------------------------### $prop = eval { FooServer->run(conf_file => __FILE__.'.conf', user => 'runargs')->{'server'} }; ok($prop, "Loaded server"); $prop ||= {}; is($prop->{'user'}, 'runargs', "Right user \"$prop->{'user'}\""); ###----------------------------------------------------------------### $prop = eval { FooServer->run(my_option => 'wow')->{'server'} }; ok($prop, "Loaded server"); $prop ||= {}; is($prop->{'my_option'}, 'wow', 'Could use custom options'); ###----------------------------------------------------------------### $prop = eval { FooServer->run(an_arrayref_item => 'wow')->{'server'} }; ok($prop, "Loaded server"); $prop ||= {}; is(scalar(@{ $prop->{'an_arrayref_item'} }), 1, "Had 1 configured custom array option"); is($prop->{'an_arrayref_item'}->[0], 'wow', "Right value"); ###----------------------------------------------------------------### $prop = eval { FooServer->run(conf_file => __FILE__.'.conf', user => 'runargs')->{'server'} }; ok($prop, "Loaded server"); $prop ||= {}; is($prop->{'my_option'}, 'bar', "Right my_option \"$prop->{'my_option'}\""); is(scalar(@{ $prop->{'an_arrayref_item'} }), 3, "Had 3 configured custom array option"); is($prop->{'an_arrayref_item'}->[0], 'one', "Right value"); is($prop->{'an_arrayref_item'}->[1], 'three', "Right value"); is($prop->{'an_arrayref_item'}->[2], 'two', "Right value"); ###----------------------------------------------------------------### $prop = eval { local @ARGV = ('--group=cmdline'); FooServer->run(conf_file => __FILE__.'.conf', group => 'runargs')->{'server'} }; ok($prop, "Loaded server"); $prop ||= {}; is($prop->{'group'}, 'cmdline', "Right group \"$prop->{'group'}\""); ###----------------------------------------------------------------### $prop = eval { FooServer->run(conf_file => __FILE__.'.conf', group => 'runargs')->{'server'} }; ok($prop, "Loaded server"); $prop ||= {}; is($prop->{'group'}, 'runargs', "Right group \"$prop->{'group'}\""); ###----------------------------------------------------------------### $prop = eval { FooServer->run(conf_file => __FILE__.'.conf')->{'server'} }; ok($prop, "Loaded server"); $prop ||= {}; is($prop->{'group'}, 'confgroup', "Right group \"$prop->{'group'}\""); ###----------------------------------------------------------------### $prop = eval { FooServer->run->{'server'} }; ok($prop, "Loaded server"); $prop ||= {}; is($prop->{'group'}, 'defaultgroup', "Right group \"$prop->{'group'}\""); is(scalar(@{ $prop->{'allow'} }), 2, "Defaults for allow are set also"); ###----------------------------------------------------------------### { package BarServer; @BarServer::ISA = qw(FooServer); sub default_values { return { conf_file => __FILE__.'.conf' }; } } $prop = eval { BarServer->run->{'server'} }; $prop ||= {}; is($prop->{'group'}, 'confgroup', "Right group \"$prop->{'group'}\""); ###----------------------------------------------------------------### $prop = eval { FooServer->new({ conf_file => __FILE__.'.conf', # arguments passed to new win })->run({ conf_file => 'somefile_that_doesnot_exist', })->{'server'} }; $prop ||= {}; is($prop->{'group'}, 'confgroup', "Right group \"$prop->{'group'}\""); ###----------------------------------------------------------------### if (!$ENV{'TEST_LOG4PERL'}) { SKIP: { skip("TEST_LOG4PERL not set - skipping Log::Log4perl tests", 7) }; } elsif (!eval { require Log::Log4perl; require File::Temp }) { SKIP: { skip("Log::Log4perl not installed: $@", 7) }; } else { $prop = eval { FooServer->run( log_file => "Log::Log4perl" ) }; like("$@", qr/Must specify a log4perl_conf file/, "Got error due to missing log4perl_conf"); my ($log_fh, $log4perl_file) = File::Temp::tempfile(SUFFIX => '.log', UNLINK => 1); unlink $log4perl_file; my $conf = << "EOF"; log4perl.logger.tester = WARN, FileAppndr1 log4perl.appender.FileAppndr1 = Log::Log4perl::Appender::File log4perl.appender.FileAppndr1.filename = ${log4perl_file} log4perl.appender.FileAppndr1.layout = Log::Log4perl::Layout::SimpleLayout EOF my ($conf_fh, $conf_file) = File::Temp::tempfile(SUFFIX => '.log4perl', UNLINK => 1); print $conf_fh $conf; close $conf_fh; # This log file is same as specified in Options.t.log4perl open my $old_stdout, ">&", STDOUT; # save this off because setting a log_file is going to force close STDIN and STDOUT $prop = eval { FooServer->run( log_file => "Log::Log4perl", log4perl_conf => $conf_file, log4perl_logger => "tester", )->{'server'} }; my $err = "$@"; open STDOUT, ">&", $old_stdout; # restore it # There was a test for a bad log4perl_conf file, but log4perl only allows you to initialise once # so subsequent initialisations always had the bad filename #like( $@, qr/Cannot open config file '.*?'/, "Got error due to missing log4perl_conf file" ); ok(!$err, "No Log4perl errors"); is(ref($prop->{'log_function'}), "CODE", "Log4perl initialised with function created"); ok(-e $log4perl_file, "Log file $log4perl_file found"); ok(! -s $log4perl_file, "Log file is 0 bytes"); $prop->{'log_function'}->(1, "A test message"); ok(-s $log4perl_file, "Log file now has data"); open my $fh, '<', $log4perl_file; my $data = <$fh>; is($data, "ERROR - A test message\n", "Got expected log message"); } Net-Server-2.008/t/Options.t.conf0000644000175000017500000000071512331755703015221 0ustar paulpaul# test configuration file used by Options.t # options that are an arrayref in the options template may have multiple values port 5401 port 5402 port 5403 # other options use the last found option user bling user foo group confgroup # custom options may be used if the options method is overridden # scalar options uses last found my_option bim my_option bar # arrayref options take all found an_arrayref_item one an_arrayref_item three an_arrayref_item two Net-Server-2.008/t/Server_PreForkSimple.t0000644000175000017500000000432412331755703016712 0ustar paulpaul#!/usr/bin/perl package Net::Server::Test; use strict; use FindBin qw($Bin); use lib $Bin; use NetServerTest qw(prepare_test ok use_ok diag); my $env = prepare_test({n_tests => 5, start_port => 20500, n_ports => 2}); # runs three of its own tests use_ok('Net::Server::PreForkSimple'); @Net::Server::Test::ISA = qw(Net::Server::PreForkSimple); sub accept { $env->{'signal_ready_to_test'}->(); return shift->SUPER::accept(@_); } my $ok = eval { local $SIG{'ALRM'} = sub { die "Timeout\n" }; alarm $env->{'timeout'}; my $ppid = $$; my $pid = fork; die "Trouble forking: $!" if ! defined $pid; ### parent does the client if ($pid) { local $SIG{'ALRM'} = sub { die "Timed out waiting for server\n" }; alarm $env->{'timeout'}; $env->{'block_until_ready_to_test'}->(); my $remote = NetServerTest::client_connect(PeerAddr => $env->{'hostname'}, PeerPort => $env->{'ports'}->[0]) || die "Couldn't open child to sock: $!"; my $line = <$remote>; die "Didn't get the type of line we were expecting: ($line)" if $line !~ /Net::Server/; print $remote "quit\n"; $remote = NetServerTest::client_connect(PeerAddr => $env->{'hostname'}, PeerPort => $env->{'ports'}->[1]) || die "Couldn't open child to sock: $!"; $line = <$remote>; die "Didn't get the type of line we were expecting: ($line)" if $line !~ /Net::Server/; print $remote "exit\n"; alarm 0; return 1; ### child does the server } else { eval { alarm $env->{'timeout'}; close STDERR; Net::Server::Test->run( port => $env->{'ports'}->[0], port => "$env->{'hostname'}:$env->{'ports'}->[1]", host => $env->{'hostname'}, ipv => $env->{'ipv'}, max_servers => 2, max_requests => 2, background => 0, setsid => 0, ); } || do { diag("Trouble running server: $@"); kill(9, $ppid) && ok(0, "Failed during run of server"); }; exit; } alarm(0); }; alarm(0); ok($ok, "Got the correct output from the server") || diag("Error: $@"); Net-Server-2.008/t/SSLEAY_test.t0000644000175000017500000001342112331755703014677 0ustar paulpaul#!/usr/bin/perl package Net::Server::Test; use strict; use FindBin qw($Bin); use lib $Bin; use NetServerTest qw(prepare_test ok use_ok diag skip); my $env = prepare_test({n_tests => 4, start_port => 20200, n_ports => 2}); # runs three of its own tests if (! eval { require File::Temp } || ! eval { require Net::SSLeay } ) { SKIP: { skip("Cannot load Net::SSleay libraries to test Socket SSL server: $@", 1); }; exit; } if (! eval { require Net::Server::Proto::SSLEAY }) { diag "Cannot load SSLEAY library on this platform: $@"; SKIP: { skip("Skipping tests on this platform", 1); }; exit; } my $pem = << 'PEM'; # this certificate is invalid, please only use for testing -----BEGIN CERTIFICATE----- MIICKTCCAZICCQDFxHnOjdmTTjANBgkqhkiG9w0BAQUFADBZMQswCQYDVQQGEwJB VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 cyBQdHkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMTIwMTE0MTgzMjMwWhcN NzUxMTE0MTIwNDE0WjBZMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0 ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRIwEAYDVQQDDAls b2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKLGfQantHdi/0cd eoOHRbWKChpI/g84hU8SnwmrSMZR0x76vDLKMDYohISoKxRPx6j2M2x3P4K+kEJm C5H9iGdD9p9ljGnRdkGp5yYeuwWfePRb4AOwP5qgQtEb0OctFIMjcAIIAw/lsnUs hGnom0+uA9W2H63PgO0o4qiVAn7NAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEATDGA dYRl5wpsYcpLgNzu0M4SENV0DAE2wNTZ4LIR1wxHbcxdgzMhjp0wwfVQBTJFNqWu DbeIFt4ghPMsUQKmMc4+og2Zyll8qev8oNgWQneKjDAEKKpzdvUoRZyGx1ZocGzi S4LDiMd4qhD+GGePcHwmR8x/okoq58xZO/+Qygc= -----END CERTIFICATE----- -----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQCixn0Gp7R3Yv9HHXqDh0W1igoaSP4POIVPEp8Jq0jGUdMe+rwy yjA2KISEqCsUT8eo9jNsdz+CvpBCZguR/YhnQ/afZYxp0XZBqecmHrsFn3j0W+AD sD+aoELRG9DnLRSDI3ACCAMP5bJ1LIRp6JtPrgPVth+tz4DtKOKolQJ+zQIDAQAB AoGASXDmvhbyfJ8k8HAjc66XzBWxAzUFs9Zbh1aufM1UM259o8+bFAtXf0f+ql+5 uBtaySf0Aa8374SNT/f8pmzOmpiXMvYRz8Z5Gc6JYpYd/PrCoSCGtP+NdCvk7Y5c eUmmpiEto4+fgCAKrtqc5jm8eBWn/yNhQNDBVJ9qX+kXQOECQQDVBLvBZaECSMTm djKuPlZ93cmyI7g+TURTl2N08fz4xQVVbo5+AV0GsEZupBpTgrHpLTk8gKP/nfdR 9KWZldbZAkEAw55+SqrVTv4cI0fMvC0t8Wl46zTkY9tK65TGnbO1DbTQh9qs+NwH +v3uu47ef5w/73xLtDjQouz//0z5rgF3FQJAfrmOKQOYwY8g9CmlBNu5ALAM6Zku ZoH4//G0DUJYyHYNMkHPK08MVIpRnEisELpTtPBeeIvfBJapJ2xvh+sIIQJASeY4 I5EB4EOS8akQKQ6QSqDjs0dZ+HdBiFm95pmbDkB+frQXoDPPN/xyEZzZZS/r31b/ amgEOWh7FUFJGXkoOQJBALfOgsiss0lASlOXAg1rwO4m2OaDiaEde01PLcSjIaKl Qfbzc7ZYF+fGDsHHlD5Kgj1CGaWCVVHqCv4UHSrA/gM= -----END RSA PRIVATE KEY----- PEM my ($pem_fh, $pem_filename) = File::Temp::tempfile(SUFFIX => '.pem', UNLINK => 1); print $pem_fh $pem; $pem_fh->close; require Net::Server; @Net::Server::Test::ISA = qw(Net::Server); sub accept { my $self = shift; exit if $^O eq 'MSWin32' && $self->{'__one_accept_only'}++; $env->{'signal_ready_to_test'}->(); return $self->SUPER::accept(@_); } sub process_request { my $self = shift; my $client = $self->{'server'}->{'client'}; return $self->SUPER::process_request if $client->NS_port == $env->{'ports'}->[1]; my $offset = 0; my $total = 0; my $buf; # Wait data my $vec = ''; vec($vec, $client->fileno, 1) = 1; until ($buf) { select($vec, undef, undef, undef); $client->sysread(\$buf, 100, $total); } select(undef, $vec, undef, undef); $client->syswrite($buf); $self->server_close; } my $ok = eval { local $SIG{'ALRM'} = sub { die "Timeout\n" }; alarm $env->{'timeout'}; my $ppid = $$; my $pid = fork; die "Trouble forking: $!" if ! defined $pid; ### parent does the client if ($pid) { $env->{'block_until_ready_to_test'}->(); my $remote = NetServerTest::client_connect(PeerAddr => $env->{'hostname'}, PeerPort => $env->{'ports'}->[1]) || die "Couldn't open child to sock: $!"; my $ctx = Net::SSLeay::CTX_new() or Net::SSLeay::die_now("Failed to create SSL_CTX $!"); Net::SSLeay::CTX_set_options($ctx, &Net::SSLeay::OP_ALL) and Net::SSLeay::die_if_ssl_error("ssl ctx set options"); my $ssl = Net::SSLeay::new($ctx) or Net::SSLeay::die_now("Failed to create SSL $!"); Net::SSLeay::set_fd($ssl, $remote->fileno); Net::SSLeay::connect($ssl); my $line = Net::SSLeay::read($ssl); die "Didn't get the type of line we were expecting: ($line)" if $line !~ /Net::Server/; diag $line; Net::SSLeay::write($ssl, "quit\n"); my $line2 = Net::SSLeay::read($ssl); diag $line2; $remote = NetServerTest::client_connect(PeerAddr => $env->{'hostname'}, PeerPort => $env->{'ports'}->[0]) || die "Couldn't open child to sock: $!"; $ctx = Net::SSLeay::CTX_new() or Net::SSLeay::die_now("Failed to create SSL_CTX $!"); Net::SSLeay::CTX_set_options($ctx, &Net::SSLeay::OP_ALL) and Net::SSLeay::die_if_ssl_error("ssl ctx set options"); $ssl = Net::SSLeay::new($ctx) or Net::SSLeay::die_now("Failed to create SSL $!"); Net::SSLeay::set_fd($ssl, $remote->fileno); Net::SSLeay::connect($ssl); Net::SSLeay::write($ssl, "foo bar"); my $res = Net::SSLeay::read($ssl); return $res eq "foo bar"; ### child does the server } else { eval { alarm $env->{'timeout'}; close STDERR; Net::Server::Test->run( host => $env->{'hostname'}, port => $env->{'ports'}, ipv => $env->{'ipv'}, proto => 'ssleay', background => 0, setsid => 0, SSL_cert_file => $pem_filename, SSL_key_file => $pem_filename, ); } || do { diag("Trouble running server: $@"); kill(9, $ppid) && ok(0, "Failed during run of server"); }; exit; } alarm(0); }; alarm(0); ok($ok, "Got the correct output from the server") || diag("Error: $@"); Net-Server-2.008/t/Server_MultiType.t0000644000175000017500000000344112331755703016123 0ustar paulpaul#!/usr/bin/perl package Net::Server::Test; use strict; use FindBin qw($Bin); use lib $Bin; use NetServerTest qw(prepare_test ok use_ok diag); my $env = prepare_test({n_tests => 5, start_port => 20400, n_ports => 1}); # runs three of its own tests use_ok('Net::Server::MultiType'); @Net::Server::Test::ISA = qw(Net::Server::MultiType); sub accept { $env->{'signal_ready_to_test'}->(); diag("Net::Server::MultiType::ISA: (".join(",",@Net::Server::MultiType::ISA).")"); return shift->SUPER::accept(@_); } my $ok = eval { local $SIG{'ALRM'} = sub { die "Timeout\n" }; alarm $env->{'timeout'}; my $ppid = $$; my $pid = fork; die "Trouble forking: $!" if ! defined $pid; ### parent does the client if ($pid) { $env->{'block_until_ready_to_test'}->(); my $remote = NetServerTest::client_connect(PeerAddr => $env->{'hostname'}, PeerPort => $env->{'ports'}->[0]) || die "Couldn't open child to sock: $!"; my $line = <$remote>; diag($line); die "Didn't get the type of line we were expecting: ($line)" if $line !~ /Net::Server/; print $remote "exit\n"; return 1; ### child does the server } else { eval { alarm $env->{'timeout'}; close STDERR; Net::Server::Test->run( port => $env->{'ports'}->[0], host => $env->{'hostname'}, ipv => $env->{'ipv'}, background => 0, setsid => 0, server_type => 'Single', ); } || do { diag("Trouble running server: $@"); kill(9, $ppid) && ok(0, "Failed during run of server"); }; exit; } alarm(0); }; alarm(0); ok($ok, "Got the correct output from the server") || diag("Error: $@"); Net-Server-2.008/t/Server_INET.t0000644000175000017500000000426212331755703014730 0ustar paulpaul#!/usr/bin/perl package Net::Server::Test; use strict; use FindBin qw($Bin); use lib $Bin; use NetServerTest qw(prepare_test ok use_ok diag); my $env = prepare_test({n_tests => 5, start_port => 20300, n_ports => 1}); # runs three of its own tests use_ok('Net::Server::INET'); @Net::Server::Test::ISA = qw(Net::Server::INET); my $ok = eval { local $SIG{'ALRM'} = sub { die "Timeout\n" }; alarm $env->{'timeout'}; my $ppid = $$; my $pid = fork; die "Trouble forking: $!" if ! defined $pid; ### parent does the client if ($pid) { $env->{'block_until_ready_to_test'}->(); my $remote = NetServerTest::client_connect(PeerAddr => $env->{'hostname'}, PeerPort => $env->{'ports'}->[0]) || die "Couldn't open child to sock: $!"; my $line = <$remote>; die "Didn't get the type of line we were expecting: ($line)" if $line !~ /Net::Server/; print $remote "exit\n"; return 1; ### child does the server } else { eval { alarm $env->{'timeout'}; # pretend we're inetd my $sock = NetServerTest::client_connect( # not really a client - client_connect is a raw passthrough to IO::Socket::INET->new LocalAddr => $env->{'hostname'}, LocalPort => $env->{'ports'}->[0], Listen => 5, ReuseAddr => 1, Reuse => 1, ) || die "Couldn't setup server: $!"; $env->{'signal_ready_to_test'}->(); my $client = $sock->accept || die "Couldn't accept"; # map these to look like inetd local *STDIN = \*{ $client }; local *STDOUT = \*{ $client }; close STDERR; Net::Server::Test->run( port => $env->{'ports'}->[0], host => $env->{'hostname'}, ipv => $env->{'ipv'}, background => 0, setsid => 0, ); } || do { diag("Trouble running server: $@"); kill(9, $ppid) && ok(0, "Failed during run of server"); }; exit; } alarm 0; }; alarm 0; ok($ok, "Got the correct output from the server") || diag("Error: $@"); Net-Server-2.008/t/Server_BASE.t0000644000175000017500000000622612331755703014705 0ustar paulpaul#!/usr/bin/perl package Net::Server::Test; use strict; use FindBin qw($Bin); use lib $Bin; use NetServerTest qw(prepare_test ok use_ok diag); my $env = prepare_test({n_tests => 6, start_port => 20100, n_ports => 3}); # runs three of its own tests use_ok('Net::Server'); @Net::Server::Test::ISA = qw(Net::Server); sub accept { $env->{'signal_ready_to_test'}->(); return shift->SUPER::accept(@_); } my $ok = eval { local $SIG{'ALRM'} = sub { die "Timeout\n" }; alarm $env->{'timeout'}; my $pid = fork; die "Trouble forking: $!" if ! defined $pid; ### parent does the client if ($pid) { $env->{'block_until_ready_to_test'}->(); my $remote = NetServerTest::client_connect(PeerAddr => $env->{'hostname'}, PeerPort => $env->{'ports'}->[0]) || die "Couldn't open child to sock: $!"; my $line = <$remote>; die "Didn't get the type of line we were expecting: ($line)" if $line !~ /Net::Server/; print $remote "exit\n"; return 1; ### child does the server } else { eval { alarm $env->{'timeout'}; close STDERR; Net::Server::Test->run( port => $env->{'ports'}->[0], host => $env->{'hostname'}, ipv => $env->{'ipv'}, background => 0, setsid => 0, ); } || diag("Trouble running server: $@"); exit; } alarm(0); }; alarm(0); ok($ok, "Got the correct output from the server") || diag("Error: $@"); ### start up a multiport server and connect to it $ok = eval { local $SIG{'ALRM'} = sub { die "Timeout\n" }; alarm $env->{'timeout'}; my $ppid = $$; my $pid = fork; die "Trouble forking: $!" if ! defined $pid; ### parent does the client if ($pid) { $env->{'block_until_ready_to_test'}->(); my $remote = NetServerTest::client_connect(PeerAddr => $env->{'hostname'}, PeerPort => $env->{'ports'}->[2]) || die "Couldn't open child to sock: $!"; my $line = <$remote>; die "Didn't get the type of line we were expecting: ($line)" if $line !~ /Net::Server/; print $remote "quit\n"; $remote = NetServerTest::client_connect(PeerAddr => $env->{'hostname'}, PeerPort => $env->{'ports'}->[1]) || die "Couldn't open child to sock: $!"; $line = <$remote>; die "Didn't get the type of line we were expecting: ($line)" if $line !~ /Net::Server/; print $remote "exit\n"; return 1; ### child does the server } else { eval { alarm $env->{'timeout'}; close STDERR; Net::Server::Test->run( port => "$env->{'hostname'}:$env->{'ports'}->[2]", port => $env->{'ports'}->[1], host => $env->{'hostname'}, ipv => $env->{'ipv'}, background => 0, setsid => 0, ); } || do { diag("Trouble running server: $@"); kill(9, $ppid) && ok(0, "Failed during run of server"); }; exit; } alarm(0); }; alarm(0); ok($ok, "Got the correct output from the multiport server") || diag("Error: $@"); Net-Server-2.008/t/Server_Single.t0000644000175000017500000000041112331755703015402 0ustar paulpaul#!/usr/bin/perl use strict; use FindBin qw($Bin); use lib $Bin; use NetServerTest qw(prepare_test ok use_ok diag); prepare_test({n_tests => 1, plan_only => 1}); use_ok('Net::Server::Single'); ### not much to test ### this is only a personality for the MultiType Net-Server-2.008/t/Server_http.t0000644000175000017500000000365512331755703015155 0ustar paulpaul#!/usr/bin/perl package Net::Server::Test; use strict; use FindBin qw($Bin); use lib $Bin; use NetServerTest qw(prepare_test ok use_ok diag); my $env = prepare_test({n_tests => 5, start_port => 20200, n_ports => 1}); # runs three of its own tests use_ok('Net::Server::HTTP'); @Net::Server::Test::ISA = qw(Net::Server::HTTP); sub accept { my $self = shift; exit if $^O eq 'MSWin32' && $self->{'__one_accept_only'}++; $env->{'signal_ready_to_test'}->(); return $self->SUPER::accept(@_); } sub done { 1 } # force exit after first request my $ok = eval { local $SIG{'ALRM'} = sub { die "Timeout\n" }; alarm $env->{'timeout'}; my $ppid = $$; my $pid = fork; die "Trouble forking: $!" if ! defined $pid; ### parent does the client if ($pid) { $env->{'block_until_ready_to_test'}->(); my $remote = NetServerTest::client_connect(PeerAddr => $env->{'hostname'}, PeerPort => $env->{'ports'}->[0]) || die "Couldn't open child to sock: $!"; print $remote "GET / HTTP/1.0\nFoo: bar\n\n"; ### sample a line my @lines = <$remote>; print map {s/\s*$//; "# $_\n"} @lines; die "Didn't get a correct http response: ($lines[0])" if !@lines || $lines[0] !~ m{^HTTP/1.0}; return 1; ### child does the server } else { eval { alarm $env->{'timeout'}; close STDERR; Net::Server::Test->run( port => $env->{'ports'}->[0], host => $env->{'hostname'}, ipv => $env->{'ipv'}, server_type => 'Single', background => 0, setsid => 0, ); } || do { diag("Trouble running server: $@"); kill(9, $ppid) && ok(0, "Failed during run of server"); }; alarm(0); exit; } alarm(0); }; alarm(0); ok($ok, "Got the correct output from the server") || diag("Error: $@"); Net-Server-2.008/t/NetServerTest.pm0000644000175000017500000001356312334164524015573 0ustar paulpaulpackage NetServerTest; use strict; use IO::Socket; use Exporter; @NetServerTest::ISA = qw(Exporter); @NetServerTest::EXPORT_OK = qw(prepare_test client_connect ok is like use_ok skip diag); my %env; use constant debug => $ENV{'NS_DEBUG'} ? 1 : 0; END { warn "# number of tests ran ".($env{'_ok_n'} || 0)." did not match number of specified tests ".($env{'_ok_N'} || 0)."\n" if ($env{'_ok_N'} || 0) ne ($env{'_ok_n'} || 0) && ($env{'_ok_pid'} || 0) == $$; } sub client_connect { shift if $_[0] && $_[0] eq __PACKAGE__; if ($env{'ipv'} && $env{'ipv'} ne 4) { require IO::Socket::INET6; return IO::Socket::INET6->new(@_); } else { return IO::Socket::INET->new(@_); } } # most of our tests need forking, a certain number of ports, and some pipes sub prepare_test { my $args = shift || {}; my $N = $args->{'n_tests'} || die "Missing n_tests"; print "1..$N\n"; %env = map {/NET_SERVER_TEST_(\w+)/; lc($1) => $ENV{$_}} grep {/^NET_SERVER_TEST_\w+$/} keys %ENV; $env{'_ok_N'} = $N; $env{'_ok_pid'} = $$; return if $args->{'plan_only'}; $env{'_ok_n'} = 0; $env{'timeout'} ||= 5; # allow for finding a hostname that we can use in our tests that appears to be valid if (!$env{'hostname'}) { eval { require Net::Server::Proto } || do { SKIP: { skip("Could not load Net::Server::Proto to lookup host: $@", $N - 1) }; exit; }; foreach my $host (qw(localhost localhost.localdomain localhost6 * ::1)) { # try local bindings first to avoid opening external ports during testing my @info = eval { Net::Server::Proto->get_addr_info($host) }; next if ! @info; @info = sort {$a->[2] <=> $b->[2]} @info; # try IPv4 first in the name of consistency, but let IPv6 work too $env{'hostname'} = $info[0]->[0]; $env{'ipv'} = $info[0]->[2]; last; } die "Could not find a hostname to test connections with (tried localhost, *, ::1)" if ! $env{'hostname'}; } warn "# Checking can_fork\n" if debug; ok(can_fork(), "Can fork on this platform") || do { SKIP: { skip("Fork doesn't work on this platform", $N - 1) }; exit; }; warn "# Checked can_fork\n" if debug; warn "# Getting ports\n" if debug; my $ports = $env{'ports'} = get_ports($args); ok(scalar(@$ports), "Got needed ports (@$ports)") || do { SKIP: { skip("Couldn't get the needed ports for testing", $N - 2) }; exit }; warn "# Got ports\n" if debug; warn "# Checking pipe serialization\n" if debug; pipe(NST_READ, NST_WRITE); NST_READ->autoflush(1); NST_WRITE->autoflush(1); print NST_WRITE "22"; is(read(NST_READ, my $buf, 2), 2, "Pipe works") || do { SKIP: { skip ("Couldn't use working pipe", $N - 3) }; exit }; warn "# Checked pipe serialization\n" if debug; $env{'block_until_ready_to_test'} = sub { read(NST_READ, my $buf, 1) }; $env{'signal_ready_to_test'} = sub { print NST_WRITE "1"; NST_WRITE->flush; }; return \%env; } sub can_fork { return eval { my $pid = fork; die "Trouble while forking" unless defined $pid; # can't fork exit unless $pid; # can fork, exit child 1; }; } sub get_ports { my $args = shift; my $start_port = $args->{'start_port'} || die "Missing start_port"; my $n = $args->{'n_ports'} || die "Missing n_ports"; my @ports; eval { local $SIG{'ALRM'} = sub { die }; alarm $env{'timeout'}; for my $port ($start_port .. $start_port + 99){ my $serv = client_connect( LocalAddr => $env{'hostname'}, LocalPort => $port, Timeout => 2, Listen => 1, ReuseAddr => 1, Reuse => 1, ) || do { warn "Couldn't open server socket on port $port: $!\n" if $env{'trace'}; next }; my $client = client_connect( PeerAddr => $env{'hostname'}, PeerPort => $port, Timeout => 2, ) || do { warn "Couldn't open client socket on port $port: $!\n" if $env{'trace'}; next }; my $sock = $serv->accept || do { warn "Didn't accept properly on server: $!" if $env{'trace'}; next }; $sock->autoflush(1); print $sock "hi from server\n"; $client->autoflush(1); print $client "hi from client\n"; next if <$sock> !~ /^hi from client/; next if <$client> !~ /^hi from server/; $client->close; $sock->close; push @ports, $port; last if @ports == $n; } alarm(0); }; die "Number of ports didn't match (@ports) != $n ($@)" if @ports < $n; return \@ports; } ###----------------------------------------------------------------### sub ok { my ($ok, $msg, $level) = @_; my $n = ++$env{'_ok_n'}; print "".($ok ? "" : "not ")."ok $n"; print " - $msg" if defined $msg; print "\n" if $msg !~ /\n\Z/; if (! $ok) { my ($pkg, $file, $line) = caller($level || 0); print "# failed at $file line $line\n"; } return $ok; } sub is { my ($a, $b, $msg) = @_; if (! ok($a eq $b, $msg, 1)) { print "# got: $a\n"; print "# expected: $b\n"; return; } return 1; } sub like { my ($a, $b, $msg) = @_; if (! ok($a =~ $b, $msg, 1)) { print "# got: $a\n"; print "# expected: $b\n"; return; } return 1; } sub use_ok { my $pkg = shift; my $ok = eval("require $pkg") && eval {$pkg->import(@_);1}; ok($ok, "use $pkg", 1) || do { print "# failed to import $pkg: $@\n"; return 0 }; } sub skip { my ($msg, $n) = @_; print "ok ".(++$env{'_ok_n'})." # skip $msg\n" for 1 .. $n; no warnings 'exiting'; last SKIP; } sub diag { for my $line (@_) { chomp $line; print "# $line\n"; } } 1; Net-Server-2.008/examples/0000755000175000017500000000000012334210320014005 5ustar paulpaulNet-Server-2.008/examples/samplechat.pl0000755000175000017500000001121212331755703016503 0ustar paulpaul#!/usr/bin/perl -w =head1 NAME samplechat.pl - Show a basic Net::Server::Multiplex sample =head SERVER SYNOPIS # To run this in background daemon mode, listening on port 2000, do: samplechat.pl --setsid=1 --log_file=/tmp/samplechat.log --pid_file=/tmp/samplechat.pid --port=2000 # To turn off the daemon, do: kill `cat /tmp/samplechat.pid`; =head CLIENT SYNOPIS # from a terminal type telnet localhost 2000 # you will then be in a echo server. =head DESCRIPTION This example demonstrates some of the features of Net::Server::Multiplex =cut package SampleChatServer; use strict; use base qw(Net::Server::Multiplex); __PACKAGE__->run(); exit; ###----------------------------------------------------------------### # Demonstrate a Net::Server style hook sub allow_deny_hook { my $self = shift; my $prop = $self->{server}; my $sock = $prop->{client}; return 1 if $prop->{peeraddr} =~ /^127\./; return 0; } # Another Net::Server style hook sub request_denied_hook { print "Go away!\n"; print STDERR "DEBUG: Client denied!\n"; } # IO::Multiplex style callback hook sub mux_connection { my $self = shift; my $mux = shift; my $fh = shift; my $peer = $self->{peeraddr}; # Net::Server stores a connection counter in the {requests} field. $self->{id} = $self->{net_server}->{server}->{requests}; # Keep some values that I might need while the {server} # property hash still contains the current client info # and stash them in my own object hash. $self->{peerport} = $self->{net_server}->{server}->{peerport}; # Net::Server directs STDERR to the log_file print STDERR "DEBUG: Client [$peer] (id $self->{id}) just connected...\n"; # Notify everyone that the client arrived $self->broadcast($mux,"JOIN: (#$self->{id}) from $peer\r\n"); # STDOUT is tie'd to the correct IO::Multiplex handle print "Welcome, you are number $self->{id} to connect.\r\n"; # Try out the timeout feature of IO::Multiplex $mux->set_timeout($fh, undef); $mux->set_timeout($fh, 20); # This is my state and will be unique to this connection $self->{state} = "junior"; } # If this callback is ever hooked, then the mux_connection callback # is guaranteed to have already been run once (if defined). sub mux_input { my $self = shift; my $mux = shift; my $fh = shift; my $in_ref = shift; # Scalar reference to the input my $peer = $self->{peeraddr}; my $id = $self->{id}; print STDERR "DEBUG: input from [$peer] ready for consuming.\n"; # Process each line in the input, leaving partial lines # in the input buffer while ($$in_ref =~ s/^(.*?)\r?\n//) { next unless $1; my $message = "[$id - $peer] $1\r\n"; $self->broadcast($mux, $message); print " - sent ".(length $message)." byte message\r\n"; } if ($self->{state} eq "senior") { $mux->set_timeout($fh, undef); $mux->set_timeout($fh, 40); } } # It is possible that this callback will be called even # if mux_connection or mux_input were never called. This # occurs when allow_deny or allow_deny_hook fails to # authorize the client. The callback object will be the # default listen object instead of a client unique object. # However, both object should contain the $self->{net_server} # key pointing to the original Net::Server object. sub mux_close { my $self = shift; my $mux = shift; my $fh = shift; my $peer = $self->{peeraddr}; # If mux_connection has actually been run if (exists $self->{id}) { $self->broadcast($mux,"LEFT: (#$self->{id}) from $peer\r\n"); print STDERR "DEBUG: Client [$peer] (id $self->{id}) closed connection!\n"; } } # This callback will happen when the mux->set_timeout expires. sub mux_timeout { my $self = shift; my $mux = shift; my $fh = shift; print STDERR "DEBUG: HEARTBEAT!\n"; if ($self->{state} eq "junior") { print "Whoa, you must have a lot of patience. You have been upgraded.\r\n"; $self->{state} = "senior"; } elsif ($self->{state} eq "senior") { print "If you don't want to talk then you should leave. *BYE*\r\n"; close(STDOUT); } $mux->set_timeout($fh, undef); $mux->set_timeout($fh, 40); } # Routine to send a message to all clients in a mux. sub broadcast { my $self = shift; my $mux = shift; my $msg = shift; foreach my $fh ($mux->handles) { # NOTE: All the client unique objects can be found at # $mux->{_fhs}->{$fh}->{object} # In this example, the {id} would be # $mux->{_fhs}->{$fh}->{object}->{id} print $fh $msg; } } Net-Server-2.008/examples/sigtest.pl0000644000175000017500000000645112331755703016052 0ustar paulpaul#!/usr/bin/perl -w =head1 NAME sigtest.pl - test for safe/unsafe signal handling =head1 SYNOPSIS sigtest.pl SIGNAME SAFE|UNSAFE # (SIGNAME is a standard signal - default is USR1) # (SAFE will use Net::Server::SIG, UNSAFE uses \$SIG{} - default is SAFE) # If the child isn't saying anything, the test is invalid. # If the child dies, look for a core file. # The process will run until it dies or you kill it =head1 DESCRIPTION Recent versions of Perl (5.8 ish) have much better signal handling so the safe signal handling may not be necessary. But on older versions of Perl the safe signal handling was necessary. It still doesn't hurt to use some of the safer practices on newer Perls. =cut use IO::Select (); use IO::Socket (); use Net::Server::SIG qw(register_sig check_sigs); use POSIX (); print "Usage: $0 SIGNAME SAFE|UNSAFE (SIGNAME is a standard signal - default is USR1) (SAFE will use Net::Server::SIG, UNSAFE uses \$SIG{} - default is SAFE) If the child isn't saying anything, the test is invalid. If the child dies, look for a core file. "; my $SIG = shift() || 'USR1'; my $safe = shift() || 'SAFE'; $safe = uc($safe) eq 'UNSAFE' ? undef : 1; my $x = 0; my %hash = (); ### set up a pipe pipe(READ,WRITE); READ->autoflush(1); WRITE->autoflush(1); STDOUT->autoflush(1); my $pid = fork(); die "Couldn't fork [$!]" unless defined $pid; ### see if child left $SIG{CHLD} = sub { print "P ($$): Child died (\$?=$?)\n" while (waitpid(-1, POSIX::WNOHANG()) > 0); }; ### let the parent try to kill the child if( $pid ){ sleep(2); ### for off children to help bombard the child for(1..4){ my $pid2 = fork(); unless( defined $pid2 ){ kill 9, $pid; die "Couldn't fork [$!]"; } unless( $pid2 ){ $SIG{CHLD} = 'DEFAULT'; last; } } print "P ($$): Starting up!\n"; ### kill the child with that signal my $n = 50000; while (1){ last unless kill $SIG, $pid; unless( ++$x % $n ){ print "P ($$): $x SIG_$SIG\'s sent.\n"; print WRITE "$n\n"; } } ### let the child try to stay alive }else{ print "C ($$): Starting up!\n"; my $select = IO::Select->new(); $select->add(\*READ); ### do some variable manipulation in the signal handler my $subroutine = sub { $hash{foo} = "abcde"x10000; $hash{bar} ++; delete $hash{baz}; delete $hash{bar}; }; ### register a signal and see if it will bounce off of the can_read if( $safe ){ print "C ($$): Using SAFE signal handler.\n"; register_sig($SIG => $subroutine); ### This is an unsafe signal handler. See how long ### it can take signals. }else{ print "C ($$): Using UNSAFE signal handler.\n"; $SIG{$SIG} = $subroutine; } my $total = 0; ### loop forever trying to stay alive while ( 1 ){ my @fh = $select->can_read(10); my $key; my $val; ### this is the handler for safe (fine under unsafe also) next if check_sigs() && ! @fh; ### do some hash manipulation delete $hash{foo}; $hash{bar} = 0; $hash{baz} = "abcde"x100000; next unless @fh; my $line = ; chomp($line); $total += $line; print "C ($$): P said \"$line\"\n"; unless( ++$x % 5 ){ print "C ($$): $x lines read. $total SIG's received\n"; } } print "Child is done\n"; } Net-Server-2.008/examples/httpd0000755000175000017500000000656412331755703015111 0ustar paulpaul#!/usr/bin/perl =head1 NAME httpd - Simple http daemon =head1 SYNOPSIS # customize options in sub configure_hook ./httpd =cut use strict; use warnings; use base qw(Net::Server::HTTP); __PACKAGE__->run; exit; ###----------------------------------------------------------------### sub default_server_type { 'PreFork' } sub default_port { '8080' } #sub default_port { ['8443/SSL', '8080'] } # if you want a secure server #sub SSL_key_file { '/path/to/secure.key' } #sub SSL_cert_file { '/path/to/secure.crt' } # set up some server parameters sub configure_hook { my $self = shift; my $prop = $self->{'server'}; # $prop->{user} = 'nobody'; # user to run as # $prop->{group} = 'nobody'; # group to run as my $root = $self->{'server_root'} ||= "/var/www"; $self->{document_root} = "$root/htdocs"; $self->{default_index} = [ qw(index.html index.htm main.htm) ]; $self->{access_log} = "$root/access.log"; # $prop->{log_file} = "$root/error.log"; # $prop->{setsid} = 1; # daemonize $self->{mime_types} = { html => 'text/html', htm => 'text/html', gif => 'image/gif', jpg => 'image/jpeg', }; $self->{mime_default} = 'text/plain'; } sub post_configure_hook { my $self = shift; open(ACCESS, ">>". $self->{access_log}) || die "Couldn't open ACCESS: $!"; my $old = select ACCESS; $| = 1; select $old; } sub send_error { my ($self, $n, $msg) = @_; $self->send_status($n); print "Content-type: text/html\r\n\r\n"; print "

Error $n

$msg

"; } sub process_http_request { my $self = shift; my $uri = $ENV{'PATH_INFO'} || ''; if ($uri =~ /[\ \;]/) { return $self->send_error(400, "Malformed URL"); } $uri =~ s/%(\w\w)/chr(hex($1))/eg; 1 while $uri =~ s|^\.\./+||; # can't go below doc root my $path = "$self->{document_root}$uri"; # see if there's an index page if (-d $path) { my $_path; foreach (@{ $self->{'default_index'} }){ next if !-e "$path/$_"; $_path = "$path/$_"; last; } $path = $_path || return $self->send_error(403, "Directory listing not supported"); } if (! -e $path) { warn "File not found: $path\n"; return $self->send_error(404, "File Not Found"); } # work in progress to allow for an easy exec_cgi # if ($path =~ /\.cgi/ && -x $path) { ## my $pid = fork; ## return $self->send_501("Couldn't exec: $!") if ! defined $pid; # $ENV{'SCRIPT_NAME'} = $uri; # do $path; # # #require IPC::Open2; # #warn "here\n"; # #$self->{'server'}->{'client'}->autoflush(1); # #my $pid = IPC::Open2::open2(my $out, $self->{'server'}->{'client'}, $uri) || $self->send_501("Couldn't exec: $!"); # #warn "Parent--------\n".`ls -l /proc/$$/fd`; # ##my $pid = open(my $out, '-|', $uri) || $self->send_501("Couldn't exec: $!"); # #print <$out>; ## waitpid $pid, 0; # warn "Back\n"; # return; # } # spit out the static content open(my $fh, '<', $path) || return $self->send_501("Can't open file [$!]"); my $type = $path =~ /([^\.]+)$/ ? $1 : ''; $type = $self->{'mime_types'}->{$type} || $self->{'mime_default'}; print "Content-type: $type\r\n\r\n"; print $_ while read $fh, $_, 8192; close $fh; } 1; Net-Server-2.008/examples/LoadTester.pl0000755000175000017500000000745112331755703016442 0ustar paulpaul#!/usr/bin/perl =head1 NAME LoadTester.pl - Allow for testing load agains various servers =head1 SYNOPIS # start - or find a server somewhere perl -e 'use base qw(Net::Server::PreForkSimple); __PACKAGE__->run' # change parameters in sub configure_hook # setup the load to test against the server in sub load # run this script LoadTester.pl =cut use strict; use warnings; use base qw(Net::Server::PreFork); use IO::Socket; BEGIN { Time::HiRes->import('time') if eval { require Time::HiRes }; } $| = 1; __PACKAGE__->run(min_servers => 100, max_servers => 255, max_spare_servers => 101); exit; ###----------------------------------------------------------------### ### set up the test parameters sub configure_hook { my $self = shift; $self->{'addr'} = 'localhost'; # choose a remote addr $self->{'port'} = 20203; # choose a remote port $self->{'file'} = '/tmp/mysock'; # sock file for Load testing a unix socket $self->{'failed'} = 0; # failed hits (server was blocked) $self->{'hits'} = 0; # log hits $self->{'hits2'} = 0; # log hits $self->{'report_hits'} = 1000; # how many hits in between reports $self->{'max_hits'} = 20_000; # how many impressions to do $self->{'time_begin'} = time; # keep track of time $self->{'time_begin2'} = time; # keep track of time $self->{'sleep'} = 0; # sleep between hits? $self->{'ssl'} = 0; # use SSL ? } ### these generally deal with sockets - ignore them sub pre_bind { require IO::Socket::SSL if shift->{'ssl'} } sub bind { shift()->log(2, "Running under pid $$") } sub accept { 1 } sub post_accept {} sub get_client_info {} sub allow_deny { 1 } sub post_process_request {} sub process_request { my $self = shift; sleep $self->{'sleep'} if $self->{'sleep'}; ### try to connect and deliver the load my $class = $self->{'ssl'} ? 'IO::Socket::SSL' : 'IO::Socket::INET'; if ($self->{'remote'} = $class->new(PeerAddr => $self->{'addr'}, PeerPort => $self->{'port'})) { $self->load; return; } #if ($self->{remote} = IO::Socket::UNIX->new(Peer => $self->{'file'})) { # $self->load; # return; #} print { $self->{'server'}->{'_WRITE'} } "$$ failed [$!]\n"; } sub load { my $self = shift; my $handle = $self->{'remote'}; $handle->autoflush(1); my $line = <$handle>; print $handle "quit\n"; } sub parent_read_hook { my ($self, $status) = @_; if ($status =~ /failed/i) { $self->{'failed'}++; print $status; if ($self->{'failed'} >= 300) { $self->{'time_end'} = time; $self->print_report; $self->server_close; } return 1; } return if $status !~ /processing/i; $self->{'hits'}++; $self->{'hits2'}++; print "*" if not $self->{'hits'} % 100; if (not $self->{'hits'} % $self->{'report_hits'}) { $self->{'time_end'} = time; $self->print_report; $self->{'hits2'} = 0; $self->{'time_begin2'} = time; } $self->server_close if $self->{'hits'} >= $self->{'max_hits'}; } sub print_report { my $self = shift; my $time = $self->{'time_end'} - $self->{'time_begin'}; my $time2 = $self->{'time_end'} - $self->{'time_begin2'}; print "\n$0 Results\n"; print "--------------------------------------------\n"; printf "(%d) overall hits in (%.3f) seconds: %.3f hits per second\n", $self->{'hits'}, $time, ($time ? $self->{'hits'}/$time : $self->{'hits'}); printf "(%d) hits in (%.3f) seconds: %.3f hits per second\n", $self->{'hits2'}, $time2, ($time2 ? $self->{'hits2'}/$time2 : $self->{'hits2'}); print "($self->{failed}) failed hits\n"; } Net-Server-2.008/examples/udp_server.pl0000755000175000017500000000554112331755703016550 0ustar paulpaul#!/usr/bin/perl =head1 NAME udp_server.pl - Simple sample udp echo server =head1 SERVER SYNOPSIS perl udp_server.pl --log_level 3 # default is to not background =head1 CLIENT SYNOPSIS # In another terminal perl udp_server.pl --client =cut package MyUDPD; use strict; use warnings; use Data::Dumper; my $port = 20203; my $host = 'localhost'; my $recv_length = 8192; # packet size ### what type of server is this - we could ### use multi type when we add command line ### parsing to this http server to allow ### for different configurations use base qw(Net::Server::PreFork); if (grep {/\bclient\b/i} @ARGV) { handle_client(); } else { ### run the server MyUDPD->run( port => "$host:$port/udp", # we could also do the following: # port => '*:20203/udp', # port => 'somehost:20203/udp', # port => '20203/udp', port => '20204/udp', # port => '20203/udp', port => '20203/tcp', ); } exit; ###----------------------------------------------------------------### ### overridden server hooks ### set up some server parameters sub configure_hook { my $self = shift; ### change the packet len? $self->{server}->{udp_recv_len} = $recv_length; # default is 4096 } ### this is the main method to override ### this is where most of the work will occur ### A sample server is shown below. sub process_request { my $self = shift; my $prop = $self->{'server'}; ### if we were writing a server that did both tcp and udp, ### we would need to check $prop->{udp_true} to see ### if the current connection is udp or not # if ($prop->{udp_true}) { # # yup, this is udp # } # all of the client data is already in 'udp_data' if ($prop->{'udp_data'} =~ /dump/) { local $Data::Dumper::Sortkeys = 1; $prop->{'client'}->send(Data::Dumper::Dumper($self), 0); } else { $prop->{'client'}->send("You said \"$prop->{udp_data}\"", 0); } return; } ###----------------------------------------------------------------### ### dummy client terminal echo relay sub handle_client { require IO::Socket; my $recv_flags = 0; print "$0\nEcho server client relay\nType anything and hit enter\n"; print "-------------------------------\n"; while (defined(my $line = )) { chomp $line; my $sock = IO::Socket::INET->new( PeerAddr => $host, PeerPort => $port, Proto => 'udp', ) || die "Couldn't connect to $host:$port: $!"; $sock->send($line, 0); my $data = ''; $sock->recv($data, $recv_length, $recv_flags); print "From the server:\n$data\n-------------------------\n"; } } Net-Server-2.008/examples/connection_test.pl0000644000175000017500000000616612331755703017571 0ustar paulpaul#!/usr/bin/perl -w =head1 NAME connection_test.pl - Test UDP/TCP/UNIX/UNIX_DGRAM connections =head1 SERVER SYNOPSIS # in a separate terminal window perl connection_test.pl =head1 CLIENT SYNOPSIS perl connection_test.pl UDP # or perl connection_test.pl TCP # or perl connection_test.pl UNIX # or perl connection_test.pl UNIX_DGRAM =cut package MyPack; use strict; use warnings; use base qw(Net::Server); use IO::Socket (); use POSIX qw(tmpnam); use Socket qw(SOCK_DGRAM SOCK_STREAM); sub post_bind_hook { my $self = shift; foreach my $sock ( @{ $self->{server}->{sock} } ){ $self->log(2,$sock->show); } } my $socket_file = tmpnam(); $socket_file =~ s|/[^/]+$|/mysocket.file|; my $socket_file2 = $socket_file ."2"; my $udp_port = 20204; my $tcp_port = 20204; print "\$Net::Server::VERSION = $Net::Server::VERSION\n"; if( @ARGV ){ if( uc($ARGV[0]) eq 'UDP' ){ print "Testing UDP\n"; my $sock = IO::Socket::INET->new(PeerAddr => 'localhost', PeerPort => $udp_port, Proto => 'udp', ) || die "Can't connect [$!]"; ### send a packet, get a packet $sock->send("Are you there?",0); my $data = undef; $sock->recv($data,4096,0); print $data,"\n"; exit; } if( uc($ARGV[0]) eq 'TCP' ){ print "Testing TCP\n"; my $sock = IO::Socket::INET->new(PeerAddr => 'localhost', PeerPort => $tcp_port, Proto => 'tcp', ) || die "Can't connect [$!]"; print $sock "hi\n"; my $line = $sock->getline(); print $line; exit; } if( uc($ARGV[0]) eq 'UNIX' ){ print "Testing UNIX (File socket with SOCK_STREAM)\n"; my $sock = IO::Socket::UNIX->new(Peer => $socket_file) || die "Can't connect [$!]"; print $sock "hi\n"; my $line = $sock->getline(); print $line; exit; } if( uc($ARGV[0]) eq 'UNIX_DGRAM' ){ print "Testing UNIX_DGRAM\n"; my $sock = IO::Socket::UNIX->new(Peer => $socket_file2, Type => SOCK_DGRAM, ) || die "Can't connect [$!]"; ### send a packet, get a packet $sock->send("Are you there?",0); ### The server receives the data just fine ### however, the default arguments don't seem to work for ### sending it back. If anybody knows why, let me know. my $data = undef; $sock->recv($data,4096,0); print $data,"\n"; exit; } print "USAGE: $0 UDP|TCP|UNIX|UNIX_DGRAM (If no arguments are passed, the server will start. You should start the server in one window, and connect in another window). "; exit; } ### set up servers doing ## SOCK_DGRAM on INET (udp) ## SOCK_STREAM on INET (tcp) ## SOCK_DGRAM on UNIX ## SOCK_STREAM on UNIX MyPack->run(port => "$udp_port|udp", port => "$tcp_port|tcp", port => "$socket_file|unix", # default is SOCK_STREAM port => join("|",$socket_file2,SOCK_DGRAM,"unix"), log_level => 4, ); Net-Server-2.008/Makefile.PL0000644000175000017500000000126212334207542014156 0ustar paulpauluse 5.006; use ExtUtils::MakeMaker; my %args = ( NAME => 'Net::Server', VERSION_FROM => 'lib/Net/Server.pm', ABSTRACT_FROM => 'lib/Net/Server.pod', AUTHOR => 'Paul Seamons and Rob Brown ', EXE_FILES => [ 'bin/net-server' ], PREREQ_PM => { 'IO::Socket' => 0, Socket => 0, POSIX => 0, 'Time::HiRes' => 0, }, dist => { DIST_DEFAULT => 'all tardist', COMPRESS => 'gzip -vf', SUFFIX => '.gz', }, clean => { FILES => '*~', }, realclean => { FILES => '*~', }, ); WriteMakefile(%args); 1; Net-Server-2.008/MANIFEST0000644000175000017500000000556612334210320013334 0ustar paulpaulbin/net-server Simple executor Changes Module History examples/connection_test.pl Sample connection on all protocols examples/httpd Sample http daemon examples/LoadTester.pl Sample script to hit upon a running server examples/samplechat.pl Demonstrates some Net::Server::Multiplex features examples/sigtest.pl Compare safe signals under Net::Server::SIG examples/udp_server.pl Sample UDP server lib/Net/Server.pm Base Module containing core functionality lib/Net/Server.pod lib/Net/Server/Daemonize.pm Module for managing daemonization lib/Net/Server/Fork.pm Sub class allowing for fork upon accept lib/Net/Server/HTTP.pm Sub class allowing for easy HTTP daemon creation lib/Net/Server/INET.pm Sub class allowing for inetd process lib/Net/Server/Multiplex.pm Sub class allowing for multiplexing connections lib/Net/Server/MultiType.pm Sub class allowing for running any sub class lib/Net/Server/PreFork.pm Sub class allowing for managed pre forked processes lib/Net/Server/PreForkSimple.pm Sub class allowing for simple pre forked processes lib/Net/Server/Proto.pm Class for managing different connection types lib/Net/Server/Proto/SSL.pm Proto interface to IO::Socket::SSL (experimental) lib/Net/Server/Proto/SSLEAY.pm Proto interface to Net::SSLeay lib/Net/Server/Proto/TCP.pm Proto interface to IO::Socket::INET lib/Net/Server/Proto/UDP.pm Proto interface to IO::Socket::INET lib/Net/Server/Proto/UNIX.pm Proto interface to IO::Socket::UNIX under SOCK_STREAM lib/Net/Server/Proto/UNIXDGRAM.pm Proto interface to IO::Socket::UNIX under SOCK_DGRAM lib/Net/Server/PSGI.pm Sub class allowing for easy HTTP PSGI daemon creation lib/Net/Server/SIG.pm Module to make signals safer lib/Net/Server/Single.pm Sub class allowing for parallelism of base class lib/Net/Server/Log/Log/Log4perl.pm lib/Net/Server/Log/Sys/Syslog.pm Makefile.PL Makefile script MANIFEST This file MANIFEST.SKIP Files to skip when making the dist META.yml Module meta-data (added by MakeMaker) README What it says t/NetServerTest.pm t/Options.t t/Options.t.conf t/Port_Configuration.t t/Server_BASE.t Test script for Net::Server t/Server_Fork.t Test script for Net::Server::Fork t/Server_http.t Test script for Net::Server::HTTP t/Server_INET.t Test script for Net::Server::INET t/Server_Multiplex.t Test script for Net::Server::Multiplex t/Server_MultiType.t Test script for Net::Server::MultiType t/Server_PreFork.t Test script for Net::Server::PreFork t/Server_PreForkSimple.t Test script for Net::Server::PreForkSimple t/Server_Single.t Test script for Net::Server::Single t/SSL_test.t Test script for Net::Server::Proto::SSL t/SSLEAY_test.t Test script for Net::Server::Proto::SSLEAY t/UDP_test.t Test script for UDP connection t/UNIX_test.t Test script for UNIX Socket connections META.json Module JSON meta-data (added by MakeMaker)