File Coverage

lib/ControlFreak/Socket.pm
Criterion Covered Total %
statement 72 107 67.2
branch 17 42 40.4
condition 2 12 16.6
subroutine 13 19 68.4
pod 4 9 44.4
total 108 189 57.1


line stmt bran cond sub pod time code
1             package ControlFreak::Socket;
2 8     8   49 use strict;
  8         16  
  8         338  
3 8     8   47 use warnings;
  8         13  
  8         258  
4              
5 8     8   42 use Carp();
  8         14  
  8         176  
6 8         44 use Object::Tiny qw{
7             name
8             host
9             service
10             nonblocking
11             listen_qsize
12              
13             fh
14 8     8   38 };
  8         14  
15 8     8   2487 use Params::Util qw{ _STRING };
  8         14  
  8         471  
16 8     8   42 use Scalar::Util();
  8         13  
  8         157  
17 8     8   46 use Socket qw(AF_INET AF_UNIX SOCK_STREAM SOL_SOCKET SO_REUSEADDR SOMAXCONN);
  8         14  
  8         629  
18 8     8   51 use AnyEvent::Util qw(fh_nonblocking AF_INET6);
  8         12  
  8         412  
19 8     8   49 use AnyEvent::Socket();
  8         19  
  8         9410  
20              
21             =head1 NAME
22              
23             ControlFreak::Socket - Defines a (shared) socket controlled by ControlFreak
24              
25             =cut
26              
27             =head1 SYNOPSIS
28              
29             $sock = ControlFreak::Socket->new(
30             ctrl => $ctrl,
31             name => "fcgisock",
32             host => "unix/",
33             service => "/tmp/cfk-x.sock",
34             options => "TBD",
35             );
36             $sock->bind;
37             $sock->unbind;
38             $sock->set_host;
39             $sock->set_service;
40             print $sock->service;
41              
42             =head1 DESCRIPTION
43              
44             Each socket object has a unique name inside B controller,
45             services interested in a socket just reference it using this name.
46             The controller pipes the socket to children's stdin after forking,
47             and before executing the service.
48              
49             =head1 METHODS
50              
51             =head2 new(%param)
52              
53             Creates a socket objects. Params are:
54              
55             =over 4
56              
57             =item * ctrl
58              
59             The controller to attach the socket to. If not specified, the
60             socket object won't be created, C will just return undef.
61              
62             =item * name
63              
64             The name of the socket, MUST be unique within C.
65              
66             =item * host
67              
68             eg. '127.0.0.0', '0.0.0.0', 'unix/', '[::1]'.
69              
70             =item * service
71              
72             eg. '80', '/tmp/cfk.sock'.
73              
74             =back
75              
76             If a socket with that name already exists, it will return undef
77             and log the error.
78              
79             =cut
80              
81             sub new {
82 5     5 1 1781 my $class = shift;
83 5         32 my %param = @_;
84              
85 5         11 delete $param{fh};
86 5         9 my $ctrl = $param{ctrl};
87 5 100       26 unless ($ctrl) {
88 1         272 warn "Socket creation attempt without ctrl";
89 1         8 return;
90             }
91              
92 4 50       16 unless ($param{name}) {
93 0         0 $ctrl->log->error("Socket creation attempt without a name");
94 0         0 return;
95             }
96              
97 4         35 my $socket = $class->SUPER::new(%param);
98 4         43 $socket->{ctrl} = $ctrl;
99 4 100       20 unless ($ctrl->add_socket($socket)) {
100 1         28 $ctrl->log->error("A socket by that name already exists");
101 1         171 return;
102             }
103 3         17 Scalar::Util::weaken($socket->{ctrl});
104 3         11 return $socket;
105             }
106              
107             =head2 bind
108              
109             Creates, binds the socket and puts it in listen mode, then returns
110             immediately.
111             Once bound, $socket->fh will return the filehandle.
112              
113             =cut
114              
115             sub bind {
116 2     2 1 1688 my $socket = shift;
117              
118 2         10 my $ctrl = $socket->{ctrl};
119 2         234 my $name = $socket->name;
120 2 50       21 if ($socket->{fh}) {
121 0         0 $ctrl->log->error("'$name' socket is already bound");
122 0         0 return;
123             }
124              
125 2         10 my ($fh, $host, $service) = $socket->_bind;
126 2 50       8 unless ($fh) {
127 0         0 $ctrl->log->error("cannot bind '$name': $!");
128 0         0 return;
129             }
130 2         67 $ctrl->log->info("'$name' socket is now bound: $fh");
131             ## reset with real values
132 2         800 $socket->{service} = $service;
133 2         6 $socket->{host} = $host;
134 2         4 $socket->{fh} = $fh;
135 2         8 return;
136             }
137              
138             sub _bind {
139 2     2   5 my $socket = shift;
140              
141 2         59 my $host = $socket->host;
142 2         244 my $service = $socket->service;
143              
144             ## part reaped from AnyEvent::Socket
145              
146 2 50       24 my $ipn = AnyEvent::Socket::parse_address($host)
147             or Carp::croak "AnyEvent::Socket::tcp_server: "
148             . "cannot parse '$host' as host address";
149              
150 2         144 my $af = AnyEvent::Socket::address_family($ipn);
151              
152 2         14 my $fh;
153              
154             # win32 perl is too stupid to get this right :/
155 2         4 Carp::croak "tcp_server/socket: address family not supported"
156             if AnyEvent::WIN32 && $af == AF_UNIX;
157              
158 2 50       123 socket $fh, $af, SOCK_STREAM, 0
159             or Carp::croak "tcp_server/socket: $!";
160              
161 2 50 33     14 if ($af == AF_INET || $af == AF_INET6) {
    0          
162 2 50       237 setsockopt $fh, SOL_SOCKET, SO_REUSEADDR, 1
163             or Carp::croak "tcp_server/so_reuseaddr: $!"
164             unless AnyEvent::WIN32; # work around windows bug
165              
166 2 50       100 unless ($service =~ /^\d*$/) {
167 0 0       0 $service = (getservbyname $service, "tcp")[2]
168             or Carp::croak "$service: service unknown"
169             }
170             } elsif ($af == AF_UNIX) {
171 0         0 unlink $service;
172             }
173              
174 2 50       15 CORE::bind $fh, AnyEvent::Socket::pack_sockaddr($service, $ipn)
175             or Carp::croak "bind: $!";
176              
177 2 50       539 fh_nonblocking $fh, ($socket->nonblocking ? 1 : 0 );
178              
179 2   50     293 my $len = $socket->listen_qsize || SOMAXCONN;
180 2         42 ($service, $host) = AnyEvent::Socket::unpack_sockaddr( getsockname $fh );
181 2         36 ($host, $service) = (AnyEvent::Socket::format_address($host), $service);
182              
183 2 50       162 listen $fh, $len or Carp::croak "listen: $!";
184 2         11 return ($fh, $host, $service);
185             }
186              
187             =head2 is_bound
188              
189             Returns true if the socket is bound.
190              
191             =cut
192              
193             sub is_bound {
194 5 100   5 1 4386 return shift->{fh} ? 1 : 0;
195             }
196              
197             =head2 unbind()
198              
199             Unbind and destroys the socket.
200              
201             =cut
202              
203             sub unbind {
204 0     0 1   my $socket = shift;
205 0 0         return unless $socket->is_bound;
206 0           $socket->{fh} = undef;
207 0           return 1;
208             }
209              
210             sub set_host {
211 0     0 0   my $sock = shift;
212 0 0         my $value = _STRING($_[0]) or return;
213 0           $value =~ s/[\n\r\t\0]+//g; ## desc should be one line
214 0           $sock->{host} = $value;
215 0           return 1;
216             }
217              
218             sub set_service {
219 0     0 0   my $sock = shift;
220 0 0         my $value = _STRING($_[0]) or return;
221 0           $value =~ s/[\n\r\t\0]+//g; ## desc should be one line
222 0           $sock->{service} = $value;
223 0           return 1;
224             }
225              
226             sub set_nonblocking {
227 0     0 0   my $sock = shift;
228 0 0         my $value = shift() ? 1 : 0;
229 0           $sock->{nonblocking} = $value;
230 0           return 1;
231             }
232              
233             sub set_listen_qsize {
234 0     0 0   my $sock = shift;
235 0           my $size = shift;
236 0 0 0       $size = SOMAXCONN if $size && $size =~ /^\s*max\s*$/i;
237 0   0       my $value = _NUMBER($size) || 0;
238 0           $sock->{listen_qsize} = $value;
239             }
240              
241             sub unset {
242 0     0 0   my $sock = shift;
243 0   0       my $attr = shift || "";
244 0           $sock->{$attr} = undef;
245 0           return 1;
246             }
247              
248             =head1 AUTHOR
249              
250             Yann Kerherve
251              
252             =cut
253              
254             "chaussette";