File Coverage

blib/lib/IO/Socket/INET/Daemon.pm
Criterion Covered Total %
statement 59 62 95.1
branch 16 20 80.0
condition 5 10 50.0
subroutine 13 13 100.0
pod 6 7 85.7
total 99 112 88.3


line stmt bran cond sub pod time code
1              
2             package IO::Socket::INET::Daemon;
3              
4 2     2   50282 use strict;
  2         6  
  2         76  
5 2     2   10 use warnings;
  2         4  
  2         84  
6              
7             our $VERSION = '0.04';
8              
9 2     2   10 use Carp;
  2         12  
  2         200  
10              
11 2     2   2148 use IO::Socket::INET;
  2         100888  
  2         16  
12 2     2   19146 use IO::Select;
  2         6970  
  2         2566  
13              
14              
15             sub new {
16 2     2 1 42 my ($class, %rc) = @_;
17              
18 2 50 33     22 croak "Called with no/invalid port.\n" if(!$rc{port} or $rc{port} =~ /\D/);
19              
20 2   50     78 return bless {
      50        
21             port => $rc{port},
22             host => $rc{host} || 'localhost',
23             callback => $rc{callback} || {},
24             timeout => $rc{timeout},
25             }, $class;
26             }
27              
28              
29             sub callback {
30 2     2 1 1626 my ($self, %callback) = @_;
31              
32 2         10 @{$self->{callback}}{keys %callback} = values %callback;
  2         12  
33             }
34              
35              
36             sub run {
37 1     1 1 1356 my ($self) = @_;
38              
39             # Create server socket.
40 1 50       128 my $host = $self->{sck} = new IO::Socket::INET(
41             LocalHost => $self->{host},
42             LocalPort => $self->{port},
43             Proto => 'tcp',
44             ReuseAddr => !0,
45             Listen => 32,
46             ) or return;
47              
48 1         2898 $self->{stop} = 0;
49 1         59 my $select = $self->{select} = new IO::Select($host);
50              
51             # The main loop.
52 1         118 until($self->{stop}) {
53 5         645 $self->call('tick', undef);
54              
55             # Get readable sockets.
56 5         27 for my $io ($select->can_read($self->{timeout})) {
57              
58             # If the server socket is readable, get the pending incoming
59             # connection, call the callback and add the peer to our list.
60 5 100       1010837 if($io == $host) {
    50          
61 2         45 my $peer = $io->accept;
62              
63 2 50       321 if($self->call('add', $peer)) {
64 2         285 $select->add($peer);
65             }
66             else {
67 0         0 $self->remove($peer);
68             }
69             }
70              
71             # If it's a peer, call the data callback. Remove peer if the
72             # callback returns something false or if the connection is dead.
73             elsif($io->connected) {
74 3 100       66 if(!$self->call('data', $io)) {
75 1         91 $self->call('remove', $io);
76 1         7 $self->remove($io);
77             }
78             }
79             else {
80 0         0 $self->call('remove', $io);
81 0         0 $self->remove($io);
82             }
83             }
84             }
85             }
86              
87             # Call a callback function. For internal use only. Takes the name of the
88             # callback (add, remove or data) and the socket handle as arguments. Returns
89             # true if there was no such callback, it returns non-zero by default, otherwise
90             # the return value of the callback is returned.
91             sub call {
92 11     11 0 31 my ($self, $callback, $io) = @_;
93              
94 11         42 my $func = $self->{callback}->{$callback};
95              
96 11 100       36 return $func ? &{$func}($io, $self) : !0;
  6         47  
97             }
98              
99             # This closes a connection to a peer and removes it from our socket list.
100             sub remove {
101 3     3 1 30 my ($self, $io) = @_;
102              
103 3 100 66     450 if($io and $io != $self->{sck}) {
104 2         5 my $select = $self->{select};
105              
106 2         23 $select->remove($io);
107              
108 2         103 $io->shutdown(SHUT_RDWR);
109 2         5992 $io->close;
110             }
111             }
112              
113             # This simply sets a variable to a true value, so the main loop will stop after
114             # the next cycle.
115             sub stop {
116 1     1 1 139 my ($self) = @_;
117 1         14 $self->{stop} = !0;
118             }
119              
120             # This closes all connections and the server socket. Can be called to clean up
121             # manually, but is also called automatically from DESTROY.
122             sub destroy {
123 2     2 1 7 my ($self) = @_;
124              
125 2         16 $self->{stop} = !0;
126              
127 2         11 my $select = $self->{select};
128              
129 2 100       16 if($select) {
130 1         21 $self->remove($_) for($select->handles);
131 1         69 delete $self->{select};
132             }
133              
134 2         10 my $host = delete $self->{sck};
135 2 100       255 if($host) {
136 1         5 $host->shutdown(SHUT_RDWR);
137 1         20 $host->close;
138             }
139             }
140              
141             sub DESTROY {
142 2     2   1029482 my ($self) = @_;
143 2         55 $self->destroy;
144             }
145              
146             !0;
147              
148              
149             __END__