File Coverage

blib/lib/XAS/Lib/Net/Server.pm
Criterion Covered Total %
statement 24 172 13.9
branch 0 12 0.0
condition 0 3 0.0
subroutine 8 33 24.2
pod 13 14 92.8
total 45 234 19.2


line stmt bran cond sub pod time code
1             package XAS::Lib::Net::Server;
2              
3             our $VERSION = '0.05';
4              
5 1     1   3 use POE;
  1         1  
  1         6  
6 1     1   275 use Try::Tiny;
  1         1  
  1         45  
7 1     1   3 use Socket ':all';
  1         2  
  1         816  
8 1     1   444 use POE::Filter::Line;
  1         1765  
  1         20  
9 1     1   528 use POE::Wheel::ReadWrite;
  1         4231  
  1         20  
10 1     1   539 use POE::Wheel::SocketFactory;
  1         4095  
  1         69  
11              
12             use XAS::Class
13 1         16 debug => 0,
14             version => $VERSION,
15             base => 'XAS::Lib::POE::Service',
16             mixin => 'XAS::Lib::Mixins::Handlers',
17             utils => ':validation weaken params',
18             accessors => 'session clients',
19             constants => 'ARRAY',
20             vars => {
21             PARAMS => {
22             -port => 1,
23             -tcp_keepalive => { optional => 1, default => 0 },
24             -inactivity_timer => { optional => 1, default => 600 },
25             -filter => { optional => 1, default => undef },
26             -address => { optional => 1, default => 'localhost' },
27             -eol => { optional => 1, default => "\015\012" },
28             }
29             }
30 1     1   7 ;
  1         1  
