File Coverage

blib/lib/Mojo/IOLoop/Client.pm
Criterion Covered Total %
statement 96 130 73.8
branch 24 52 46.1
condition 13 25 52.0
subroutine 26 30 86.6
pod 3 3 100.0
total 162 240 67.5


line stmt bran cond sub pod time code
1             package Mojo::IOLoop::Client;
2 63     63   64327 use Mojo::Base 'Mojo::EventEmitter';
  63         935  
  63         437  
3              
4 63     63   14807 use Errno qw(EINPROGRESS);
  63         40511  
  63         7092  
5 63     63   37742 use IO::Socket::IP;
  63         1295030  
  63         375  
6 63     63   33118 use IO::Socket::UNIX;
  63         175  
  63         675  
7 63     63   100230 use Mojo::IOLoop;
  63         165  
  63         798  
8 63     63   28476 use Mojo::IOLoop::TLS;
  63         236  
  63         1277  
9 63     63   672 use Scalar::Util qw(weaken);
  63         192  
  63         3816  
10 63     63   530 use Socket qw(IPPROTO_TCP SOCK_STREAM TCP_NODELAY);
  63         234  
  63         8242  
11              
12             # Non-blocking name resolution requires Net::DNS::Native
13 63 100   63   582 use constant NNR => $ENV{MOJO_NO_NNR} ? 0 : !!eval { require Net::DNS::Native; Net::DNS::Native->VERSION('0.15'); 1 };
  63         176  
  63         580  
  62         18021  
  0         0  
  0         0  
14             my $NDN;
15              
16             # SOCKS support requires IO::Socket::Socks
17             use constant SOCKS => $ENV{MOJO_NO_SOCKS}
18             ? 0
19 63 100   63   450 : !!eval { require IO::Socket::Socks; IO::Socket::Socks->VERSION('0.64'); 1 };
  63         174  
  63         518  
  62         14773  
  0         0  
  0         0  
20 63     63   436 use constant READ => SOCKS ? IO::Socket::Socks::SOCKS_WANT_READ() : 0;
  63         174  
  63         4184  
21 63     63   434 use constant WRITE => SOCKS ? IO::Socket::Socks::SOCKS_WANT_WRITE() : 0;
  63         167  
  63         126287  
22              
23             has reactor => sub { Mojo::IOLoop->singleton->reactor }, weak => 1;
24              
25 197     197   729 sub DESTROY { shift->_cleanup }
26              
27 2     2 1 16 sub can_nnr {NNR}
28 2     2 1 12 sub can_socks {SOCKS}
29              
30             sub connect {
31 196 100   196 1 1394 my ($self, $args) = (shift, ref $_[0] ? $_[0] : {@_});
32              
33             # Timeout
34 196         833 weaken $self;
35 196         853 my $reactor = $self->reactor;
36 196   100 0   1651 $self->{timer} = $reactor->timer($args->{timeout} || 10, sub { $self->emit(error => 'Connect timeout') });
  0         0  
37              
38             # Blocking name resolution
39 196   66     1799 $_ && s/[[\]]//g for @$args{qw(address socks_address)};
40 196   66     1435 my $address = $args->{socks_address} || ($args->{address} ||= '127.0.0.1');
41 196 100   196   1282 return $reactor->next_tick(sub { $self && $self->_connect($args) }) if !NNR || $args->{handle} || $args->{path};
  196         1496  
42              
43             # Non-blocking name resolution
44 0   0     0 $NDN //= Net::DNS::Native->new(pool => 5, extra_thread => 1);
45             my $handle = $self->{dns}
46 0         0 = $NDN->getaddrinfo($address, _port($args), {protocol => IPPROTO_TCP, socktype => SOCK_STREAM});
47             $reactor->io(
48             $handle => sub {
49 0     0   0 my $reactor = shift;
50              
51 0         0 $reactor->remove($self->{dns});
52 0         0 my ($err, @res) = $NDN->get_result(delete $self->{dns});
53 0 0       0 return $self->emit(error => "Can't resolve: $err") if $err;
54              
55 0         0 $args->{addr_info} = \@res;
56 0         0 $self->_connect($args);
57             }
58 0         0 )->watch($handle, 1, 0);
59             }
60              
61             sub _cleanup {
62 388     388   716 my $self = shift;
63 388 0 33     1089 $NDN->timedout($self->{dns}) if $NDN && $self->{dns};
64 388 50       1236 return $self unless my $reactor = $self->reactor;
65 388   66     2798 $self->{$_} && $reactor->remove(delete $self->{$_}) for qw(dns timer handle);
66 388         4880 return $self;
67             }
68              
69             sub _connect {
70 195     195   552 my ($self, $args) = @_;
71              
72 195         492 my $path = $args->{path};
73 195         667 my $handle = $self->{handle} = $args->{handle};
74              
75 195 50       610 unless ($handle) {
76 195 50       685 my $class = $path ? 'IO::Socket::UNIX' : 'IO::Socket::IP';
77 195         653 my %options = (Blocking => 0);
78              
79             # UNIX domain socket
80 195 50       657 if ($path) { $options{Peer} = $path }
  0         0  
81              
82             # IP socket
83             else {
84 195 50       631 if (my $info = $args->{addr_info}) { $options{PeerAddrInfo} = $info }
  0         0  
85             else {
86 195   66     1211 $options{PeerAddr} = $args->{socks_address} || $args->{address};
87 195         684 $options{PeerPort} = _port($args);
88             }
89 195 100       773 @options{keys %{$args->{socket_options}}} = values %{$args->{socket_options}} if $args->{socket_options};
  179         552  
  179         538  
90             }
91              
92 195 50       1971 return $self->emit(error => "Can't connect: $@") unless $self->{handle} = $handle = $class->new(%options);
93             }
94 195         120195 $handle->blocking(0);
95              
96 195 50       4109 $path ? $self->_try_socks($args) : $self->_wait('_ready', $handle, $args);
97             }
98              
99 195 0 66 195   1512 sub _port { $_[0]{socks_port} || $_[0]{port} || ($_[0]{tls} ? 443 : 80) }
    50          
