| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package NetServer::Generic; |
|
4
|
|
|
|
|
|
|
|
|
5
|
14
|
|
|
14
|
|
110
|
use Carp; |
|
|
14
|
|
|
|
|
30
|
|
|
|
14
|
|
|
|
|
1818
|
|
|
6
|
14
|
|
|
14
|
|
19500
|
use Data::Dumper; |
|
|
14
|
|
|
|
|
143170
|
|
|
|
14
|
|
|
|
|
1022
|
|
|
7
|
14
|
|
|
14
|
|
112
|
use Exporter; |
|
|
14
|
|
|
|
|
28
|
|
|
|
14
|
|
|
|
|
402
|
|
|
8
|
14
|
|
|
14
|
|
70
|
use Fcntl; |
|
|
14
|
|
|
|
|
22
|
|
|
|
14
|
|
|
|
|
3758
|
|
|
9
|
14
|
|
|
14
|
|
10864
|
use IO::File; |
|
|
14
|
|
|
|
|
142710
|
|
|
|
14
|
|
|
|
|
3848
|
|
|
10
|
14
|
|
|
14
|
|
11642
|
use IO::Socket; |
|
|
14
|
|
|
|
|
244018
|
|
|
|
14
|
|
|
|
|
64
|
|
|
11
|
14
|
|
|
14
|
|
8218
|
use IO::Handle; |
|
|
14
|
|
|
|
|
36
|
|
|
|
14
|
|
|
|
|
470
|
|
|
12
|
14
|
|
|
14
|
|
10692
|
use IO::Select; |
|
|
14
|
|
|
|
|
23048
|
|
|
|
14
|
|
|
|
|
588
|
|
|
13
|
14
|
|
|
14
|
|
1202810
|
use IO::Pipe; |
|
|
14
|
|
|
|
|
22392
|
|
|
|
14
|
|
|
|
|
664
|
|
|
14
|
14
|
|
|
14
|
|
14266
|
use POSIX qw(mkfifo BUFSIZ EWOULDBLOCK WNOHANG); |
|
|
14
|
|
|
|
|
121380
|
|
|
|
14
|
|
|
|
|
130
|
|
|
15
|
14
|
|
|
14
|
|
21024
|
use Socket; |
|
|
14
|
|
|
|
|
34
|
|
|
|
14
|
|
|
|
|
16722
|
|
|
16
|
14
|
|
|
14
|
|
28694
|
use Time::HiRes qw(gettimeofday tv_interval); |
|
|
14
|
|
|
|
|
26070
|
|
|
|
14
|
|
|
|
|
74
|
|
|
17
|
14
|
|
|
14
|
|
25628
|
use Tie::RefHash; |
|
|
14
|
|
|
|
|
29982
|
|
|
|
14
|
|
|
|
|
828
|
|
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
BEGIN { |
|
20
|
14
|
50
|
|
14
|
|
876
|
if (! eval "require Thread") { |
|
21
|
|
|
|
|
|
|
# want warnings? uncomment the next line |
|
22
|
|
|
|
|
|
|
# warn "Could not import Thread.pm: $@\n"; |
|
23
|
14
|
|
|
|
|
4242
|
$MAIN::no_thread = 1; |
|
24
|
|
|
|
|
|
|
} else { |
|
25
|
0
|
|
|
|
|
0
|
Thread->import(); |
|
26
|
|
|
|
|
|
|
} |
|
27
|
|
|
|
|
|
|
} |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
@ISA = (qw(NetServer)); |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
$VERSION = "1.03"; |
|
32
|
|
|
|
|
|
|
|
|
33
|
14
|
|
|
14
|
|
84
|
use strict; |
|
|
14
|
|
|
|
|
28
|
|
|
|
14
|
|
|
|
|
91886
|
|
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=pod |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=head1 NAME |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
Server - generic TCP/IP server class |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
my $server_cb = sub { |
|
45
|
|
|
|
|
|
|
my ($s) = shift ; |
|
46
|
|
|
|
|
|
|
print STDOUT "Echo server: type bye to quit, exit ", |
|
47
|
|
|
|
|
|
|
"to kill the server.\n\n" ; |
|
48
|
|
|
|
|
|
|
while (defined ($tmp = )) { |
|
49
|
|
|
|
|
|
|
return if ($tmp =~ /^bye/i); |
|
50
|
|
|
|
|
|
|
$s->quit() if ($tmp =~ /^exit/i); |
|
51
|
|
|
|
|
|
|
print STDOUT "You said:>$tmp\n"; |
|
52
|
|
|
|
|
|
|
} |
|
53
|
|
|
|
|
|
|
my ($foo) = new NetServer::Generic; |
|
54
|
|
|
|
|
|
|
$foo->port(9000); |
|
55
|
|
|
|
|
|
|
$foo->callback($server_cb); |
|
56
|
|
|
|
|
|
|
$foo->mode("forking"); |
|
57
|
|
|
|
|
|
|
print "Starting server\n"; |
|
58
|
|
|
|
|
|
|
$foo->run(); |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
C provides a (very) simple server daemon for TCP/IP |
|
63
|
|
|
|
|
|
|
processes. It is intended to free the programmer from having to think |
|
64
|
|
|
|
|
|
|
too hard about networking issues so that they can concentrate on |
|
65
|
|
|
|
|
|
|
doing something useful. |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
The C object accepts the following methods, which |
|
68
|
|
|
|
|
|
|
configure various aspects of the new server: |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
=over 4 |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=item port |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
The port to listen on. |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
=item hostname |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
The local address to bind to. If no address is specified, listens for |
|
79
|
|
|
|
|
|
|
any connection on the designated port. |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=item listen |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
Queue size for listen. |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=item proto |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
Protocol we're listening to (defaults to tcp) |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=item timeout |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
Timeout value (see L) |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=item allowed |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
list of IP addresses or hostnames that are explicitly allowed to connect |
|
96
|
|
|
|
|
|
|
to the server. If empty, the default policy is to allow connections from |
|
97
|
|
|
|
|
|
|
anyone not in the 'forbidden' list. |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
NOTE: IP addresses or hostnames may be specified as perl regular |
|
100
|
|
|
|
|
|
|
expressions; for example 154\.153\.4\..* matches any IP address |
|
101
|
|
|
|
|
|
|
beginning with '154.153.4.'; |
|
102
|
|
|
|
|
|
|
.*antipope\.org matches any hostname in the antipope.org domain. |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=item forbidden |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
list of IP addresses or hostnames that are refused permission to |
|
107
|
|
|
|
|
|
|
connect to the server. If empty, the default policy is to refuse |
|
108
|
|
|
|
|
|
|
connections from anyone not in the 'allowed' list (unless the |
|
109
|
|
|
|
|
|
|
allowed list is empty, in which case anyone may connect). |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=item callback |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
Coderef to a subroutine which handles incoming connections (called |
|
114
|
|
|
|
|
|
|
with one parameter -- a C object which can be used |
|
115
|
|
|
|
|
|
|
to shut down the session). |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=item mode |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
Can be one of B, B |
|
120
|
|
|
|
|
|
|
B, or B. |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
By default, B mode is selected. |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
B mode is selected, the server handles requests by forking a |
|
125
|
|
|
|
|
|
|
child process to service them. If B |
|
126
|
|
|
|
|
|
|
uses the C class to implement a simple non-forking server. |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
The select-based server may block on i/o on a heavily-loaded system. If |
|
129
|
|
|
|
|
|
|
you need to do non-blocking i/o you should look at NetServer::FastSelect. |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
The B mode is special; it indicates that rather than sitting |
|
132
|
|
|
|
|
|
|
around waiting for an incoming connection, the server is itself a |
|
133
|
|
|
|
|
|
|
TCP/IP client. In client mode, C is the B host to |
|
134
|
|
|
|
|
|
|
connect to and C is the remote port to open. The callback |
|
135
|
|
|
|
|
|
|
routine is used, as elsewhere, but it should be written as for a |
|
136
|
|
|
|
|
|
|
client -- i.e. it should issue a request or command, then read. |
|
137
|
|
|
|
|
|
|
An additional method exists for client mode: C. C |
|
138
|
|
|
|
|
|
|
expects a coderef as a parameter. This coderef is executed |
|
139
|
|
|
|
|
|
|
before the client-mode server spawns a child; if it returns a non-zero |
|
140
|
|
|
|
|
|
|
value the child is forked and opens a client connection to the target |
|
141
|
|
|
|
|
|
|
host, otherwise the server exits. The trigger method may be used to |
|
142
|
|
|
|
|
|
|
sleep for a random interval then return 1 (so that repeated clients |
|
143
|
|
|
|
|
|
|
are spawned at random intervals), or fork several children (on a one- |
|
144
|
|
|
|
|
|
|
time-only basis) then work as above (so that several clients poke at |
|
145
|
|
|
|
|
|
|
the target server on a random basis). The default trigger method |
|
146
|
|
|
|
|
|
|
returns 1 immediately the first time it is called, then returns 0 -- |
|
147
|
|
|
|
|
|
|
this means that the client makes a single connection to the target |
|
148
|
|
|
|
|
|
|
host, invokes the callback routine, then exits. (See the test examples |
|
149
|
|
|
|
|
|
|
which come with this module for examples of how to use client mode.) |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
Note that client mode relies on the fork() system call. |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
The B mode indicates that multithreading will be used to |
|
154
|
|
|
|
|
|
|
service requests. This feature requires Perl 5.005 or higher and a |
|
155
|
|
|
|
|
|
|
native threads library to run, so it's not 100% portable). Moreover, |
|
156
|
|
|
|
|
|
|
it's unreliable! Don't use this mode unless you're prepared to do some |
|
157
|
|
|
|
|
|
|
debugging. |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
The B mode indicates that the server will bind to the |
|
160
|
|
|
|
|
|
|
designated port, then fork repeatedly up to C<$start_servers> times |
|
161
|
|
|
|
|
|
|
(where C is a scalar parameter to C). |
|
162
|
|
|
|
|
|
|
Each child then enters a select-based loop. (i.e. run_select), but exits |
|
163
|
|
|
|
|
|
|
after handling C<$server_lifespan> transactions (where C |
|
164
|
|
|
|
|
|
|
is another parameter to C). Every time a child |
|
165
|
|
|
|
|
|
|
handles a transaction it writes its PID and generation number down a pipe |
|
166
|
|
|
|
|
|
|
to the parent process, with a message when it exits. The parent keeps |
|
167
|
|
|
|
|
|
|
track of how many servers are in use and fires up extra children (up to |
|
168
|
|
|
|
|
|
|
C<$max_servers>) if the number in use leaves less than C<$min_spare_servers> |
|
169
|
|
|
|
|
|
|
free. See the example B for a minimal HTTP 0.9 server |
|
170
|
|
|
|
|
|
|
implemented using the B mode. |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=back |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
Of these, the C method is most important; it specifies |
|
176
|
|
|
|
|
|
|
a reference to a subroutine which effectively does whatever the |
|
177
|
|
|
|
|
|
|
server does. |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
A callback subroutine is a normal Perl subroutine. It is invoked |
|
180
|
|
|
|
|
|
|
with STDIN and STDOUT attached to an C object, |
|
181
|
|
|
|
|
|
|
so that reads from STDIN get information from the client, and writes |
|
182
|
|
|
|
|
|
|
to STDOUT send information to the client. Note that both STDIN and |
|
183
|
|
|
|
|
|
|
STDOUT are unbuffered. In addition, a C object is |
|
184
|
|
|
|
|
|
|
passed as an argument (but the C is free to ignore it). |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
Your server reads and writes data via the socket as if it is the |
|
187
|
|
|
|
|
|
|
standard input and standard output filehandles; for example: |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
while (defined ($tmp = )) { # read a line from the socket |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
print STDOUT "You said: $tmp\n"; # print something to the socket |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
(See C and C for more information on this.) |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
If you're not familiar with sockets, don't get too fresh and try to |
|
196
|
|
|
|
|
|
|
close or seek on STDIN or STDOUT; just treat them like a file. |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
The server object is not strictly necessary in the callback, but comes |
|
199
|
|
|
|
|
|
|
in handy: you can shut down the server completely by calling the |
|
200
|
|
|
|
|
|
|
C method. |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
When writing a callback subroutine, remember to define some condition under |
|
203
|
|
|
|
|
|
|
which you return! |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
Here's a slightly more complex server example: |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# minimal http server (HTTP/0.9): |
|
209
|
|
|
|
|
|
|
# this is a REALLY minimal HTTP server. It only understands GET |
|
210
|
|
|
|
|
|
|
# requests, does no header parsing whatsoever, and doesn't understand |
|
211
|
|
|
|
|
|
|
# relative addresses! Nor does it understand CGI scripts. And it ain't |
|
212
|
|
|
|
|
|
|
# suitable as a replacement for Apache (at least, not any time soon :). |
|
213
|
|
|
|
|
|
|
# The base directory for the server and the default |
|
214
|
|
|
|
|
|
|
# file name are defined in B, which maps URLs to |
|
215
|
|
|
|
|
|
|
# absolute pathnames. The server code itself is defined in the |
|
216
|
|
|
|
|
|
|
# closure B<$http>, which shows how simple it is to write a server |
|
217
|
|
|
|
|
|
|
# using this module. |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
sub url_to_file($) { |
|
220
|
|
|
|
|
|
|
# for a given URL, turn it into an absolute pathname |
|
221
|
|
|
|
|
|
|
my ($u) = shift ; # incoming URL fragment from GET request |
|
222
|
|
|
|
|
|
|
my ($f) = ""; # file pathname to return |
|
223
|
|
|
|
|
|
|
my ($htbase) = "/usr/local/etc/httpd/docs/"; |
|
224
|
|
|
|
|
|
|
my ($htdefault) = "index.html"; |
|
225
|
|
|
|
|
|
|
chop $u; |
|
226
|
|
|
|
|
|
|
if ($u eq "/") { |
|
227
|
|
|
|
|
|
|
$f = $htbase . $htdefault; |
|
228
|
|
|
|
|
|
|
return $f; |
|
229
|
|
|
|
|
|
|
} else { |
|
230
|
|
|
|
|
|
|
if ($u =~ m|^/.+|) { |
|
231
|
|
|
|
|
|
|
$f = $htbase; chop $f; |
|
232
|
|
|
|
|
|
|
$f .= $u; |
|
233
|
|
|
|
|
|
|
} elsif ($u =~ m|[^/]+|) { |
|
234
|
|
|
|
|
|
|
$f = $htbase . $u; |
|
235
|
|
|
|
|
|
|
} |
|
236
|
|
|
|
|
|
|
if ($u =~ m|.+/$|) { |
|
237
|
|
|
|
|
|
|
$f .= $htdefault; |
|
238
|
|
|
|
|
|
|
} |
|
239
|
|
|
|
|
|
|
if ($f =~ /\.\./) { |
|
240
|
|
|
|
|
|
|
my (@path) = split("/", $f); |
|
241
|
|
|
|
|
|
|
my ($buff, $acc) = ""; |
|
242
|
|
|
|
|
|
|
shift @path; |
|
243
|
|
|
|
|
|
|
while ($buff = shift @path) { |
|
244
|
|
|
|
|
|
|
my ($tmp) = shift @path; |
|
245
|
|
|
|
|
|
|
if ($tmp ne '..') { |
|
246
|
|
|
|
|
|
|
unshift @path, $tmp; |
|
247
|
|
|
|
|
|
|
$acc .= "/$buff"; |
|
248
|
|
|
|
|
|
|
} |
|
249
|
|
|
|
|
|
|
} |
|
250
|
|
|
|
|
|
|
$f = $acc; |
|
251
|
|
|
|
|
|
|
} |
|
252
|
|
|
|
|
|
|
} |
|
253
|
|
|
|
|
|
|
return $f; |
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
my ($http) = sub { |
|
257
|
|
|
|
|
|
|
my ($fh) = shift ; |
|
258
|
|
|
|
|
|
|
while (defined ($tmp = )) { |
|
259
|
|
|
|
|
|
|
chomp $tmp; |
|
260
|
|
|
|
|
|
|
if ($tmp =~ /^GET\s+(.*)$/i) { |
|
261
|
|
|
|
|
|
|
$getfile = $1; |
|
262
|
|
|
|
|
|
|
$getfile = url_to_file($getfile); |
|
263
|
|
|
|
|
|
|
print STDERR "Sending $getfile\n"; |
|
264
|
|
|
|
|
|
|
my ($in) = new IO::File(); |
|
265
|
|
|
|
|
|
|
if ($in->open("<$getfile") ) { |
|
266
|
|
|
|
|
|
|
$in->autoflush(1); |
|
267
|
|
|
|
|
|
|
print STDOUT "Content-type: text/html\n\n"; |
|
268
|
|
|
|
|
|
|
while (defined ($line = <$in>)) { |
|
269
|
|
|
|
|
|
|
print STDOUT $line; |
|
270
|
|
|
|
|
|
|
} |
|
271
|
|
|
|
|
|
|
} else { |
|
272
|
|
|
|
|
|
|
print STDOUT "404: File not found\n\n"; |
|
273
|
|
|
|
|
|
|
} |
|
274
|
|
|
|
|
|
|
} |
|
275
|
|
|
|
|
|
|
return 0; |
|
276
|
|
|
|
|
|
|
} |
|
277
|
|
|
|
|
|
|
}; |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
# main program starts here |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
my (%config) = ("port" => 9000, |
|
282
|
|
|
|
|
|
|
"callback" => $http, |
|
283
|
|
|
|
|
|
|
"hostname" => "public.antipope.org"); |
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
my ($allowed) = ['.*antipope\.org', |
|
286
|
|
|
|
|
|
|
'.*localhost.*']; |
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
my ($forbidden) = [ '194\.205\.10\.2']; |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
my ($foo) = new Server(%config); # create new http server bound to port |
|
291
|
|
|
|
|
|
|
# 9000 of public.antipope.org |
|
292
|
|
|
|
|
|
|
$foo->allowed($allowed); # who is allowed to connect to us |
|
293
|
|
|
|
|
|
|
$foo->forbidden($forbidden); # who is refused access |
|
294
|
|
|
|
|
|
|
print "Starting http server on port 9000\n"; |
|
295
|
|
|
|
|
|
|
$foo->run(); |
|
296
|
|
|
|
|
|
|
exit 0; |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=head2 Additional methods |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
C provides a couple of extra methods. |
|
302
|
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
=over 4 |
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
=item peer() |
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
The B method returns a reference to a two-element list containing |
|
308
|
|
|
|
|
|
|
the hostname and IP address of the host at the other end of the socket. |
|
309
|
|
|
|
|
|
|
If called before a connection has been received, its value will be undefined. |
|
310
|
|
|
|
|
|
|
(Don't try to assign values via B unless you want to confuse the |
|
311
|
|
|
|
|
|
|
allowed/forbidden checking code!) |
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
=item quit() |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
The B method attempts to shut down a server. If running as a forking |
|
316
|
|
|
|
|
|
|
service, it does so by sending a kill -15 to the parent process. If running |
|
317
|
|
|
|
|
|
|
as a select-based service it returns from B. |
|
318
|
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
=item start_servers() |
|
320
|
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
In B mode, specifies how many child servers to start up. |
|
322
|
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
=item max_servers() |
|
324
|
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
In B mode, specifies the maximum number of children to spawn |
|
326
|
|
|
|
|
|
|
under load. |
|
327
|
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
=item min_spare_servers() |
|
329
|
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
In B mode, specifies a number of spare (inactive) child |
|
331
|
|
|
|
|
|
|
servers; if we drop below this level (due to load), the parent will spawn |
|
332
|
|
|
|
|
|
|
additional children (up to a maximum of B) until we go back |
|
333
|
|
|
|
|
|
|
over B. |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=item server_lifespan() |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
In B server mode, child servers run as select servers. After |
|
338
|
|
|
|
|
|
|
B connections they will commit suicide and be replaced by |
|
339
|
|
|
|
|
|
|
the parent. If B is set to 1, children will effectively |
|
340
|
|
|
|
|
|
|
run once then exit (like a forking server). For purposes of insanity, |
|
341
|
|
|
|
|
|
|
a lifespan of 0 is treated like a lifespan of 1. |
|
342
|
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
=item servername() |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
In the B server, unless you I tell the server to bind |
|
346
|
|
|
|
|
|
|
to a named host, it will accept all incoming connections. Within a client, |
|
347
|
|
|
|
|
|
|
you may need to know what local IP address an incoming connection was |
|
348
|
|
|
|
|
|
|
intended for. The C method can be invoked within the child |
|
349
|
|
|
|
|
|
|
server's callback and returns a two-element arrayref containing the port |
|
350
|
|
|
|
|
|
|
and IP address that the connection came in on. For example, in the client: |
|
351
|
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
my $callback = sub { |
|
353
|
|
|
|
|
|
|
my $server = shift; |
|
354
|
|
|
|
|
|
|
my ($server_port, $server_addr) = @{ $server->servername() }; |
|
355
|
|
|
|
|
|
|
print "Connection on $server_addr:$server_port\n"; |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
=back |
|
359
|
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
=head2 Types of server |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
A full discussion of internet servers is well beyond the scope of this man |
|
363
|
|
|
|
|
|
|
page. Beginners may want to start with a source like L
|
|
364
|
|
|
|
|
|
|
Programming> (which provides a simple, lucid discussion); more advanced |
|
365
|
|
|
|
|
|
|
readers may find Stevens' L |
|
366
|
|
|
|
|
|
|
useful. |
|
367
|
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
In general, on non-threaded systems, a forking server is slightly less |
|
369
|
|
|
|
|
|
|
efficient than a select-based server (and uses up lots of PIDs). On the other |
|
370
|
|
|
|
|
|
|
hand, a select-based server is not a good solution to high workloads or |
|
371
|
|
|
|
|
|
|
time-consuming processes such as providing an NNTP news feed to an online |
|
372
|
|
|
|
|
|
|
newsreader. |
|
373
|
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
A major issue with the select-based server code in this release is that |
|
375
|
|
|
|
|
|
|
the IO::Select based server cannot know that a socket is ready until some |
|
376
|
|
|
|
|
|
|
data is received over it. (It calls B to detect sockets waiting |
|
377
|
|
|
|
|
|
|
to be read from.) Thus, it is not suitable for writing servers like |
|
378
|
|
|
|
|
|
|
which emit status information without first reading a request. |
|
379
|
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
382
|
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
L, |
|
384
|
|
|
|
|
|
|
L, |
|
385
|
|
|
|
|
|
|
L, |
|
386
|
|
|
|
|
|
|
L, |
|
387
|
|
|
|
|
|
|
L |
|
388
|
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
=head1 BUGS |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
There are two bugs lurking in NetServer::Generic. Or maybe they're |
|
392
|
|
|
|
|
|
|
design flaws. I don't have time to fix them right now, but maybe |
|
393
|
|
|
|
|
|
|
you'd like to contribute an hour or two and get your name in the |
|
394
|
|
|
|
|
|
|
credits? |
|
395
|
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
Bug the first: |
|
397
|
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
NetServer::Generic attempts to make it easy to write a server by letting |
|
399
|
|
|
|
|
|
|
the programmer concentrate on reading from STDIN and writing to STDOUT. |
|
400
|
|
|
|
|
|
|
However, this form of i/o is line oriented. NetServer::Generic relies |
|
401
|
|
|
|
|
|
|
on the buffering and i/o capabilities provided by Perl and IO::Socket |
|
402
|
|
|
|
|
|
|
respectively. It doesn't buffer its own input. |
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
This means that in principle a malicious attacker (or just a badly- |
|
405
|
|
|
|
|
|
|
written client program) can write a stream of bytes to a |
|
406
|
|
|
|
|
|
|
NetServer::Generic application and, as long as those bytes don't |
|
407
|
|
|
|
|
|
|
include a "\n", Perl will keep gobbling it up until it runs out of |
|
408
|
|
|
|
|
|
|
virtual memory. |
|
409
|
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
This can be fixed by replacing the globbed IO::Socket::INET that is |
|
411
|
|
|
|
|
|
|
attached to STDIN with something else -- probably an object that presents |
|
412
|
|
|
|
|
|
|
itself as an IO::Stringy but that does its own buffering, so that it |
|
413
|
|
|
|
|
|
|
will return I a line, or some sort of error message in $! if |
|
414
|
|
|
|
|
|
|
it sees something undigestible in its input stream. (If anyone wants |
|
415
|
|
|
|
|
|
|
to contribute a patch that fixes this, please feel free; this is an open |
|
416
|
|
|
|
|
|
|
source project, after all ...) |
|
417
|
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
Bug the second: |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
The select-based server was originally written because I wanted to |
|
421
|
|
|
|
|
|
|
share state information between some forking servers and I couldn't |
|
422
|
|
|
|
|
|
|
use System V shared memory (the application had to be portable to a |
|
423
|
|
|
|
|
|
|
flavour of UNIX that didn't support it). |
|
424
|
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
It works okay, up to a point, but under heavy load on Linux it can run |
|
426
|
|
|
|
|
|
|
into major problems. Partly this may be attributable to deficiencies |
|
427
|
|
|
|
|
|
|
in the way Linux handles the select() system call (or so Stephen |
|
428
|
|
|
|
|
|
|
Tweedie keeps telling me), but the result is that the select-based |
|
429
|
|
|
|
|
|
|
server tends to drop some connections when it's under stress: if |
|
430
|
|
|
|
|
|
|
two connections come in while it's serving another, the first may |
|
431
|
|
|
|
|
|
|
never get processed before a timeout occurs. |
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
A somewhat worse problem is that IO::Select doesn't do buffered (line- |
|
434
|
|
|
|
|
|
|
oriented) input; it just checks to see if one or more bytes are |
|
435
|
|
|
|
|
|
|
waiting to be read from one of the file handles it's got hold of. It |
|
436
|
|
|
|
|
|
|
is possible for a couple of bytes to come in (but not a whole line), |
|
437
|
|
|
|
|
|
|
so that the select-based server merrily tries to process a transaction |
|
438
|
|
|
|
|
|
|
and blocks until the rest of the input arrives -- thus ensuring that |
|
439
|
|
|
|
|
|
|
the server is bottlenecked by the speed of the slowest client connection. |
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
Suggestion: if you need to serve lots of connections using select(), |
|
442
|
|
|
|
|
|
|
look at the eventserver module instead. If you're a bit more |
|
443
|
|
|
|
|
|
|
ambitious, the defect in NetServer::Generic is fixable by writing a |
|
444
|
|
|
|
|
|
|
module with a similar API to IO::Select, but which provides buffering |
|
445
|
|
|
|
|
|
|
for the file handles under its control and which only returns |
|
446
|
|
|
|
|
|
|
something in response to can_read() when one of the buffers has a |
|
447
|
|
|
|
|
|
|
complete line of input waiting. |
|
448
|
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
=head1 AUTHOR |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
Charlie Stross (charle@antipope.org). With thanks for bugfixes and |
|
452
|
|
|
|
|
|
|
patches to Marius Kjeldahl I, Art Sackett |
|
453
|
|
|
|
|
|
|
I, Claudio Garcia I, |
|
454
|
|
|
|
|
|
|
Claudio Calvelli I, Martin Waite |
|
455
|
|
|
|
|
|
|
I. Debian package |
|
456
|
|
|
|
|
|
|
contributed by Jon Middleton, I. |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
=head1 HISTORY |
|
459
|
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
=over 4 |
|
461
|
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
=item Version 0.1 |
|
463
|
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
Based on the simple forking server in Chapter 10 of "Advanced Perl |
|
465
|
|
|
|
|
|
|
Programming" by Sriram Srinivasan, with a modular wrapper to make |
|
466
|
|
|
|
|
|
|
it easy to use and configure, and a rudimentary access control system. |
|
467
|
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
=item Version 0.2 |
|
469
|
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
Added the B method to provide peer information. |
|
471
|
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
Bugfix to B from Marius Kjeldahl I. |
|
473
|
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
Added select-based server code, B method to switch between forking |
|
475
|
|
|
|
|
|
|
and selection server modes. |
|
476
|
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
Updated test code (should do something now!) |
|
478
|
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
Added example: fortune server and client code. |
|
480
|
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
Supports NetServer::SMTP (and, internally, NetServer::vTID). |
|
482
|
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
=item Version 0.3 |
|
484
|
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
fixed test failure. |
|
486
|
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
=item Version 1.0 |
|
488
|
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
Added alpha-ish prefork server mode. |
|
490
|
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
Added alpha-ish multithreaded mode (UNSTABLE) |
|
492
|
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
Modified IP address filtering to cope with regexps |
|
494
|
|
|
|
|
|
|
(suggested by Art Sackett I) |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
Modified select() server to do non-blocking writes via a |
|
497
|
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
Non-blocking-socket class tied to STDIN/STDOUT |
|
499
|
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
Option to log new connection peer addresses via STDERR |
|
501
|
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
Extra test scripts |
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
Updated documentation |
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
=item 1.01 |
|
507
|
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
Fix so it works on installations with no threading support (duh). Tested |
|
509
|
|
|
|
|
|
|
on Solaris, too. |
|
510
|
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
=item 1.02 |
|
512
|
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
Bugfixes to the preforked mode (thanks to Art Sackett for detecting |
|
514
|
|
|
|
|
|
|
them). Bugfix to ok_to_serve() (thanks to Claudio Garcia, |
|
515
|
|
|
|
|
|
|
cgarcia@dbitech.com). Some notes on the two known bugs (related |
|
516
|
|
|
|
|
|
|
to buffering). |
|
517
|
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
=item 1.03 |
|
519
|
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
Signal handling code was fixed to avoid leaving zombie processes |
|
521
|
|
|
|
|
|
|
(thanks to Luis Munoz, lem@cantv.net) |
|
522
|
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
=back |
|
524
|
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=cut |
|
527
|
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
# NetServer::FieldTypes contains a hash of autoload method names, and the |
|
529
|
|
|
|
|
|
|
# type of parameter they expect. For example, NetServer->callback() takes |
|
530
|
|
|
|
|
|
|
# a coderef as a parameter; AUTOLOAD needs to know this so it can whine |
|
531
|
|
|
|
|
|
|
# about incorrect parameter types. |
|
532
|
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
$NetServer::FieldTypes = { |
|
534
|
|
|
|
|
|
|
"port" => "scalar", |
|
535
|
|
|
|
|
|
|
"callback" => "code", |
|
536
|
|
|
|
|
|
|
"listen" => "scalar", |
|
537
|
|
|
|
|
|
|
"proto" => "scalar", |
|
538
|
|
|
|
|
|
|
"hostname" => "scalar", |
|
539
|
|
|
|
|
|
|
"timeout" => "scalar", |
|
540
|
|
|
|
|
|
|
"root_pid" => "scalar", |
|
541
|
|
|
|
|
|
|
"allowed" => "array", |
|
542
|
|
|
|
|
|
|
"forbidden" => "array", |
|
543
|
|
|
|
|
|
|
"peer" => "array", |
|
544
|
|
|
|
|
|
|
"mode" => "scalar", |
|
545
|
|
|
|
|
|
|
"trigger" => "code", |
|
546
|
|
|
|
|
|
|
"sock" => "IO::Socket::INET", |
|
547
|
|
|
|
|
|
|
"tags" => "hash", |
|
548
|
|
|
|
|
|
|
"my_age" => "scalar", |
|
549
|
|
|
|
|
|
|
"start_servers" => "scalar", |
|
550
|
|
|
|
|
|
|
"min_spare_servers" => "scalar", |
|
551
|
|
|
|
|
|
|
"max_servers" => "scalar", |
|
552
|
|
|
|
|
|
|
"server_lifespan" => "scalar", |
|
553
|
|
|
|
|
|
|
"fifo" => "scalar", |
|
554
|
|
|
|
|
|
|
"read_pipe" => "scalar", |
|
555
|
|
|
|
|
|
|
"write_pipe" => "scalar", |
|
556
|
|
|
|
|
|
|
"handle" => "IO::File", |
|
557
|
|
|
|
|
|
|
"scoreboard" => "hash", |
|
558
|
|
|
|
|
|
|
"servername" => "array", |
|
559
|
|
|
|
|
|
|
"parent_callback" => "code", |
|
560
|
|
|
|
|
|
|
"ante_parent_callback" => "code", |
|
561
|
|
|
|
|
|
|
}; |
|
562
|
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
# $NetServer::Debug; if non-zero, emit some debugging info on STDERR |
|
564
|
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
$NetServer::Debug = 0; |
|
566
|
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
# here is a default callback routine. It basically echoes back anything |
|
568
|
|
|
|
|
|
|
# you sent to the server, unless the line begins with quit, bye, or |
|
569
|
|
|
|
|
|
|
# exit -- in which case it kills the server (rather than simply exiting). |
|
570
|
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
$NetServer::default_cb = sub { |
|
572
|
|
|
|
|
|
|
my ($s) = shift; |
|
573
|
|
|
|
|
|
|
my ($tmp) = ""; |
|
574
|
|
|
|
|
|
|
print STDOUT "Echo server: type bye to quit, ", |
|
575
|
|
|
|
|
|
|
"exit to kill the server.\n\n" ; |
|
576
|
|
|
|
|
|
|
while (defined ($tmp = )) { |
|
577
|
|
|
|
|
|
|
return if ($tmp =~ /^bye/i); |
|
578
|
|
|
|
|
|
|
$s->quit() if ($tmp =~ /^exit/i); |
|
579
|
|
|
|
|
|
|
print STDOUT "You said:>$tmp\n"; |
|
580
|
|
|
|
|
|
|
} |
|
581
|
|
|
|
|
|
|
}; |
|
582
|
|
|
|
|
|
|
# Methods |
|
583
|
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
sub new { |
|
585
|
28
|
50
|
|
28
|
0
|
259
|
$NetServer::Debug && print STDERR "[", join("][", @_), "]\n"; |
|
586
|
28
|
50
|
|
|
|
378
|
my ($class) = shift if @_; |
|
587
|
28
|
|
|
|
|
1066
|
my ($self) = {"listen" => 5, |
|
588
|
|
|
|
|
|
|
"timeout" => 60, |
|
589
|
|
|
|
|
|
|
"hostname" => "localhost", |
|
590
|
|
|
|
|
|
|
"proto" => "tcp", |
|
591
|
|
|
|
|
|
|
"callback" => $NetServer::default_cb, |
|
592
|
|
|
|
|
|
|
"version" => $NetServer::Generic::VERSION, |
|
593
|
|
|
|
|
|
|
}; |
|
594
|
28
|
|
|
|
|
226
|
$self->{tags} = $NetServer::FieldTypes; |
|
595
|
28
|
|
50
|
|
|
341
|
bless $self, ($class or "Server"); |
|
596
|
28
|
50
|
|
|
|
180
|
if (@_) { |
|
597
|
0
|
|
|
|
|
0
|
my (%tmp) = @_; my ($field) = ""; |
|
|
0
|
|
|
|
|
0
|
|
|
598
|
0
|
|
|
|
|
0
|
foreach $field (keys %tmp) { |
|
599
|
0
|
|
|
|
|
0
|
$self->$field($tmp{$field}); |
|
600
|
|
|
|
|
|
|
} |
|
601
|
|
|
|
|
|
|
} |
|
602
|
28
|
|
|
|
|
299
|
return $self; |
|
603
|
|
|
|
|
|
|
} |
|
604
|
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
sub VERSION { |
|
606
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
607
|
0
|
|
|
|
|
0
|
return $self->{version}; |
|
608
|
|
|
|
|
|
|
} |
|
609
|
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
sub run_prefork { |
|
611
|
2
|
|
|
2
|
0
|
6
|
my $self = shift; |
|
612
|
|
|
|
|
|
|
# get preforking parameters or adopt sensible default values |
|
613
|
2
|
|
50
|
|
|
16
|
my $start_servers = ($self->start_servers() or 5 ); |
|
614
|
2
|
|
50
|
|
|
8
|
my $spare_servers = ($self->min_spare_servers() or 1 ); |
|
615
|
2
|
|
50
|
|
|
22
|
my $max_servers = ($self->max_servers() or 10 ); |
|
616
|
2
|
|
50
|
|
|
18
|
my $server_lifespan = ($self->server_lifespan() or 1000 ); |
|
617
|
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
# Create socket and bind, then Fork repeatedly up to $start_servers times. |
|
619
|
|
|
|
|
|
|
# Once in each child, do a select-based loop. i.e. run_select, but exit |
|
620
|
|
|
|
|
|
|
# after handling $server_lifespan transactions. |
|
621
|
|
|
|
|
|
|
# Every time we do a task we write our PID and generation number down a |
|
622
|
|
|
|
|
|
|
# pipe to the parent process, with a message when we exit. |
|
623
|
|
|
|
|
|
|
# |
|
624
|
|
|
|
|
|
|
# In the parent, keep track of how many servers are in use |
|
625
|
|
|
|
|
|
|
# and fire up extra children (up to $max_servers) if the number in |
|
626
|
|
|
|
|
|
|
# use leaves less than $spare_servers free. |
|
627
|
2
|
|
|
|
|
8
|
my %init = ( |
|
628
|
|
|
|
|
|
|
LocalPort => $self->port(), |
|
629
|
|
|
|
|
|
|
Listen => $self->listen(), |
|
630
|
|
|
|
|
|
|
Proto => $self->proto(), |
|
631
|
|
|
|
|
|
|
Reuse => 1 |
|
632
|
|
|
|
|
|
|
); |
|
633
|
2
|
50
|
|
|
|
10
|
if ($self->hostname() ne "") { |
|
634
|
2
|
|
|
|
|
8
|
$init{LocalAddr} = $self->hostname(); |
|
635
|
|
|
|
|
|
|
} |
|
636
|
2
|
|
|
|
|
102
|
my ($main_sock) = new IO::Socket::INET(%init); |
|
637
|
2
|
50
|
|
|
|
3070
|
if (! $main_sock) { |
|
638
|
0
|
|
|
|
|
0
|
print STDERR "$$:run_select(): could not create socket: $!\n"; |
|
639
|
0
|
|
|
|
|
0
|
exit 0; |
|
640
|
|
|
|
|
|
|
} |
|
641
|
2
|
|
|
|
|
20
|
$self->sock($main_sock); |
|
642
|
2
|
50
|
|
|
|
8
|
$NetServer::Debug && print STDERR |
|
643
|
|
|
|
|
|
|
"Created socket(port => ", $self->port(), "\n", |
|
644
|
|
|
|
|
|
|
" " x 15, "hostname => ", $self->hostname(), ")\n"; |
|
645
|
2
|
|
|
|
|
18
|
my $scoreboard = {}; |
|
646
|
2
|
|
|
|
|
34
|
$self->scoreboard($scoreboard); |
|
647
|
|
|
|
|
|
|
# set up named pipe -- children will write, parent will read |
|
648
|
|
|
|
|
|
|
#my $fifo = $self->_new_fifo(); |
|
649
|
|
|
|
|
|
|
#$self->fifo($fifo); |
|
650
|
|
|
|
|
|
|
# switch to using a pipe instead |
|
651
|
2
|
|
|
|
|
26
|
pipe(READ_PIPE, WRITE_PIPE); |
|
652
|
2
|
|
|
|
|
12
|
$self->{read_pipe} = *READ_PIPE; |
|
653
|
2
|
|
|
|
|
14
|
$self->{write_pipe} = *WRITE_PIPE; |
|
654
|
2
|
|
|
|
|
20
|
$self->root_pid($$); # set server root PID |
|
655
|
|
|
|
|
|
|
# now create lots of spawn |
|
656
|
2
|
|
|
|
|
22
|
for (my $i = 0; $i < $start_servers; $i++) { |
|
657
|
3
|
|
|
|
|
4267
|
my $pid = fork(); |
|
658
|
3
|
50
|
|
|
|
278
|
die "Cannot fork: $!\n" unless defined ($pid); |
|
659
|
3
|
100
|
|
|
|
107
|
if ($pid == 0) { |
|
660
|
|
|
|
|
|
|
# child |
|
661
|
2
|
|
|
|
|
271
|
$self->_do_preforked_child(); |
|
662
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR "$0:$$: end of transaction\n"; |
|
663
|
0
|
|
|
|
|
0
|
exit 0; |
|
664
|
|
|
|
|
|
|
} else { |
|
665
|
|
|
|
|
|
|
# parent |
|
666
|
1
|
|
|
|
|
60
|
$scoreboard->{$pid} = "idle"; |
|
667
|
1
|
50
|
|
|
|
17
|
$NetServer::Debug && print STDERR "$0:$$: forked $pid\n"; |
|
668
|
|
|
|
|
|
|
} |
|
669
|
|
|
|
|
|
|
} |
|
670
|
|
|
|
|
|
|
# we have no forked $start_servers children that are |
|
671
|
|
|
|
|
|
|
# in _do_preforked_child(). |
|
672
|
0
|
|
|
|
|
0
|
$self->scoreboard($scoreboard); |
|
673
|
0
|
|
|
|
|
0
|
$self->_do_preforked_parent(); |
|
674
|
0
|
|
|
|
|
0
|
return; |
|
675
|
|
|
|
|
|
|
} |
|
676
|
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
sub reap_child { |
|
678
|
2
|
|
|
2
|
0
|
114
|
do {} while waitpid(-1, WNOHANG) > 0; |
|
679
|
|
|
|
|
|
|
} |
|
680
|
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
sub _do_preforked_parent { |
|
682
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
683
|
|
|
|
|
|
|
# we are a parent process to a bunch of raucous kiddies. We have an |
|
684
|
|
|
|
|
|
|
# IO::Pipe called $self->reader() that we read status from and stick |
|
685
|
|
|
|
|
|
|
# in a scoreboard. As processes die, we replace them. As the scoreboard |
|
686
|
|
|
|
|
|
|
# fills up, we add extra servers. NB: when we fork, we replicate |
|
687
|
|
|
|
|
|
|
# self->reader() and self->writer(). |
|
688
|
|
|
|
|
|
|
|
|
689
|
0
|
|
|
|
|
0
|
my $n = "_do_preforked_adult($$)"; # for reporting status |
|
690
|
0
|
|
0
|
|
|
0
|
my $start_servers = ( $self->start_servers() or 5 ); |
|
691
|
0
|
|
0
|
|
|
0
|
my $spare_servers = ( $self->min_spare_servers() or 1 ); |
|
692
|
0
|
|
0
|
|
|
0
|
my $max_servers = ( $self->max_servers() or 10 ); |
|
693
|
0
|
|
0
|
|
|
0
|
my $scoreboard = ( $self->scoreboard() or {} ); |
|
694
|
0
|
|
|
|
|
0
|
$SIG{CHLD} = \&reap_child; |
|
695
|
0
|
|
|
|
|
0
|
my @buffer = (); |
|
696
|
0
|
|
|
|
|
0
|
my $buffer = ""; |
|
697
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR "$n: About to loop on scoreboard file\n"; |
|
698
|
0
|
|
|
|
|
0
|
my $loopcnt = 0; |
|
699
|
0
|
|
|
|
|
0
|
my $busycnt = 0; |
|
700
|
0
|
|
|
|
|
0
|
my @busyvec = (); |
|
701
|
|
|
|
|
|
|
#while(@buffer = $self->_read_fifo()) { |
|
702
|
0
|
|
|
|
|
0
|
*READ_PIPE = $self->read_pipe(); |
|
703
|
0
|
|
|
|
|
0
|
while($buffer = ) { |
|
704
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug |
|
705
|
|
|
|
|
|
|
&& print STDERR "busyvec: [", join("][", @busyvec), "]\n"; |
|
706
|
0
|
|
|
|
|
0
|
$loopcnt++; |
|
707
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug |
|
708
|
|
|
|
|
|
|
&& print STDERR "$n: in pipe read loop $loopcnt\n"; |
|
709
|
0
|
|
|
|
|
0
|
$buffer =~ tr/ //; |
|
710
|
0
|
|
|
|
|
0
|
chomp $buffer; |
|
711
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug |
|
712
|
|
|
|
|
|
|
&& print STDERR "$n: buffer: $buffer\n"; |
|
713
|
0
|
|
|
|
|
0
|
my ($child_pid, $status) = split(/:/, $buffer); |
|
714
|
|
|
|
|
|
|
# kids write $$:busy or $$:idle into the pipe whenever |
|
715
|
|
|
|
|
|
|
# they change state. |
|
716
|
0
|
0
|
|
|
|
0
|
if ($status eq "exit") { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
# a child just exited on us |
|
718
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug |
|
719
|
|
|
|
|
|
|
&& print STDERR "$n: child $child_pid just died\n"; |
|
720
|
0
|
|
|
|
|
0
|
delete($scoreboard->{$child_pid}); |
|
721
|
|
|
|
|
|
|
} elsif ($status eq "busy") { |
|
722
|
0
|
|
|
|
|
0
|
$scoreboard->{$child_pid} = "busy"; |
|
723
|
0
|
|
|
|
|
0
|
push(@busyvec, $child_pid); |
|
724
|
0
|
|
|
|
|
0
|
$busycnt++; |
|
725
|
|
|
|
|
|
|
} elsif ($status eq "idle") { |
|
726
|
0
|
|
|
|
|
0
|
$scoreboard->{$child_pid} = "idle"; |
|
727
|
0
|
|
|
|
|
0
|
@busyvec = grep(!/$child_pid/, @busyvec); |
|
728
|
0
|
|
|
|
|
0
|
$busycnt--; |
|
729
|
|
|
|
|
|
|
} elsif ($status eq "start") { |
|
730
|
0
|
|
|
|
|
0
|
$scoreboard->{$child_pid} = "idle"; |
|
731
|
|
|
|
|
|
|
} |
|
732
|
|
|
|
|
|
|
$NetServer::Debug && print STDERR "$n: $child_pid has status [", |
|
733
|
0
|
0
|
|
|
|
0
|
$scoreboard->{$child_pid}, "]\n", |
|
734
|
|
|
|
|
|
|
"$n: got ", scalar(@busyvec), " busy kids\n"; |
|
735
|
0
|
|
|
|
|
0
|
$busycnt = scalar(@busyvec); |
|
736
|
0
|
|
|
|
|
0
|
my $all_kids = scalar keys %$scoreboard; |
|
737
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && |
|
738
|
|
|
|
|
|
|
print STDERR "$n: $busycnt children busy of $all_kids total\n"; |
|
739
|
|
|
|
|
|
|
# busy_kids is number of kids currently busy; all_kids is number of kids |
|
740
|
0
|
0
|
0
|
|
|
0
|
if ((($all_kids - $busycnt) < $spare_servers) and |
|
741
|
|
|
|
|
|
|
($all_kids <= $max_servers)) { |
|
742
|
0
|
|
|
|
|
0
|
my $kids_to_launch = ($spare_servers - ($all_kids - $busycnt)) +1; |
|
743
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && |
|
744
|
|
|
|
|
|
|
print STDERR "spare servers: $spare_servers, ", |
|
745
|
|
|
|
|
|
|
"all kids: $all_kids, ", |
|
746
|
|
|
|
|
|
|
"busycnt: $busycnt\n", |
|
747
|
|
|
|
|
|
|
"kids to launch = spares - (all - busy) +1 ", |
|
748
|
|
|
|
|
|
|
" => $kids_to_launch\n"; |
|
749
|
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
# launch new children |
|
751
|
0
|
|
|
|
|
0
|
for (my ($i) = 0; $i < $kids_to_launch; $i++) { |
|
752
|
0
|
|
|
|
|
0
|
my $pid = fork(); |
|
753
|
0
|
0
|
|
|
|
0
|
if ($pid == 0) { |
|
754
|
|
|
|
|
|
|
# new child |
|
755
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && |
|
756
|
|
|
|
|
|
|
print STDERR "spawned child\n"; |
|
757
|
0
|
|
|
|
|
0
|
$self->_do_preforked_child(); |
|
758
|
0
|
|
|
|
|
0
|
exit 0; |
|
759
|
|
|
|
|
|
|
} else { |
|
760
|
|
|
|
|
|
|
# parent |
|
761
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR |
|
762
|
|
|
|
|
|
|
"$n: spawned new child $pid\n"; |
|
763
|
0
|
|
|
|
|
0
|
$scoreboard->{$pid} = "idle"; |
|
764
|
|
|
|
|
|
|
} |
|
765
|
|
|
|
|
|
|
} |
|
766
|
|
|
|
|
|
|
} # end of child launch cycle |
|
767
|
|
|
|
|
|
|
$NetServer::Debug |
|
768
|
0
|
0
|
|
|
|
0
|
&& print STDERR "$n: scoreboard: \n", Dumper $scoreboard; |
|
769
|
|
|
|
|
|
|
} |
|
770
|
0
|
|
|
|
|
0
|
print STDERR "exited getline loop\n"; |
|
771
|
|
|
|
|
|
|
} |
|
772
|
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
sub _do_preforked_child { |
|
774
|
2
|
|
|
2
|
|
42
|
my $self = shift; |
|
775
|
|
|
|
|
|
|
# we are a preforked child process. We have an IO::Pipe called |
|
776
|
|
|
|
|
|
|
# $self->writer() that we write strange things to. Each "strange thing" |
|
777
|
|
|
|
|
|
|
# consists of a line containing our PID, a colon, and one of three strings: |
|
778
|
|
|
|
|
|
|
# busy, idle, or exit. We run like a run_select server, except that we |
|
779
|
|
|
|
|
|
|
# write a busy line whenever we accept a connection, an idle line whenever |
|
780
|
|
|
|
|
|
|
# we finish handling a connection, and an exit line when our age exceeds |
|
781
|
|
|
|
|
|
|
# $self->server_lifespan() and we suicide. |
|
782
|
|
|
|
|
|
|
# |
|
783
|
2
|
|
|
|
|
144
|
my $n = "_do_preforked_child($$)"; # for reporting status |
|
784
|
2
|
|
50
|
|
|
351
|
my $server_lifespan = ( $self->server_lifespan() or 1000 ); |
|
785
|
2
|
|
50
|
|
|
154
|
my $my_age = ( $self->my_age() or 0 ); |
|
786
|
2
|
|
|
|
|
27
|
my $main_sock = $self->sock(); |
|
787
|
2
|
|
|
|
|
17
|
my $LOCK_SH = 1; |
|
788
|
2
|
|
|
|
|
19
|
my $LOCK_EX = 2; |
|
789
|
2
|
|
|
|
|
9
|
my $LOCK_NB = 4; |
|
790
|
2
|
|
|
|
|
16
|
my $LOCK_UN = 8; |
|
791
|
2
|
|
|
|
|
276
|
my $rh = new IO::Select($main_sock); |
|
792
|
2
|
50
|
|
|
|
942
|
$NetServer::Debug && print STDERR "$n: Created IO::Select()\n"; |
|
793
|
2
|
|
|
|
|
96
|
*WRITE_PIPE = $self->{write_pipe}; |
|
794
|
2
|
50
|
|
|
|
17
|
$NetServer::Debug |
|
795
|
|
|
|
|
|
|
&& print WRITE_PIPE "$$:start\n"; |
|
796
|
2
|
|
|
|
|
31
|
my (@ready, @err) = (); |
|
797
|
2
|
50
|
|
|
|
30
|
$NetServer::Debug |
|
798
|
|
|
|
|
|
|
&& print STDERR "$n: about to call IO::Select->can_read()\n"; |
|
799
|
|
|
|
|
|
|
SELECT: |
|
800
|
2
|
|
33
|
|
|
140
|
while (@ready = $rh->can_read() or @err = $rh->has_error(0)) { |
|
801
|
8
|
50
|
|
|
|
6376
|
if (scalar(@err) > 0) { |
|
802
|
0
|
|
|
|
|
0
|
foreach my $s (@err) { |
|
803
|
0
|
0
|
|
|
|
0
|
if ($NetServer::Debug > 0) { |
|
804
|
0
|
|
|
|
|
0
|
print STDERR "Sock err: ", $s->error(), "\n"; |
|
805
|
|
|
|
|
|
|
} |
|
806
|
0
|
0
|
|
|
|
0
|
if ($s->eof()) { |
|
807
|
0
|
|
|
|
|
0
|
$rh->remove($s); |
|
808
|
0
|
|
|
|
|
0
|
$s->close(); |
|
809
|
|
|
|
|
|
|
} else { |
|
810
|
0
|
|
|
|
|
0
|
$s->clearerr(); |
|
811
|
|
|
|
|
|
|
} |
|
812
|
|
|
|
|
|
|
} |
|
813
|
0
|
|
|
|
|
0
|
@err = (); |
|
814
|
0
|
|
|
|
|
0
|
next SELECT; |
|
815
|
|
|
|
|
|
|
} |
|
816
|
8
|
50
|
|
|
|
55
|
$NetServer::Debug && print STDERR "$n: got a connection\n"; |
|
817
|
8
|
|
|
|
|
36
|
foreach my $sock (@ready) { |
|
818
|
9
|
50
|
|
|
|
26
|
$NetServer::Debug && print STDERR "$n: got a socket\n"; |
|
819
|
9
|
100
|
|
|
|
48
|
if ($sock == $main_sock) { |
|
820
|
5
|
50
|
|
|
|
98
|
flock($sock, $LOCK_EX) or do { |
|
821
|
0
|
|
|
|
|
0
|
print STDERR "+++ flock LOCK_EX failed on parent socket: ", |
|
822
|
|
|
|
|
|
|
"$!\n"; |
|
823
|
|
|
|
|
|
|
}; |
|
824
|
5
|
|
|
|
|
256
|
my ($new_sock) = $sock->accept(); |
|
825
|
5
|
|
|
|
|
1968
|
flock $sock, $LOCK_UN; |
|
826
|
5
|
|
|
|
|
23
|
$new_sock->autoflush(1); |
|
827
|
5
|
|
|
|
|
193
|
$rh->add($new_sock); |
|
828
|
5
|
50
|
|
|
|
248
|
if (! $self->ok_to_serve($new_sock)) { |
|
829
|
0
|
|
|
|
|
0
|
$rh->remove($sock); |
|
830
|
0
|
|
|
|
|
0
|
close($sock); |
|
831
|
|
|
|
|
|
|
} |
|
832
|
|
|
|
|
|
|
} else { |
|
833
|
4
|
50
|
|
|
|
127
|
if (! eof($sock)) { |
|
834
|
4
|
|
|
|
|
14
|
$my_age++; |
|
835
|
4
|
50
|
|
|
|
20
|
$NetServer::Debug |
|
836
|
|
|
|
|
|
|
&& print STDERR "$n: print WRITE_PIPE ($$:busy)\n"; |
|
837
|
4
|
|
|
|
|
86
|
print WRITE_PIPE "$$:busy\n"; |
|
838
|
4
|
50
|
|
|
|
18
|
$NetServer::Debug |
|
839
|
|
|
|
|
|
|
&& print STDERR "$n: serving connection\n"; |
|
840
|
4
|
|
|
|
|
24
|
$sock->autoflush(1); |
|
841
|
4
|
|
|
|
|
234
|
my ($in_port, $in_addr) = sockaddr_in($sock->sockname()); |
|
842
|
4
|
|
|
|
|
169
|
$self->servername([$in_port, $in_addr]); |
|
843
|
4
|
|
|
|
|
30
|
my ($code) = $self->callback(); |
|
844
|
4
|
|
|
|
|
39
|
$self->sock($sock); |
|
845
|
4
|
|
|
|
|
93
|
*OLD_STDIN = *STDIN; |
|
846
|
4
|
|
|
|
|
35
|
*OLD_STDOUT = *STDOUT; |
|
847
|
4
|
|
|
|
|
12
|
*STDIN = $sock; |
|
848
|
4
|
|
|
|
|
13
|
*STDOUT = $sock; |
|
849
|
4
|
|
|
|
|
13
|
select STDIN; $| = 1; |
|
|
4
|
|
|
|
|
51
|
|
|
850
|
4
|
|
|
|
|
194
|
select STDOUT; $| = 1; |
|
|
4
|
|
|
|
|
11
|
|
|
851
|
4
|
|
|
|
|
265
|
&$code($self); |
|
852
|
3
|
|
|
|
|
30
|
*STDIN = *OLD_STDIN; |
|
853
|
3
|
|
|
|
|
16
|
*STDOUT = *OLD_STDOUT; |
|
854
|
3
|
50
|
|
|
|
19
|
$NetServer::Debug && do { |
|
855
|
0
|
|
|
|
|
0
|
print STDERR "$n: print WRITE_PIPE $$:idle\n", |
|
856
|
|
|
|
|
|
|
"$n: served $my_age calls\n"; |
|
857
|
|
|
|
|
|
|
}; |
|
858
|
3
|
|
|
|
|
38
|
print WRITE_PIPE "$$:idle\n$$:idle\n"; |
|
859
|
3
|
|
|
|
|
33
|
$rh->remove($sock); |
|
860
|
3
|
|
|
|
|
562
|
close $sock; |
|
861
|
|
|
|
|
|
|
} else { |
|
862
|
0
|
|
|
|
|
0
|
$rh->remove($sock); |
|
863
|
0
|
|
|
|
|
0
|
close($sock); |
|
864
|
|
|
|
|
|
|
} |
|
865
|
|
|
|
|
|
|
} |
|
866
|
|
|
|
|
|
|
} |
|
867
|
7
|
50
|
|
|
|
28
|
$NetServer::Debug && print STDERR "$n: checking age $my_age ", |
|
868
|
|
|
|
|
|
|
"against lifespan $server_lifespan\n"; |
|
869
|
7
|
100
|
|
|
|
58
|
if ($my_age >= $server_lifespan) { |
|
870
|
1
|
50
|
|
|
|
6
|
$NetServer::Debug |
|
871
|
|
|
|
|
|
|
&& print STDERR "$n: time to live exceeded\n", |
|
872
|
|
|
|
|
|
|
"$n: print WRITE_PIPE $$:exit\n"; |
|
873
|
|
|
|
|
|
|
#$self->_write_fifo("$$:exit\n"); |
|
874
|
1
|
|
|
|
|
16
|
print WRITE_PIPE "$$:exit\n"; |
|
875
|
1
|
|
|
|
|
446
|
exit 0; |
|
876
|
|
|
|
|
|
|
} |
|
877
|
|
|
|
|
|
|
} |
|
878
|
|
|
|
|
|
|
$NetServer::Debug |
|
879
|
0
|
0
|
|
|
|
0
|
&& print STDERR "Warning! Should never reach this point:", |
|
880
|
|
|
|
|
|
|
join("\n", caller()), "\n"; |
|
881
|
0
|
|
|
|
|
0
|
print WRITE_PIPE "$$:exit\n"; |
|
882
|
0
|
|
|
|
|
0
|
exit 0; |
|
883
|
|
|
|
|
|
|
} |
|
884
|
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
sub run_select { |
|
887
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
888
|
0
|
|
|
|
|
0
|
my ($main_sock) = |
|
889
|
|
|
|
|
|
|
new IO::Socket::INET( # LocalAddr => $self->hostname(), |
|
890
|
|
|
|
|
|
|
LocalPort => $self->port(), |
|
891
|
|
|
|
|
|
|
Listen => $self->listen(), |
|
892
|
|
|
|
|
|
|
Proto => $self->proto(), |
|
893
|
|
|
|
|
|
|
Reuse => 1 |
|
894
|
|
|
|
|
|
|
); |
|
895
|
|
|
|
|
|
|
# die "$$:run_select(): could not create socket: $!\n" unless ($main_sock); |
|
896
|
0
|
0
|
|
|
|
0
|
if (! $main_sock) { |
|
897
|
0
|
|
|
|
|
0
|
print STDERR "$$:run_select(): could not create socket: $!\n"; |
|
898
|
0
|
|
|
|
|
0
|
exit 0; |
|
899
|
|
|
|
|
|
|
} |
|
900
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR "Created socket\n"; |
|
901
|
0
|
|
|
|
|
0
|
my $rh = new IO::Select($main_sock); |
|
902
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR "Created IO::Select()\n"; |
|
903
|
0
|
|
|
|
|
0
|
my (@ready) = (); |
|
904
|
0
|
|
|
|
|
0
|
while (@ready = $rh->can_read() ) { |
|
905
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR |
|
906
|
|
|
|
|
|
|
"NetServer::Generic::run_select(): got ", |
|
907
|
|
|
|
|
|
|
scalar(@ready), " handles at ", |
|
908
|
|
|
|
|
|
|
scalar(localtime(time)), "\n"; |
|
909
|
0
|
|
|
|
|
0
|
my ($sock) = ""; |
|
910
|
0
|
|
|
|
|
0
|
foreach $sock (@ready) { |
|
911
|
0
|
0
|
|
|
|
0
|
if ($sock == $main_sock) { |
|
912
|
0
|
|
|
|
|
0
|
my ($new_sock) = $sock->accept(); |
|
913
|
0
|
|
|
|
|
0
|
$new_sock->autoflush(1); |
|
914
|
0
|
|
|
|
|
0
|
$rh->add($new_sock); |
|
915
|
0
|
0
|
|
|
|
0
|
if (! $self->ok_to_serve($new_sock)) { |
|
916
|
0
|
|
|
|
|
0
|
$rh->remove($sock); |
|
917
|
0
|
|
|
|
|
0
|
close($sock); |
|
918
|
|
|
|
|
|
|
} |
|
919
|
|
|
|
|
|
|
} else { |
|
920
|
0
|
0
|
|
|
|
0
|
if (! eof($sock)) { |
|
921
|
0
|
|
|
|
|
0
|
$sock->autoflush(1); |
|
922
|
0
|
|
|
|
|
0
|
my ($code) = $self->callback(); |
|
923
|
0
|
|
|
|
|
0
|
$self->sock($sock); |
|
924
|
0
|
|
|
|
|
0
|
*STDIN = $sock; |
|
925
|
0
|
|
|
|
|
0
|
*STDOUT = $sock; |
|
926
|
0
|
|
|
|
|
0
|
select STDIN; $| = 1; |
|
|
0
|
|
|
|
|
0
|
|
|
927
|
0
|
|
|
|
|
0
|
select STDOUT; $| = 1; |
|
|
0
|
|
|
|
|
0
|
|
|
928
|
0
|
|
|
|
|
0
|
&$code($self); |
|
929
|
0
|
|
|
|
|
0
|
$rh->remove($sock); |
|
930
|
0
|
|
|
|
|
0
|
close $sock; |
|
931
|
|
|
|
|
|
|
# shutdown($sock, 2); |
|
932
|
|
|
|
|
|
|
} else { |
|
933
|
0
|
|
|
|
|
0
|
$rh->remove($sock); |
|
934
|
0
|
|
|
|
|
0
|
close($sock); |
|
935
|
|
|
|
|
|
|
} |
|
936
|
|
|
|
|
|
|
} |
|
937
|
|
|
|
|
|
|
} |
|
938
|
|
|
|
|
|
|
} |
|
939
|
|
|
|
|
|
|
} |
|
940
|
|
|
|
|
|
|
|
|
941
|
|
|
|
|
|
|
sub run_thread { |
|
942
|
|
|
|
|
|
|
# first pass at multithreaded execution -- as for fork() except we use |
|
943
|
|
|
|
|
|
|
# threads. This is ugly -- may want to bodge it up to see if the |
|
944
|
|
|
|
|
|
|
# run_select_fast method is a better model? |
|
945
|
0
|
|
|
0
|
0
|
0
|
my ($self) = shift ; |
|
946
|
0
|
0
|
|
|
|
0
|
if ($MAIN::no_thread == 1) { |
|
947
|
0
|
|
|
|
|
0
|
warn "Warning: Threading not supported!\n"; |
|
948
|
0
|
|
|
|
|
0
|
return; |
|
949
|
|
|
|
|
|
|
} |
|
950
|
0
|
|
|
|
|
0
|
my %init = ( |
|
951
|
|
|
|
|
|
|
LocalPort => $self->port(), |
|
952
|
|
|
|
|
|
|
Listen => $self->listen(), |
|
953
|
|
|
|
|
|
|
Proto => $self->proto(), |
|
954
|
|
|
|
|
|
|
Reuse => 1 |
|
955
|
|
|
|
|
|
|
); |
|
956
|
0
|
0
|
|
|
|
0
|
if ($self->hostname() ne "") { |
|
957
|
0
|
|
|
|
|
0
|
$init{LocalAddr} = $self->hostname(); |
|
958
|
|
|
|
|
|
|
} |
|
959
|
0
|
|
|
|
|
0
|
my ($main_sock) = new IO::Socket::INET(%init); |
|
960
|
|
|
|
|
|
|
|
|
961
|
0
|
0
|
|
|
|
0
|
die "Socket could not be created: $!\n" unless ($main_sock); |
|
962
|
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
# we need to trap SIGKILL and SIGINT. If no traps are already |
|
964
|
|
|
|
|
|
|
# defined by the user, add some default ones. |
|
965
|
0
|
0
|
|
|
|
0
|
if (! exists $SIG{INT}) { |
|
966
|
|
|
|
|
|
|
$SIG{INT} = sub { |
|
967
|
0
|
|
|
0
|
|
0
|
print STDERR "\nSIGINT: server $$ ", |
|
968
|
|
|
|
|
|
|
"shutting down \n"; |
|
969
|
0
|
|
|
|
|
0
|
exit 0; |
|
970
|
0
|
|
|
|
|
0
|
}; |
|
971
|
|
|
|
|
|
|
} |
|
972
|
|
|
|
|
|
|
# and make sure we wait() on children |
|
973
|
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
# now loop, forking whenever a new connection arrives on the listener |
|
975
|
|
|
|
|
|
|
|
|
976
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR "Created socket\n"; |
|
977
|
0
|
|
|
|
|
0
|
my $rh = new IO::Select($main_sock); |
|
978
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR "Created IO::Select()\n"; |
|
979
|
0
|
|
|
|
|
0
|
my (@ready) = (); |
|
980
|
0
|
|
|
|
|
0
|
while (@ready = $rh->can_read()) { |
|
981
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR |
|
982
|
|
|
|
|
|
|
"NetServer::Generic::run_select(): got ", |
|
983
|
|
|
|
|
|
|
scalar(@ready), " handles at ", scalar(localtime(time)), "\n"; |
|
984
|
0
|
|
|
|
|
0
|
my ($sock) = ""; |
|
985
|
0
|
|
|
|
|
0
|
foreach $sock (@ready) { |
|
986
|
0
|
0
|
|
|
|
0
|
if ($sock == $main_sock) { |
|
987
|
0
|
|
|
|
|
0
|
my ($new_sock) = $sock->accept(); |
|
988
|
0
|
|
|
|
|
0
|
$new_sock->autoflush(1); |
|
989
|
0
|
|
|
|
|
0
|
$rh->add($new_sock); |
|
990
|
0
|
0
|
|
|
|
0
|
if (! $self->ok_to_serve($new_sock)) { |
|
991
|
0
|
|
|
|
|
0
|
$rh->remove($sock); |
|
992
|
0
|
|
|
|
|
0
|
close($sock); |
|
993
|
|
|
|
|
|
|
} |
|
994
|
|
|
|
|
|
|
} else { |
|
995
|
0
|
0
|
|
|
|
0
|
if (! eof($sock)) { |
|
996
|
0
|
|
|
|
|
0
|
$sock->autoflush(1); |
|
997
|
0
|
|
|
|
|
0
|
my ($code) = $self->callback(); |
|
998
|
0
|
|
|
|
|
0
|
$self->sock($sock); |
|
999
|
0
|
|
|
|
|
0
|
*STDIN = $sock; |
|
1000
|
0
|
|
|
|
|
0
|
*STDOUT = $sock; |
|
1001
|
0
|
|
|
|
|
0
|
select STDIN; $| = 1; |
|
|
0
|
|
|
|
|
0
|
|
|
1002
|
0
|
|
|
|
|
0
|
select STDOUT; $| = 1; |
|
|
0
|
|
|
|
|
0
|
|
|
1003
|
0
|
|
|
|
|
0
|
my $t = new Thread &$code($self) ; |
|
1004
|
0
|
|
|
|
|
0
|
$t->detach(); |
|
1005
|
|
|
|
|
|
|
#&$code($self); |
|
1006
|
0
|
|
|
|
|
0
|
$rh->remove($sock); |
|
1007
|
0
|
|
|
|
|
0
|
close $sock; |
|
1008
|
|
|
|
|
|
|
# shutdown($sock, 2); |
|
1009
|
|
|
|
|
|
|
} else { |
|
1010
|
0
|
|
|
|
|
0
|
$rh->remove($sock); |
|
1011
|
0
|
|
|
|
|
0
|
close($sock); |
|
1012
|
|
|
|
|
|
|
} |
|
1013
|
|
|
|
|
|
|
} |
|
1014
|
|
|
|
|
|
|
} |
|
1015
|
|
|
|
|
|
|
} |
|
1016
|
|
|
|
|
|
|
} |
|
1017
|
|
|
|
|
|
|
|
|
1018
|
|
|
|
|
|
|
sub _thread { |
|
1019
|
|
|
|
|
|
|
# handle socket setup inside a thread |
|
1020
|
|
|
|
|
|
|
# args: IO::Socket::INET object, NetServer::Generic object |
|
1021
|
0
|
|
|
0
|
|
0
|
my $sock = shift; |
|
1022
|
0
|
|
|
|
|
0
|
my $self = shift; |
|
1023
|
0
|
|
0
|
|
|
0
|
print STDERR "self is a ", (ref($self) or " kangaroo "), "\n"; |
|
1024
|
0
|
0
|
|
|
|
0
|
if ($self->ok_to_serve($sock)) { |
|
1025
|
0
|
|
|
|
|
0
|
$sock->autoflush(1); |
|
1026
|
0
|
|
|
|
|
0
|
my ($code) = $self->callback(); |
|
1027
|
0
|
|
|
|
|
0
|
*STDIN = $sock; |
|
1028
|
0
|
|
|
|
|
0
|
*STDOUT = $sock; |
|
1029
|
0
|
|
|
|
|
0
|
select STDIN; $| = 1; |
|
|
0
|
|
|
|
|
0
|
|
|
1030
|
0
|
|
|
|
|
0
|
select STDOUT; $| = 1; |
|
|
0
|
|
|
|
|
0
|
|
|
1031
|
0
|
|
|
|
|
0
|
$self->sock($sock); |
|
1032
|
0
|
|
|
|
|
0
|
&$code($self); |
|
1033
|
|
|
|
|
|
|
} |
|
1034
|
0
|
|
|
|
|
0
|
shutdown($sock, 2); |
|
1035
|
0
|
|
|
|
|
0
|
return; |
|
1036
|
|
|
|
|
|
|
} |
|
1037
|
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
sub run_fork { |
|
1039
|
1
|
|
|
1
|
0
|
2
|
my ($self) = shift ; |
|
1040
|
1
|
|
|
|
|
10
|
my %init = ( |
|
1041
|
|
|
|
|
|
|
LocalPort => $self->port(), |
|
1042
|
|
|
|
|
|
|
Listen => $self->listen(), |
|
1043
|
|
|
|
|
|
|
Proto => $self->proto(), |
|
1044
|
|
|
|
|
|
|
Reuse => 1 |
|
1045
|
|
|
|
|
|
|
); |
|
1046
|
1
|
50
|
|
|
|
10
|
if ($self->hostname() ne "") { |
|
1047
|
1
|
|
|
|
|
3
|
$init{LocalAddr} = $self->hostname(); |
|
1048
|
|
|
|
|
|
|
} |
|
1049
|
1
|
|
|
|
|
68
|
my ($main_sock) = new IO::Socket::INET(%init); |
|
1050
|
|
|
|
|
|
|
|
|
1051
|
1
|
50
|
|
|
|
381859
|
die "Socket could not be created: $!\n" unless ($main_sock); |
|
1052
|
|
|
|
|
|
|
|
|
1053
|
|
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
# we need to trap SIGKILL and SIGINT. If no traps are already |
|
1055
|
|
|
|
|
|
|
# defined by the user, add some default ones. |
|
1056
|
1
|
50
|
|
|
|
13
|
if (! exists $SIG{INT}) { |
|
1057
|
|
|
|
|
|
|
$SIG{INT} = sub { |
|
1058
|
0
|
|
|
0
|
|
0
|
print STDERR "\nSIGINT: server $$ ", |
|
1059
|
|
|
|
|
|
|
"shutting down \n"; |
|
1060
|
0
|
|
|
|
|
0
|
exit 0; |
|
1061
|
0
|
|
|
|
|
0
|
}; |
|
1062
|
|
|
|
|
|
|
} |
|
1063
|
|
|
|
|
|
|
# and make sure we wait() on children |
|
1064
|
1
|
|
|
|
|
28
|
$SIG{CHLD} = \&reap_child; |
|
1065
|
1
|
|
|
|
|
47
|
my $parent_callback = $self->parent_callback(); |
|
1066
|
1
|
|
|
|
|
29
|
my $ante_fork_callback = $self->ante_fork_callback(); |
|
1067
|
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
# now loop, forking whenever a new connection arrives on the listener |
|
1069
|
1
|
|
|
|
|
23
|
$self->root_pid($$); # set server root PID |
|
1070
|
1
|
|
|
|
|
20
|
while (my ($new_sock) = $main_sock->accept()) { |
|
1071
|
1
|
50
|
|
|
|
314
|
&$ante_fork_callback($self) if ( defined $ante_fork_callback ); |
|
1072
|
1
|
|
|
|
|
26
|
my $x_time = [ gettimeofday ]; # millisecond timer to track duration |
|
1073
|
1
|
|
|
|
|
2434
|
my $pid = fork(); |
|
1074
|
1
|
50
|
|
|
|
88
|
die "Cannot fork: $!\n" unless defined ($pid); |
|
1075
|
1
|
50
|
|
|
|
40
|
if ($pid == 0) { |
|
1076
|
|
|
|
|
|
|
# child |
|
1077
|
1
|
50
|
|
|
|
43
|
if ($NetServer::Debug != 0) { |
|
1078
|
0
|
|
|
|
|
0
|
my ($peeraddr) = join(".", unpack("C4", $new_sock->peeraddr())); |
|
1079
|
0
|
|
|
|
|
0
|
print STDERR "$$ : ", scalar(localtime(time)), " : ", |
|
1080
|
|
|
|
|
|
|
"incoming connection from $peeraddr\n"; |
|
1081
|
|
|
|
|
|
|
} |
|
1082
|
1
|
50
|
|
|
|
229
|
if ($self->ok_to_serve($new_sock)) { |
|
1083
|
1
|
50
|
|
|
|
24
|
$NetServer::Debug |
|
1084
|
|
|
|
|
|
|
&& print STDERR $$, " : ", scalar(localtime(time)), " : ", |
|
1085
|
|
|
|
|
|
|
"processing connection\n"; |
|
1086
|
1
|
|
|
|
|
28
|
$new_sock->autoflush(1); |
|
1087
|
1
|
|
|
|
|
202
|
my ($code) = $self->callback(); |
|
1088
|
1
|
|
|
|
|
28
|
*STDIN = $new_sock; |
|
1089
|
1
|
|
|
|
|
8
|
*STDOUT = $new_sock; |
|
1090
|
1
|
|
|
|
|
6
|
select STDIN; $| = 1; |
|
|
1
|
|
|
|
|
4
|
|
|
1091
|
1
|
|
|
|
|
6
|
select STDOUT; $| = 1; |
|
|
1
|
|
|
|
|
5
|
|
|
1092
|
1
|
|
|
|
|
45
|
$self->sock($new_sock); |
|
1093
|
1
|
|
|
|
|
32
|
&$code($self); |
|
1094
|
|
|
|
|
|
|
} else { |
|
1095
|
0
|
0
|
|
|
|
0
|
if ($NetServer::Debug) { |
|
1096
|
0
|
|
|
|
|
0
|
print STDERR $$, " : ", scalar(localtime(time)), " : ", |
|
1097
|
|
|
|
|
|
|
"rejecting unauthed connection\n"; |
|
1098
|
|
|
|
|
|
|
} |
|
1099
|
|
|
|
|
|
|
} |
|
1100
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR "$0:$$: end of transaction\n"; |
|
1101
|
0
|
|
|
|
|
0
|
shutdown($new_sock, 2); |
|
1102
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR $$, " : ", |
|
1103
|
|
|
|
|
|
|
scalar(localtime(time)), " : ", |
|
1104
|
|
|
|
|
|
|
"took ", tv_interval($x_time), |
|
1105
|
|
|
|
|
|
|
" seconds\n"; |
|
1106
|
0
|
|
|
|
|
0
|
exit 0; |
|
1107
|
|
|
|
|
|
|
} else { |
|
1108
|
|
|
|
|
|
|
# parent |
|
1109
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && print STDERR "$0:$$: forked $pid\n"; |
|
1110
|
0
|
0
|
|
|
|
0
|
if ( defined $parent_callback ) { |
|
1111
|
0
|
|
|
|
|
0
|
&$parent_callback($self); |
|
1112
|
|
|
|
|
|
|
} |
|
1113
|
|
|
|
|
|
|
} |
|
1114
|
|
|
|
|
|
|
} |
|
1115
|
|
|
|
|
|
|
} |
|
1116
|
|
|
|
|
|
|
|
|
1117
|
|
|
|
|
|
|
sub run_client { |
|
1118
|
11
|
|
|
11
|
0
|
80
|
my ($self) = shift ; |
|
1119
|
11
|
|
|
|
|
1698
|
$SIG{CHLD} = \&reap_child; |
|
1120
|
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
# despatcher is a routine that dictates how often and how fast the |
|
1122
|
|
|
|
|
|
|
# server forks and execs the test callback. The default sub (below) |
|
1123
|
|
|
|
|
|
|
# returns immediately but is only true once, so the test is executed |
|
1124
|
|
|
|
|
|
|
# immediately one time only. More realistic despatchers may sleep for |
|
1125
|
|
|
|
|
|
|
# a random interval or even pre-fork themselves (for added chaos). |
|
1126
|
|
|
|
|
|
|
my $despatcher = $self->trigger() || |
|
1127
|
0
|
|
|
0
|
|
0
|
sub { $NetServer::Generic::default_trigger++; |
|
1128
|
0
|
0
|
|
|
|
0
|
return(($NetServer::Generic::default_trigger > 1) ? 0 : 1 ); |
|
1129
|
11
|
|
50
|
|
|
190
|
}; |
|
1130
|
|
|
|
|
|
|
|
|
1131
|
11
|
|
|
|
|
142
|
my $code = $self->callback(); # sub to call in child process |
|
1132
|
11
|
|
|
|
|
392
|
$self->root_pid($$); # set server root PID |
|
1133
|
11
|
|
|
|
|
133
|
my $triggerval = &$despatcher; |
|
1134
|
11
|
|
66
|
|
|
375
|
while (($triggerval ne "") && ($triggerval ne "0")) { |
|
1135
|
|
|
|
|
|
|
# loop, forking to create new client sessions |
|
1136
|
34
|
|
|
|
|
57494
|
my $pid = fork(); |
|
1137
|
34
|
50
|
|
|
|
1544
|
die "Cannot fork: $!\n" unless defined ($pid); |
|
1138
|
34
|
100
|
|
|
|
2016
|
if ($pid == 0) { |
|
1139
|
|
|
|
|
|
|
# child |
|
1140
|
9
|
50
|
|
|
|
837
|
if ($NetServer::Debug != 0) { |
|
1141
|
0
|
|
|
|
|
0
|
print STDERR "[$$] about to call new ", |
|
1142
|
|
|
|
|
|
|
"IO::Socket::INET(\n\t\t\t\t", |
|
1143
|
|
|
|
|
|
|
"PeerAddr => ", $self->hostname(), |
|
1144
|
|
|
|
|
|
|
"\n\t\t\t\tPeerPort => ", $self->port(), |
|
1145
|
|
|
|
|
|
|
"\n\t\t\t\tProto => ", $self->proto(), |
|
1146
|
|
|
|
|
|
|
"\n)\n"; |
|
1147
|
|
|
|
|
|
|
} |
|
1148
|
9
|
|
|
|
|
1844
|
my ($sock) = |
|
1149
|
|
|
|
|
|
|
new IO::Socket::INET( PeerAddr => $self->hostname(), |
|
1150
|
|
|
|
|
|
|
PeerPort => $self->port(), |
|
1151
|
|
|
|
|
|
|
Proto => $self->proto(), |
|
1152
|
|
|
|
|
|
|
); |
|
1153
|
9
|
100
|
|
|
|
1515909
|
die "Socket could not be created: $!\n" unless ($sock); |
|
1154
|
6
|
|
|
|
|
85
|
*STDIN = $sock; |
|
1155
|
6
|
|
|
|
|
23
|
*STDOUT = $sock; |
|
1156
|
6
|
|
|
|
|
30
|
select STDIN; $| = 1; |
|
|
6
|
|
|
|
|
28
|
|
|
1157
|
6
|
|
|
|
|
83
|
select STDOUT; $| = 1; |
|
|
6
|
|
|
|
|
84
|
|
|
1158
|
6
|
|
|
|
|
109
|
&$code($self, $triggerval); |
|
1159
|
0
|
|
|
|
|
0
|
shutdown($sock, 2); |
|
1160
|
0
|
|
|
|
|
0
|
exit 0; |
|
1161
|
|
|
|
|
|
|
} else { |
|
1162
|
|
|
|
|
|
|
# in parent |
|
1163
|
25
|
50
|
|
|
|
267
|
$NetServer::Debug && print STDERR "$0:$$: forked $pid\n"; |
|
1164
|
25
|
|
|
|
|
1231
|
$triggerval = &$despatcher; |
|
1165
|
|
|
|
|
|
|
} |
|
1166
|
|
|
|
|
|
|
} |
|
1167
|
2
|
|
|
|
|
2178791
|
wait; # for last child |
|
1168
|
2
|
|
|
|
|
189
|
return; |
|
1169
|
|
|
|
|
|
|
} |
|
1170
|
|
|
|
|
|
|
|
|
1171
|
|
|
|
|
|
|
sub run { |
|
1172
|
14
|
|
|
14
|
0
|
79
|
my $self = shift; |
|
1173
|
14
|
50
|
|
|
|
157
|
$NetServer::Debug && print STDERR "run() ...\n"; |
|
1174
|
14
|
100
|
66
|
|
|
173
|
if ( (! defined ($self->mode())) || (lc($self->mode()) eq "forking")) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1175
|
1
|
|
|
|
|
16
|
$self->run_fork(); |
|
1176
|
|
|
|
|
|
|
} elsif ( lc($self->mode()) eq "select") { |
|
1177
|
0
|
|
|
|
|
0
|
$self->run_select(); |
|
1178
|
|
|
|
|
|
|
} elsif ( lc($self->mode()) eq "select_fast") { |
|
1179
|
0
|
|
|
|
|
0
|
$self->run_select_fast(); |
|
1180
|
|
|
|
|
|
|
} elsif ( lc($self->mode()) eq "client") { |
|
1181
|
11
|
|
|
|
|
232
|
$self->run_client(); |
|
1182
|
|
|
|
|
|
|
} elsif ( lc($self->mode()) eq "threaded") { |
|
1183
|
0
|
|
|
|
|
0
|
$self->run_thread(); |
|
1184
|
|
|
|
|
|
|
} elsif ( lc($self->mode()) eq "prefork") { |
|
1185
|
2
|
|
|
|
|
16
|
$self->run_prefork(); |
|
1186
|
|
|
|
|
|
|
} else { |
|
1187
|
0
|
|
|
|
|
0
|
my $aargh = "Unknown mode: " . $self->mode() . "\n"; |
|
1188
|
0
|
|
|
|
|
0
|
die $aargh; |
|
1189
|
|
|
|
|
|
|
} |
|
1190
|
2
|
|
|
|
|
67
|
return; |
|
1191
|
|
|
|
|
|
|
} |
|
1192
|
|
|
|
|
|
|
|
|
1193
|
|
|
|
|
|
|
sub ok_to_serve($$) { |
|
1194
|
|
|
|
|
|
|
# internal sub. Given a ref to a Server object, and an IO::Socket::INET, |
|
1195
|
|
|
|
|
|
|
# see if we are allowed to serve the request. Return 1 if it's okay, 0 |
|
1196
|
|
|
|
|
|
|
# otherwise. |
|
1197
|
6
|
|
|
6
|
0
|
41
|
my ($self, $new_sock) = @_; |
|
1198
|
6
|
|
|
|
|
280
|
my ($junk, $peerp) = unpack_sockaddr_in($new_sock->peername()); |
|
1199
|
6
|
|
|
|
|
1853
|
my ($peername) = gethostbyaddr($peerp, AF_INET); |
|
1200
|
6
|
|
|
|
|
141
|
my ($peeraddr) = join(".", unpack("C4", $new_sock->peeraddr())); |
|
1201
|
6
|
|
|
|
|
810
|
$self->peer([ $peername, $peeraddr]); |
|
1202
|
|
|
|
|
|
|
$NetServer::Debug && |
|
1203
|
6
|
50
|
|
|
|
39
|
print STDERR "$0:$$: request from ", join(" ", @{$self->peer()}), "\n"; |
|
|
0
|
|
|
|
|
0
|
|
|
1204
|
6
|
50
|
33
|
|
|
90
|
return 1 if ((! defined($self->forbidden())) && |
|
1205
|
|
|
|
|
|
|
(! defined($self->allowed()))); |
|
1206
|
|
|
|
|
|
|
# if we got here, forbidden or allowed are not undef, |
|
1207
|
|
|
|
|
|
|
# so we have to do some checking |
|
1208
|
|
|
|
|
|
|
# Now we have the originator's hostname and IP address, we check |
|
1209
|
|
|
|
|
|
|
# them against the allowed list and the forbidden list. |
|
1210
|
0
|
|
|
|
|
0
|
my ($found_allowed, $found_banned) = 0; |
|
1211
|
0
|
0
|
|
|
|
0
|
if(defined ($self->allowed())) { |
|
1212
|
|
|
|
|
|
|
ALLOWED: |
|
1213
|
0
|
|
|
|
|
0
|
foreach (@{ $self->allowed() }) { |
|
|
0
|
|
|
|
|
0
|
|
|
1214
|
0
|
0
|
|
|
|
0
|
next if (! defined($_)); |
|
1215
|
0
|
0
|
0
|
|
|
0
|
if (($peername =~ /^$_$/i) || ($peeraddr =~ /^$_$/i)) { |
|
1216
|
0
|
|
|
|
|
0
|
$found_allowed++; |
|
1217
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && |
|
1218
|
|
|
|
|
|
|
print STDERR "allowed: $_ matched $peername or $peeraddr\n"; |
|
1219
|
0
|
|
|
|
|
0
|
last ALLOWED; |
|
1220
|
|
|
|
|
|
|
} |
|
1221
|
|
|
|
|
|
|
} |
|
1222
|
|
|
|
|
|
|
} |
|
1223
|
0
|
0
|
|
|
|
0
|
if(defined ($self->forbidden())) { |
|
1224
|
|
|
|
|
|
|
FORBIDDEN: |
|
1225
|
0
|
|
|
|
|
0
|
foreach (@{ $self->forbidden() } ) { |
|
|
0
|
|
|
|
|
0
|
|
|
1226
|
0
|
0
|
|
|
|
0
|
next if (! defined($_)); |
|
1227
|
0
|
0
|
0
|
|
|
0
|
if (($peername =~ /^$_$/i) || ($peeraddr =~ /^$_$/i)) { |
|
1228
|
0
|
|
|
|
|
0
|
$found_banned++; |
|
1229
|
0
|
0
|
|
|
|
0
|
$NetServer::Debug && |
|
1230
|
|
|
|
|
|
|
print STDERR "forbidden: $_ matched $peername ", |
|
1231
|
|
|
|
|
|
|
"or $peeraddr\n"; |
|
1232
|
0
|
|
|
|
|
0
|
last FORBIDDEN; |
|
1233
|
|
|
|
|
|
|
} |
|
1234
|
|
|
|
|
|
|
} |
|
1235
|
|
|
|
|
|
|
} |
|
1236
|
0
|
0
|
0
|
|
|
0
|
($found_banned && ! $found_allowed) && return 0; |
|
1237
|
0
|
0
|
0
|
|
|
0
|
($found_allowed && ! $found_banned) && return 1; |
|
1238
|
0
|
0
|
0
|
|
|
0
|
($found_allowed && $found_banned) && return 0; |
|
1239
|
0
|
|
|
|
|
0
|
return 0; |
|
1240
|
|
|
|
|
|
|
} |
|
1241
|
|
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
#sub _new_fifo { |
|
1243
|
|
|
|
|
|
|
# my $self = shift; |
|
1244
|
|
|
|
|
|
|
# # create a new named pipe. Return its filename. This is used by |
|
1245
|
|
|
|
|
|
|
# # the preforked server for children to send information back to their |
|
1246
|
|
|
|
|
|
|
# # parent. |
|
1247
|
|
|
|
|
|
|
# my $fname = "/tmp/fifo.$$"; |
|
1248
|
|
|
|
|
|
|
# my $mode = 666; |
|
1249
|
|
|
|
|
|
|
# umask(0777); # possible security hole |
|
1250
|
|
|
|
|
|
|
# mkfifo($fname, $mode) or die "Unable to mkfifo(): $!\n"; |
|
1251
|
|
|
|
|
|
|
# return $fname; |
|
1252
|
|
|
|
|
|
|
#} |
|
1253
|
|
|
|
|
|
|
# |
|
1254
|
|
|
|
|
|
|
#sub _read_fifo { # Blocking read |
|
1255
|
|
|
|
|
|
|
# my $self = shift; |
|
1256
|
|
|
|
|
|
|
# # read a line from the designated fifo named $self->fifo() |
|
1257
|
|
|
|
|
|
|
# my $handle = $self->fifo(); |
|
1258
|
|
|
|
|
|
|
# $SIG{ALRM} = sub { close FIFO }; |
|
1259
|
|
|
|
|
|
|
# open(FIFO, "<$handle") or die "Can't open $handle: $!\n"; |
|
1260
|
|
|
|
|
|
|
# alarm(1); |
|
1261
|
|
|
|
|
|
|
# my @buffer = (); |
|
1262
|
|
|
|
|
|
|
# alarm(0); |
|
1263
|
|
|
|
|
|
|
# close FIFO; |
|
1264
|
|
|
|
|
|
|
# return @buffer; |
|
1265
|
|
|
|
|
|
|
#} |
|
1266
|
|
|
|
|
|
|
# |
|
1267
|
|
|
|
|
|
|
#sub _write_fifo { # Non-blocking write |
|
1268
|
|
|
|
|
|
|
# my $self = shift; |
|
1269
|
|
|
|
|
|
|
# my @args = @_; |
|
1270
|
|
|
|
|
|
|
# my $handle = $self->fifo(); |
|
1271
|
|
|
|
|
|
|
# $SIG{ALRM} = sub { close FIFO }; |
|
1272
|
|
|
|
|
|
|
# open(FIFO, "+>$handle") or die "Can't open $handle: $!\n"; |
|
1273
|
|
|
|
|
|
|
# alarm(1); |
|
1274
|
|
|
|
|
|
|
# print FIFO @_; |
|
1275
|
|
|
|
|
|
|
# alarm(0); |
|
1276
|
|
|
|
|
|
|
# close FIFO; |
|
1277
|
|
|
|
|
|
|
# return; |
|
1278
|
|
|
|
|
|
|
#} |
|
1279
|
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
sub quit { |
|
1281
|
2
|
|
|
2
|
1
|
15
|
my ($self) = shift; |
|
1282
|
2
|
50
|
|
|
|
22
|
$NetServer::Debug && print STDERR "called shutdown(): root_pid is ", |
|
1283
|
|
|
|
|
|
|
$self->root_pid(), "\n"; |
|
1284
|
2
|
|
|
|
|
37
|
kill 15, $self->root_pid(); |
|
1285
|
2
|
|
|
|
|
1366
|
exit; |
|
1286
|
|
|
|
|
|
|
} |
|
1287
|
|
|
|
|
|
|
|
|
1288
|
|
|
|
|
|
|
sub AUTOLOAD { |
|
1289
|
278
|
|
|
278
|
|
898
|
my ($self) = shift; |
|
1290
|
278
|
|
|
|
|
842
|
my ($name) = $NetServer::Generic::AUTOLOAD; |
|
1291
|
278
|
|
|
|
|
4069
|
$name =~ s/.*://; |
|
1292
|
278
|
100
|
|
|
|
1276
|
if (@_) { |
|
1293
|
108
|
|
|
|
|
999
|
my ($val) = shift; |
|
1294
|
|
|
|
|
|
|
# rudimentary type checking |
|
1295
|
108
|
|
100
|
|
|
1756
|
my ($r) = (ref($val) || "scalar"); |
|
1296
|
108
|
50
|
|
|
|
1107
|
if (! exists ($self->{tags}->{$name})) { |
|
1297
|
0
|
|
|
|
|
0
|
warn "\tno such method: $name\n"; |
|
1298
|
0
|
|
|
|
|
0
|
return undef; |
|
1299
|
|
|
|
|
|
|
} |
|
1300
|
108
|
50
|
|
|
|
3243
|
if ($r !~ /$self->{tags}->{$name}/i) { |
|
1301
|
0
|
|
|
|
|
0
|
warn "\t", ref($val), ": expecting a ", $self->{tags}->{$name}, "\n", "\tgot [", join("][", @_), "]\n"; |
|
1302
|
0
|
|
|
|
|
0
|
return undef; |
|
1303
|
|
|
|
|
|
|
} |
|
1304
|
108
|
|
|
|
|
1059
|
return $self->{$name} = $val; |
|
1305
|
|
|
|
|
|
|
} else { |
|
1306
|
170
|
|
|
|
|
5523
|
return $self->{$name}; |
|
1307
|
|
|
|
|
|
|
} |
|
1308
|
|
|
|
|
|
|
} |
|
1309
|
|
|
|
|
|
|
|
|
1310
|
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
1; |
|
1312
|
|
|
|
|
|
|
|