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   62978 use Mojo::Base 'Mojo::EventEmitter';
  63         876  
  63         383  
3              
4 63     63   13753 use Errno qw(EINPROGRESS);
  63         38031  
  63         6633  
5 63     63   35891 use IO::Socket::IP;
  63         1249515  
  63         359  
6 63     63   32190 use IO::Socket::UNIX;
  63         162  
  63         582  
7 63     63   98111 use Mojo::IOLoop;
  63         156  
  63         747  
8 63     63   26414 use Mojo::IOLoop::TLS;
  63         243  
  63         1172  
9 63     63   573 use Scalar::Util qw(weaken);
  63         179  
  63         3840  
10 63     63   470 use Socket qw(IPPROTO_TCP SOCK_STREAM TCP_NODELAY);
  63         155  
  63         7798  
11              
12             # Non-blocking name resolution requires Net::DNS::Native
13 63 100   63   527 use constant NNR => $ENV{MOJO_NO_NNR} ? 0 : !!eval { require Net::DNS::Native; Net::DNS::Native->VERSION('0.15'); 1 };
  63         156  
  63         542  
  62         16632  
  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   405 : !!eval { require IO::Socket::Socks; IO::Socket::Socks->VERSION('0.64'); 1 };
  63         166  
  63         416  
  62         13932  
  0         0  
  0         0  
20 63     63   443 use constant READ => SOCKS ? IO::Socket::Socks::SOCKS_WANT_READ() : 0;
  63         162  
  63         3917  
21 63     63   408 use constant WRITE => SOCKS ? IO::Socket::Socks::SOCKS_WANT_WRITE() : 0;
  63         166  
  63         119589  
22              
23             has reactor => sub { Mojo::IOLoop->singleton->reactor }, weak => 1;
24              
25 197     197   677 sub DESTROY { shift->_cleanup }
26              
27 2     2 1 11 sub can_nnr {NNR}
28 2     2 1 10 sub can_socks {SOCKS}
29              
30             sub connect {
31 196 100   196 1 1321 my ($self, $args) = (shift, ref $_[0] ? $_[0] : {@_});
32              
33             # Timeout
34 196         760 weaken $self;
35 196         749 my $reactor = $self->reactor;
36 196   100 0   1579 $self->{timer} = $reactor->timer($args->{timeout} || 10, sub { $self->emit(error => 'Connect timeout') });
  0         0  
37              
38             # Blocking name resolution
39 196   66     1703 $_ && s/[[\]]//g for @$args{qw(address socks_address)};
40 196   66     1375 my $address = $args->{socks_address} || ($args->{address} ||= '127.0.0.1');
41 196 100   196   1221 return $reactor->next_tick(sub { $self && $self->_connect($args) }) if !NNR || $args->{handle} || $args->{path};
  196         1406  
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   668 my $self = shift;
63 388 0 33     1007 $NDN->timedout($self->{dns}) if $NDN && $self->{dns};
64 388 50       1170 return $self unless my $reactor = $self->reactor;
65 388   66     2656 $self->{$_} && $reactor->remove(delete $self->{$_}) for qw(dns timer handle);
66 388         4559 return $self;
67             }
68              
69             sub _connect {
70 195     195   534 my ($self, $args) = @_;
71              
72 195         436 my $path = $args->{path};
73 195         558 my $handle = $self->{handle} = $args->{handle};
74              
75 195 50       575 unless ($handle) {
76 195 50       655 my $class = $path ? 'IO::Socket::UNIX' : 'IO::Socket::IP';
77 195         597 my %options = (Blocking => 0);
78              
79             # UNIX domain socket
80 195 50       557 if ($path) { $options{Peer} = $path }
  0         0  
81              
82             # IP socket
83             else {
84 195 50       567 if (my $info = $args->{addr_info}) { $options{PeerAddrInfo} = $info }
  0         0  
85             else {
86 195   66     1134 $options{PeerAddr} = $args->{socks_address} || $args->{address};
87 195         683 $options{PeerPort} = _port($args);
88             }
89 195 100       717 @options{keys %{$args->{socket_options}}} = values %{$args->{socket_options}} if $args->{socket_options};
  179         546  
  179         567  
90             }
91              
92 195 50       1753 return $self->emit(error => "Can't connect: $@") unless $self->{handle} = $handle = $class->new(%options);
93             }
94 195         111913 $handle->blocking(0);
95              
96 195 50       3887 $path ? $self->_try_socks($args) : $self->_wait('_ready', $handle, $args);
97             }
98              
99 195 0 66 195   1392 sub _port { $_[0]{socks_port} || $_[0]{port} || ($_[0]{tls} ? 443 : 80) }
    50          
100              
101             sub _ready {
102 194     194   585 my ($self, $args) = @_;
103              
104             # Socket changes in between attempts and needs to be re-added for epoll/kqueue
105 194         477 my $handle = $self->{handle};
106 194 50       698 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     13033 return $self->emit(error => $! || 'Not connected') unless $handle->connected;
113              
114             # Disable Nagle's algorithm
115 194         5835 setsockopt $handle, IPPROTO_TCP, TCP_NODELAY, 1;
116              
117 194         1025 $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   563 my ($self, $args) = @_;
136              
137 194         491 my $handle = $self->{handle};
138 194 100       1097 return $self->_try_tls($args) unless $args->{socks_address};
139 1         5 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   675 my ($self, $args) = @_;
152              
153 193         404 my $handle = $self->{handle};
154 193 100       888 return $self->_cleanup->emit(connect => $handle) unless $args->{tls};
155 2         9 my $reactor = $self->reactor;
156 2         9 $reactor->remove($handle);
157              
158             # Start TLS handshake
159 2         9 weaken $self;
160 2         14 my $tls = Mojo::IOLoop::TLS->new($handle)->reactor($self->reactor);
161 2     0   15 $tls->on(upgrade => sub { $self->_cleanup->emit(connect => pop) });
  0         0  
162 2     2   13 $tls->on(error => sub { $self->emit(error => pop) });
  2         6  
163 2         16 $tls->negotiate(%$args);
164             }
165              
166             sub _wait {
167 195     195   824 my ($self, $next, $handle, $args) = @_;
168 195         914 weaken $self;
169 195     194   743 $self->reactor->io($handle => sub { $self->$next($args) })->watch($handle, 0, 1);
  194         1164  
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