File Coverage

blib/lib/POE/Component/Server/Inet.pm
Criterion Covered Total %
statement 107 158 67.7
branch 25 58 43.1
condition 5 20 25.0
subroutine 20 27 74.0
pod 5 5 100.0
total 162 268 60.4


line stmt bran cond sub pod time code
1             package POE::Component::Server::Inet;
2             $POE::Component::Server::Inet::VERSION = '0.06';
3             #ABSTRACT: a super-server daemon implementation in POE
4              
5 4     4   3793 use strict;
  4         8  
  4         146  
6 4     4   23 use warnings;
  4         6  
  4         145  
7 4     4   21 use POE qw(Wheel::SocketFactory Wheel::Run Wheel::ReadWrite Filter::Stream);
  4         6  
  4         44  
8 4     4   91439 use Net::Netmask;
  4         29003  
  4         468  
9 4     4   44 use Socket;
  4         8  
  4         3678  
10 4     4   28 use Carp;
  4         8  
  4         9022  
11              
12             sub spawn {
13 4     4 1 2863 my $package = shift;
14 4         16 my %opts = @_;
15 4         28 $opts{lc $_} = delete $opts{$_} for keys %opts;
16 4         14 my $options = delete $opts{options};
17 4         16 my $self = bless \%opts, $package;
18 4 50       78 $self->{session_id} = POE::Session->create(
19             object_states => [
20             $self => { shutdown => '_shutdown',
21             add_tcp => '_add_tcp',
22             del_tcp => '_del_tcp',
23             # add_udp => '_add_udp',
24             # del_udp => '_del_udp',
25             },
26             $self => [ qw(_start _accept_new_client _accept_failed _get_datagram _sig_child _client_input _client_flushed _client_error _wheel_out _wheel_close _wheel_error _wheel_alarm) ],
27             ],
28             heap => $self,
29             ( ref($options) eq 'HASH' ? ( options => $options ) : () ),
30             )->ID();
31 4         597 return $self;
32             }
33              
34             sub session_id {
35 0     0 1 0 return $_[0]->{session_id};
36             }
37              
38             sub _conn_exists {
39 0     0   0 my ($self,$wheel_id) = @_;
40 0 0 0     0 return 0 unless $wheel_id and defined $self->{clients}->{ $wheel_id };
41 0         0 return 1;
42             }
43              
44             sub _start {
45 4     4   1515 my ($kernel,$self,$sender) = @_[KERNEL,OBJECT,SENDER];
46 4         24 $self->{session_id} = $_[SESSION]->ID();
47 4 50       32 if ( $self->{alias} ) {
48 0         0 $kernel->alias_set( $self->{alias} );
49             }
50             else {
51 4         21 $kernel->refcount_increment( $self->{session_id} => __PACKAGE__ );
52             }
53 4         195 $self->{stream_filter} = POE::Filter::Stream->new();
54 4         45 return;
55             }
56              
57             sub shutdown {
58 4     4 1 32010249 my $self = shift;
59 4         42 $poe_kernel->call( $self->{session_id}, 'shutdown' );
60             }
61              
62             sub _shutdown {
63 4     4   547 my ($kernel,$self) = @_[KERNEL,OBJECT];
64 4         34 $kernel->alarm_remove_all();
65 4         501 $kernel->alias_remove( $_ ) for $kernel->alias_list();
66 4 50       283 $kernel->refcount_decrement( $self->{session_id} => __PACKAGE__ ) unless $self->{alias};
67             # Shutdown TCP listeners
68 4         321 delete $self->{tcp_ports};
69             # Shutdown UDP listeners
70 4         1535 $kernel->select_read( $_->{socket} ) for values %{ $self->{udp_ports} };
  4         41  
71             # Shutdown wheels.
72 4         27 delete $self->{clients};
73 4         19 delete $self->{wheels};
74 4         24 return;
75             }
76              
77             sub add_tcp {
78 4     4 1 1884 my $self = shift;
79 4         26 $poe_kernel->call( $self->{session_id}, 'add_tcp', @_ );
80             }
81              
82             sub del_tcp {
83 0     0 1 0 my $self = shift;
84 0         0 $poe_kernel->call( $self->{session_id}, 'del_tcp', @_ );
85             }
86              
87              
88             sub _add_tcp {
89 4     4   261 my ($kernel,$self) = @_[KERNEL,OBJECT];
90 4         7 my $args;
91 4 50       16 if ( ref( $_[ARG0] ) eq 'HASH' ) {
92 0         0 $args = { %{ $_[ARG0] } };
  0         0  
93             }
94             else {
95 4         16 $args = { @_[ARG0..$#_] };
96             }
97 4 50       24 unless ( defined $args->{port} ) {
98 0         0 warn "You must specify a 'port' parameter\n";
99 0         0 return;
100             }
101 4 50       9 if ( grep { $_->{port} eq $args->{port} } values %{ $self->{tcp_ports} } ) {
  0         0  
  4         20  
102 0         0 warn "There already exists a TCP port definition for '$args->{port}'\n";
103 0         0 return;
104             }
105 4 50       25 unless ( $args->{program} ) {
106 0         0 warn "You must specify a 'program' parameter\n";
107 0         0 return;
108             }
109 4 50 33     20 delete $args->{programargs} unless $args->{programargs} and ref $args->{programargs} eq 'ARRAY';
110 4 50 33     36 if ( $args->{allow} and !$args->{allow}->isa('Net::Netmask') ) {
111 0         0 warn "'allow' parameter must be a Net::Netmask object, ignoring.\n";
112 0         0 delete $args->{allow};
113             }
114 4 50 33     19 if ( $args->{deny} and !$args->{deny}->isa('Net::Netmask') ) {
115 0         0 warn "'deny' parameter must be a Net::Netmask object, ignoring.\n";
116 0         0 delete $args->{deny};
117             }
118 4 50       50 my $sockfactory = POE::Wheel::SocketFactory->new(
119             ( defined $args->{bindaddress} ? ( BindAddress => $args->{bindaddress} ) : () ),
120             BindPort => $args->{port},
121             SuccessEvent => '_accept_new_client',
122             FailureEvent => '_accept_failed',
123             SocketDomain => AF_INET,
124             SocketType => SOCK_STREAM,
125             SocketProtocol => 'tcp',
126             Reuse => 'on',
127             );
128 4         5617 $args->{sockfactory} = $sockfactory;
129 4         20 $self->{tcp_ports}->{ $sockfactory->ID() } = $args;
130 4         30 my $port = ( sockaddr_in( $sockfactory->getsockname() ) )[0];
131 4         112 $args->{port} = $port;
132 4         21 return $port;
133             }
134              
135             sub _del_tcp {
136 0     0   0 my ($kernel,$self) = @_[KERNEL,OBJECT];
137 0         0 my $args;
138 0 0       0 if ( ref( $_[ARG0] ) eq 'HASH' ) {
139 0         0 $args = { %{ $_[ARG0] } };
  0         0  
140             }
141             else {
142 0         0 $args = { @_[ARG0..$#_] };
143             }
144 0 0       0 unless ( defined $args->{port} ) {
145 0         0 warn "You must specify a 'port' parameter\n";
146 0         0 return;
147             }
148 0         0 foreach my $sockfactory_id ( keys %{ $self->{tcp_ports} } ) {
  0         0  
149 0 0       0 next unless $self->{tcp_ports}->{ $sockfactory_id }->{port} eq $args->{port};
150 0         0 delete $self->{tcp_ports}->{ $sockfactory_id };
151 0         0 return;
152             }
153 0         0 return;
154             }
155              
156              
157             sub _accept_failed {
158 0     0   0 my ($kernel,$self,$wheel_id) = @_[KERNEL,OBJECT,ARG3];
159 0         0 warn join(' ', @_[ARG0..ARG2] ), "\n";
160 0         0 delete $self->{tcp_ports}->{ $wheel_id }->{sockfactory};
161 0         0 return;
162             }
163              
164             sub _accept_new_client {
165 14     14   38344 my ($kernel,$self,$socket,$peeraddr,$peerport,$factory_id) = @_[KERNEL,OBJECT,ARG0 .. ARG3];
166 14         159 $peeraddr = inet_ntoa($peeraddr);
167             # Check if 'accept' or 'deny'
168 14         460 my $client = POE::Wheel::ReadWrite->new (
169             Handle => $socket,
170             Filter => $self->{stream_filter},
171             InputEvent => '_client_input',
172             ErrorEvent => '_client_error',
173             FlushedEvent => '_client_flushed',
174             );
175 14         5319 my $args = $self->{tcp_ports}->{ $factory_id };
176 14 50       250 my $wheel = POE::Wheel::Run->new(
    50          
177             Program => $args->{program},
178             ProgramArgs => $args->{programargs},
179             StdioFilter => $self->{stream_filter},
180             StderrFilter => $self->{stream_filter},
181             StdoutEvent => '_wheel_out', # Received data from the child's STDOUT.
182             StderrEvent => '_wheel_out', # Received data from the child's STDERR.
183             ErrorEvent => '_wheel_error', # An I/O error occurred.
184             CloseEvent => '_wheel_close', # Child closed all output handles.
185             ( defined $args->{user} ? ( User => $args->{user} ) : () ),
186             ( defined $args->{group} ? ( Group => $args->{group} ) : () ),
187             );
188 14         124879 my $client_id = $client->ID();
189 14         1279 my $wheel_id = $wheel->ID();
190 14         433 $self->{wheels}->{ $wheel_id } = { wheel => $wheel, client => $client_id, tcp => 1 };
191 14         186 $self->{clients}->{ $client_id } = { wheel => $wheel_id, client => $client };
192 14         138 $kernel->sig_child( $wheel->PID(), '_sig_child' );
193 14         6018 return;
194             }
195              
196             sub _sig_child {
197 14     14   15214 $poe_kernel->sig_handled();
198             }
199              
200             sub _client_input {
201 11     11   14520 my ($kernel,$self,$data,$client_id) = @_[KERNEL,OBJECT,ARG0,ARG1];
202 11         62 my $wheel_id = $self->{clients}->{ $client_id }->{wheel};
203 11 50       62 return unless $self->{wheels}->{ $wheel_id };
204 11         150 $self->{wheels}->{ $wheel_id }->{wheel}->put( $data );
205 11         810 return;
206             }
207              
208             sub _client_error {
209 7     7   6656 my ($kernel,$self,$client_id) = @_[KERNEL,OBJECT,ARG3];
210 7         21 my $wheel_id = $self->{clients}->{ $client_id }->{wheel};
211 7         56 delete $self->{clients}->{ $client_id };
212 7 50       1569 return unless $self->{wheels}->{ $wheel_id };
213 7         189 $self->{wheels}->{ $wheel_id }->{wheel}->shutdown_stdin();
214 7   50     1023 $self->{wheels}->{ $wheel_id }->{alarm} =
215             $kernel->delay_set( '_wheel_alarm', $self->{timeout} || 30, $wheel_id );
216 7         513 return;
217             }
218              
219             sub _client_flushed {
220 7     7   3441 my ($kernel,$self,$client_id) = @_[KERNEL,OBJECT,ARG0];
221 7         24 $self->{clients}->{ $client_id }->{pending} = 0;
222 7 50       55 return unless $self->{clients}->{ $client_id }->{shutdown};
223 0         0 delete $self->{clients}->{ $client_id };
224 0         0 return;
225             }
226              
227             sub _wheel_out {
228 7     7   4049 my ($kernel,$self,$data,$wheel_id) = @_[KERNEL,OBJECT,ARG0,ARG1];
229 7 50       55 if ( defined $self->{wheels}->{ $wheel_id }->{tcp} ) {
230 7         21 my $client_id = $self->{wheels}->{ $wheel_id }->{client};
231 7 50       49 return unless $self->{clients}->{ $client_id };
232 7         155 $self->{clients}->{ $client_id }->{client}->put( $data );
233 7         832 $self->{clients}->{ $client_id }->{pending} = 1;
234             }
235 7         39 return;
236             }
237              
238             sub _wheel_alarm {
239 0     0   0 my ($kernel,$self,$wheel_id) = @_[KERNEL,OBJECT,ARG0];
240 0 0       0 return unless $self->{wheels}->{ $wheel_id };
241 0         0 $self->{wheels}->{ $wheel_id }->{wheel}->kill(9);
242 0         0 return;
243             }
244              
245             sub _wheel_close {
246 14     14   608 my ($kernel,$self,$wheel_id) = @_[KERNEL,OBJECT,ARG0];
247 14         152 my $wdata = delete $self->{wheels}->{ $wheel_id };
248 14 100       102 $kernel->alarm_remove( $wdata->{alarm} ) if $wdata->{alarm};
249 14 50       799 if ( defined $wdata->{tcp} ) {
250 14         53 my $client_id = $wdata->{client};
251 14 100       99 return unless $self->{clients}->{ $client_id };
252 7 50       32 if ( $self->{clients}->{ $client_id }->{pending} ) {
253 0         0 $self->{clients}->{ $client_id }->{shutdown} = 1;
254 0         0 return;
255             }
256 7         72 delete $self->{clients}->{ $client_id };
257             }
258 7         3251 return;
259             }
260              
261             sub _wheel_error {
262 28     28   44289 my ($operation, $errnum, $errstr, $wheel_id) = @_[ARG0..ARG3];
263 28 50 33     280 return if $operation eq "read" and !$errnum;
264 0 0 0       $errstr = "remote end closed" if $operation eq "read" and !$errnum;
265 0           warn "Wheel $wheel_id generated $operation error $errnum: $errstr\n";
266 0           return;
267             }
268              
269 0     0     sub _get_datagram {
270             }
271              
272             qq[Inet in'it];
273              
274             __END__