31              
32             #use Data::Dumper;
33              
34             # ----------------------------------------------------------------------
35             # Public Methods
36             # ----------------------------------------------------------------------
37              
38             sub session_initialize {
39 0     0 1   my $self = shift;
40              
41 0           my $alias = $self->alias;
42              
43 0           $self->log->debug("$alias: entering session_intialize()");
44              
45             # private events
46              
47 0           $poe_kernel->state('client_error', $self, '_client_error');
48 0           $poe_kernel->state('client_input', $self, '_client_input');
49 0           $poe_kernel->state('client_reaper', $self, '_client_reaper');
50 0           $poe_kernel->state('client_output', $self, '_client_output');
51 0           $poe_kernel->state('client_flushed', $self, '_client_flushed');
52 0           $poe_kernel->state('client_connected', $self, '_client_connected');
53 0           $poe_kernel->state('client_connection', $self, '_client_connection');
54 0           $poe_kernel->state('client_connection_failed', $self, '_client_connection_failed');
55              
56 0           $poe_kernel->state('handle_connection', $self, '_handle_connection');
57              
58             # walk the chain
59              
60 0           $self->SUPER::session_initialize();
61              
62 0           $self->log->debug("$alias: leaving session_intialize()");
63              
64             }
65              
66             sub session_startup {
67 0     0 1   my $self = shift;
68              
69 0           my $alias = $self->alias;
70              
71 0           $self->log->debug("$alias: entering session_startup()");
72              
73 0           $poe_kernel->call($alias, 'client_connection');
74              
75             # walk the chain
76              
77 0           $self->SUPER::session_startup();
78              
79 0           $self->log->debug("$alias: leaving session_startup()");
80              
81             }
82              
83             sub session_shutdown {
84 0     0 1   my $self = shift;
85              
86 0           my $alias = $self->alias;
87 0           my @clients = keys %{$self->{'clients'}};
  0            
88              
89 0           $self->log->debug("$alias: entering session_shutdown()");
90              
91 0           foreach my $client (@clients) {
92              
93 0           $poe_kernel->alarm_remove($self->clients->{$client}->{'watchdog'});
94 0           $client = undef;
95              
96             }
97              
98 0           delete $self->{'listener'};
99              
100             # walk the chain
101              
102 0           $self->SUPER::session_shutdown();
103              
104 0           $self->log->debug("$alias: leaving session_shutdown()");
105              
106             }
107              
108             sub session_pause {
109 0     0 1   my $self = shift;
110              
111 0           my $alias = $self->alias;
112 0           my @clients = keys %{$self->{'clients'}};
  0            
113              
114 0           $self->log->debug("$alias: entering session_pause()");
115              
116 0           foreach my $client (@clients) {
117              
118 0           $client->pause_input();
119 0           $poe_kernel->alarm_remove($self->clients->{$client}->{'watchdog'});
120              
121             }
122              
123             # walk the chain
124              
125 0           $self->SUPER::session_pause();
126              
127 0           $self->log->debug("$alias: leaving session_pause()");
128              
129             }
130              
131             sub session_resume {
132 0     0 1   my $self = shift;
133              
134 0           my $alias = $self->alias;
135 0           my @clients = keys %{$self->{'clients'}};
  0            
136 0           my $inactivity = $self->inactivity_timer;
137              
138 0           $self->log->debug("$alias: entering session_resume()");
139              
140 0           foreach my $client (@clients) {
141              
142 0           $client->resume_input();
143 0           $self->clients->{$client}->{'watchdog'} = $poe_kernel->alarm_set('client_reaper', $inactivity, $client);
144              
145             }
146              
147             # walk the chain
148              
149 0           $self->SUPER::session_resume();
150              
151 0           $self->log->debug("$alias: leaving session_resume()");
152              
153             }
154              
155             sub reaper {
156 0     0 1   my $self = shift;
157 0           my ($wheel) = validate_params(\@_, [1]);
158              
159 0           my $alias = $self->alias;
160              
161 0           $self->log->debug_msg('net_client_reaper', $alias, $self->host($wheel), $self->peerport($wheel));
162              
163             }
164              
165             sub process_request {
166 0     0 1   my $self = shift;
167 0           my ($input, $ctx) = validate_params(\@_, [1,1]);
168              
169 0           $self->process_response($input, $ctx);
170              
171             }
172              
173             sub process_response {
174 0     0 1   my $self = shift;
175 0           my ($output, $ctx) = validate_params(\@_, [1,1]);
176              
177 0           my $alias = $self->alias;
178              
179 0           $poe_kernel->post($alias, 'client_output', $output, $ctx);
180              
181             }
182              
183             sub process_errors {
184 0     0 1   my $self = shift;
185 0           my ($errors, $ctx) = validate_params(\@_, [1,1]);
186              
187 0           $self->process_response($errors, $ctx);
188              
189             }
190              
191             sub handle_connection {
192 0     0 1   my $self = shift;
193 0           my ($wheel) = validate_params(\@_, [1]);
194              
195             }
196              
197             # ----------------------------------------------------------------------
198             # Public Accessors
199             # ----------------------------------------------------------------------
200              
201             sub peerport {
202 0     0 1   my $self = shift;
203 0           my ($wheel) = validate_params(\@_, [1]);
204              
205 0           return $self->clients->{$wheel}->{'port'};
206              
207             }
208              
209             sub peerhost {
210 0     0 0   my $self = shift;
211 0           my ($wheel) = validate_params(\@_, [1]);
212              
213 0           return $self->clients->{$wheel}->{'host'};
214              
215             }
216              
217             sub client {
218 0     0 1   my $self = shift;
219 0           my ($wheel) = validate_params(\@_, [1]);
220              
221 0           return $self->clients->{$wheel}->{'client'};
222              
223             }
224              
225             # ----------------------------------------------------------------------
226             # Public Events
227             # ----------------------------------------------------------------------
228              
229             # ----------------------------------------------------------------------
230             # Private Events
231             # ----------------------------------------------------------------------
232              
233             sub _handle_connection {
234 0     0     my ($self, $wheel) = @_[OBJECT, ARG0];
235              
236 0           my $alias = $self->alias;
237              
238 0           $self->log->debug("$alias: _handle_connection()");
239 0           $self->handle_connection($wheel);
240              
241             }
242              
243             sub _client_connection {
244 0     0     my ($self) = $_[OBJECT];
245              
246 0           my $alias = $self->alias;
247              
248 0           $self->log->debug("$alias: _client_connection()");
249              
250             # start listening for connections
251              
252 0           $self->{'listener'} = POE::Wheel::SocketFactory->new(
253             BindAddress => $self->address,
254             BindPort => $self->port,
255             SocketType => SOCK_STREAM,
256             SocketDomain => AF_INET,
257             SocketProtocol => 'tcp',
258             Reuse => 1,
259             SuccessEvent => 'client_connected',
260             FailureEvent => 'client_connection_failed'
261             );
262              
263             }
264              
265             sub _client_connected {
266 0     0     my ($self, $socket, $peeraddr, $peerport, $wheel_id) = @_[OBJECT,ARG0..ARG3];
267              
268 0           my $alias = $self->alias;
269 0           my $inactivity = $self->inactivity_timer;
270              
271 0           $self->log->debug("$alias: _client_connected()");
272              
273 0           my $client = POE::Wheel::ReadWrite->new(
274             Handle => $socket,
275             Filter => $self->filter,
276             InputEvent => 'client_input',
277             ErrorEvent => 'client_error',
278             FlushedEvent => 'client_flushed',
279             );
280              
281 0           my $wheel = $client->ID;
282 0           my $host = gethostbyaddr($peeraddr, AF_INET);
283              
284 0           $self->{'clients'}->{$wheel}->{'host'} = $host;
285 0           $self->{'clients'}->{$wheel}->{'port'} = $peerport;
286 0           $self->{'clients'}->{$wheel}->{'client'} = $client;
287 0           $self->{'clients'}->{$wheel}->{'active'} = time();
288 0           $self->{'clients'}->{$wheel}->{'socket'} = $socket;
289 0           $self->{'clients'}->{$wheel}->{'watchdog'} = $poe_kernel->alarm_set('client_reaper', $inactivity, $wheel);
290              
291 0           $self->log->info_msg('net_client_connect', $alias, $host, $peerport);
292              
293 0           $poe_kernel->post($alias, 'handle_connection', $wheel);
294            
295             }
296              
297             sub _client_connection_failed {
298 0     0     my ($self, $syscall, $errnum, $errstr, $wheel) = @_[OBJECT,ARG0..ARG3];
299              
300 0           my $alias = $self->alias;
301              
302 0           $self->log->error_msg('net_client_connection_failed', $alias, $errnum, $errstr);
303              
304 0           delete $self->{'listener'};
305              
306             }
307              
308             sub _client_input {
309 0     0     my ($self, $input, $wheel) = @_[OBJECT,ARG0,ARG1];
310              
311 0           my $alias = $self->alias;
312 0           my $ctx = {
313             wheel => $wheel
314             };
315              
316 0           $self->log->debug("$alias: _client_input()");
317              
318 0           $self->{'clients'}->{$wheel}->{'active'} = time();
319              
320 0           $self->process_request($input, $ctx);
321              
322             }
323              
324             sub _client_output {
325 0     0     my ($self, $data, $ctx) = @_[OBJECT,ARG0,ARG1];
326              
327 0           my $alias = $self->alias;
328 0           my $wheel = $ctx->{'wheel'};
329 0           my @buffer;
330              
331 0           $self->log->debug("$alias: _client_output()");
332              
333             try {
334              
335 0 0 0 0     if (defined($wheel) and defined($self->clients->{$wheel})) {
336              
337             # emulate IO::Socket connected() method. this method
338             # calls getpeername(). getpeername() returns undef when
339             # the network stack can't validate the socket.
340              
341 1     1   2078 no warnings;
  1         2  
  1         378  
342              
343 0 0         if (getpeername($self->clients->{$wheel}->{'socket'})) {
344              
345 0           push(@buffer, $data);
346 0           $self->clients->{$wheel}->{'client'}->put(@buffer);
347              
348             } else {
349              
350 0           $self->log->error_msg(
351             'net_client_nosocket',
352             $alias,
353             $self->peerhist($wheel),
354             $self->peerport($wheel)
355             );
356 0           delete $self->clients->{$wheel};
357              
358             }
359              
360             } else {
361              
362 0           $self->log->error_msg('net_client_nowheel', $alias);
363              
364             }
365              
366             } catch {
367              
368 0     0     my $ex = $_;
369              
370 0           $self->exception_handler($ex);
371              
372 0           delete $self->clients->{$wheel};
373              
374 0           };
375              
376             }
377              
378             sub _client_error {
379 0     0     my ($self, $syscall, $errnum, $errstr, $wheel) = @_[OBJECT,ARG0..ARG3];
380              
381 0           my $alias = $self->alias;
382              
383 0           $self->log->debug("$alias: _client_error()");
384              
385 0 0         if ($errnum == 0) {
386              
387 0           $self->log->info_msg('net_client_disconnect', $alias, $self->peerhost($wheel), $self->peerport($wheel));
388              
389             } else {
390              
391 0           $self->log->error_msg('net_client_error', $alias, $errnum, $errstr);
392              
393             }
394              
395 0           delete $self->clients->{$wheel};
396              
397             }
398              
399             sub _client_reaper {
400 0     0     my ($self, $wheel) = @_[OBJECT,ARG0];
401              
402 0           my $timeout = time() - $self->inactivity_timer;
403              
404 0 0         if (defined($self->clients->{$wheel})) {
405              
406 0 0         if ($self->clients->{$wheel}->{'active'} < $timeout) {
407              
408 0           $self->reaper($wheel);
409              
410             }
411              
412             }
413              
414             }
415              
416             sub _client_flushed {
417 0     0     my ($self, $wheel) = @_[OBJECT,ARG0];
418              
419 0           my $alias = $self->alias;
420 0           my $host = $self->peerhost($wheel);
421 0           my $port = $self->peerport($wheel);
422              
423 0           $self->log->debug(sprintf('%s: _client_flushed(), wheel: %s, host: %s, port: %s', $alias, $wheel, $host, $port));
424            
425             }
426              
427             # ----------------------------------------------------------------------
428             # Private Methods
429             # ----------------------------------------------------------------------
430              
431             sub init {
432 0     0 1   my $class = shift;
433              
434 0           my $self = $class->SUPER::init(@_);
435              
436 0 0         unless (defined($self->filter)) {
437              
438 0           $self->{'filter'} = POE::Filter::Line->new(
439             InputLiteral => $self->eol,
440             OutputLiteral => $self->eol,
441             );
442              
443             }
444              
445 0           return $self;
446              
447             }
448              
449             1;
450              
451             __END__