100              
101             sub _ready {
102 194     194   706 my ($self, $args) = @_;
103              
104             # Socket changes in between attempts and needs to be re-added for epoll/kqueue
105 194         522 my $handle = $self->{handle};
106 194 50       782 unless ($handle->connect) {
107 0 0       0 return $self->emit(error => $!) unless $! == EINPROGRESS;
108 0         0 $self->reactor->remove($handle);
109 0         0 return $self->_wait('_ready', $handle, $args);
110             }
111              
112 194 50 0     14004 return $self->emit(error => $! || 'Not connected') unless $handle->connected;
113              
114             # Disable Nagle's algorithm
115 194         6249 setsockopt $handle, IPPROTO_TCP, TCP_NODELAY, 1;
116              
117 194         1189 $self->_try_socks($args);
118             }
119              
120             sub _socks {
121 0     0   0 my ($self, $args) = @_;
122              
123             # Connected
124 0         0 my $handle = $self->{handle};
125 0 0       0 return $self->_try_tls($args) if $handle->ready;
126              
127             # Switch between reading and writing
128 0         0 my $err = $IO::Socket::Socks::SOCKS_ERROR;
129 0 0       0 if ($err == READ) { $self->reactor->watch($handle, 1, 0) }
  0 0       0  
130 0         0 elsif ($err == WRITE) { $self->reactor->watch($handle, 1, 1) }
131 0         0 else { $self->emit(error => $err) }
132             }
133              
134             sub _try_socks {
135 194     194   614 my ($self, $args) = @_;
136              
137 194         520 my $handle = $self->{handle};
138 194 100       1164 return $self->_try_tls($args) unless $args->{socks_address};
139 1         7 return $self->emit(error => 'IO::Socket::Socks 0.64+ required for SOCKS support') unless SOCKS;
140              
141 0         0 my %options = (ConnectAddr => $args->{address}, ConnectPort => $args->{port});
142 0 0       0 @options{qw(AuthType Username Password)} = ('userpass', @$args{qw(socks_user socks_pass)}) if $args->{socks_user};
143 0         0 my $reactor = $self->reactor;
144 0         0 $reactor->remove($handle);
145 0 0       0 return $self->emit(error => 'SOCKS upgrade failed') unless IO::Socket::Socks->start_SOCKS($handle, %options);
146              
147 0         0 $self->_wait('_socks', $handle, $args);
148             }
149              
150             sub _try_tls {
151 193     193   700 my ($self, $args) = @_;
152              
153 193         444 my $handle = $self->{handle};
154 193 100       993 return $self->_cleanup->emit(connect => $handle) unless $args->{tls};
155 2         15 my $reactor = $self->reactor;
156 2         12 $reactor->remove($handle);
157              
158             # Start TLS handshake
159 2         8 weaken $self;
160 2         39 my $tls = Mojo::IOLoop::TLS->new($handle)->reactor($self->reactor);
161 2     0   18 $tls->on(upgrade => sub { $self->_cleanup->emit(connect => pop) });
  0         0  
162 2     2   23 $tls->on(error => sub { $self->emit(error => pop) });
  2         7  
163 2         20 $tls->negotiate(%$args);
164             }
165              
166             sub _wait {
167 195     195   951 my ($self, $next, $handle, $args) = @_;
168 195         928 weaken $self;
169 195     194   878 $self->reactor->io($handle => sub { $self->$next($args) })->watch($handle, 0, 1);
  194         1369  
170             }
171              
172             1;
173              
174             =encoding utf8
175              
176             =head1 NAME
177              
178             Mojo::IOLoop::Client - Non-blocking TCP/IP and UNIX domain socket client
179              
180             =head1 SYNOPSIS
181              
182             use Mojo::IOLoop::Client;
183              
184             # Create socket connection
185             my $client = Mojo::IOLoop::Client->new;
186             $client->on(connect => sub ($client, $handle) {...});
187             $client->on(error => sub ($client, $err) {...});
188             $client->connect(address => 'example.com', port => 80);
189              
190             # Start reactor if necessary
191             $client->reactor->start unless $client->reactor->is_running;
192              
193             =head1 DESCRIPTION
194              
195             L opens TCP/IP and UNIX domain socket connections for L.
196              
197             =head1 EVENTS
198              
199             L inherits all events from L and can emit the following new ones.
200              
201             =head2 connect
202              
203             $client->on(connect => sub ($client, $handle) {...});
204              
205             Emitted once the connection is established.
206              
207             =head2 error
208              
209             $client->on(error => sub ($client, $err) {...});
210              
211             Emitted if an error occurs on the connection, fatal if unhandled.
212              
213             =head1 ATTRIBUTES
214              
215             L implements the following attributes.
216              
217             =head2 reactor
218              
219             my $reactor = $client->reactor;
220             $client = $client->reactor(Mojo::Reactor::Poll->new);
221              
222             Low-level event reactor, defaults to the C attribute value of the global L singleton. Note that
223             this attribute is weakened.
224              
225             =head1 METHODS
226              
227             L inherits all methods from L and implements the following new ones.
228              
229             =head2 can_nnr
230              
231             my $bool = Mojo::IOLoop::Client->can_nnr;
232              
233             True if L 0.15+ is installed and non-blocking name resolution support enabled.
234              
235             =head2 can_socks
236              
237             my $bool = Mojo::IOLoop::Client->can_socks;
238              
239             True if L 0.64+ is installed and SOCKS5 support enabled.
240              
241             =head2 connect
242              
243             $client->connect(address => '127.0.0.1', port => 3000);
244             $client->connect({address => '127.0.0.1', port => 3000});
245              
246             Open a socket connection to a remote host. Note that non-blocking name resolution depends on L
247             (0.15+), SOCKS5 support on L (0.64), and TLS support on L (2.009+).
248              
249             These options are currently available:
250              
251             =over 2
252              
253             =item address
254              
255             address => 'mojolicious.org'
256              
257             Address or host name of the peer to connect to, defaults to C<127.0.0.1>.
258              
259             =item handle
260              
261             handle => $handle
262              
263             Use an already prepared L object.
264              
265             =item path
266              
267             path => '/tmp/myapp.sock'
268              
269             Path of UNIX domain socket to connect to.
270              
271             =item port
272              
273             port => 80
274              
275             Port to connect to, defaults to C<80> or C<443> with C option.
276              
277             =item socket_options
278              
279             socket_options => {LocalAddr => '127.0.0.1'}
280              
281             Additional options for L when opening new connections.
282              
283             =item socks_address
284              
285             socks_address => '127.0.0.1'
286              
287             Address or host name of SOCKS5 proxy server to use for connection.
288              
289             =item socks_pass
290              
291             socks_pass => 'secr3t'
292              
293             Password to use for SOCKS5 authentication.
294              
295             =item socks_port
296              
297             socks_port => 9050
298              
299             Port of SOCKS5 proxy server to use for connection.
300              
301             =item socks_user
302              
303             socks_user => 'sri'
304              
305             Username to use for SOCKS5 authentication.
306              
307             =item timeout
308              
309             timeout => 15
310              
311             Maximum amount of time in seconds establishing connection may take before getting canceled, defaults to C<10>.
312              
313             =item tls
314              
315             tls => 1
316              
317             Enable TLS.
318              
319             =item tls_ca
320              
321             tls_ca => '/etc/tls/ca.crt'
322              
323             Path to TLS certificate authority file.
324              
325             =item tls_cert
326              
327             tls_cert => '/etc/tls/client.crt'
328              
329             Path to the TLS certificate file.
330              
331             =item tls_key
332              
333             tls_key => '/etc/tls/client.key'
334              
335             Path to the TLS key file.
336              
337             =item tls_options
338              
339             tls_options => {SSL_alpn_protocols => ['foo', 'bar'], SSL_verify_mode => 0x00}
340              
341             Additional options for L.
342              
343             =back
344              
345             =head1 SEE ALSO
346              
347             L, L, L.
348              
349             =cut