File Coverage

blib/lib/Net/EmptyPort.pm
Criterion Covered Total %
statement 70 71 98.5
branch 39 48 81.2
condition 25 30 83.3
subroutine 13 13 100.0
pod 5 5 100.0
total 152 167 91.0


line stmt bran cond sub pod time code
1             package Net::EmptyPort;
2 19     19   102136 use strict;
  19         46  
  19         556  
3 19     19   112 use warnings;
  19         32  
  19         562  
4 19     19   97 use base qw/Exporter/;
  19         37  
  19         2344  
5 19     19   10564 use IO::Socket::IP;
  19         347329  
  19         97  
6 19     19   8798 use Time::HiRes ();
  19         1306  
  19         17482  
7              
8             our @EXPORT = qw/ can_bind empty_port check_port wait_port /;
9             our @EXPORT_OK = qw/ listen_socket /;
10              
11             sub can_bind {
12 8     8 1 6946 my ($host, $port, $proto) = @_;
13             # The following must be split across two statements, due to
14             # https://rt.perl.org/Public/Bug/Display.html?id=124248
15 8         24 my $s = _listen_socket($host, $port, $proto);
16 8         4182 return defined $s;
17             }
18              
19             sub _listen_socket {
20 29     29   99 my ($host, $port, $proto) = @_;
21 29   100     184 $port ||= 0;
22 29   100     159 $proto ||= 'tcp';
23 29 100       565 IO::Socket::IP->new(
    50          
24             (($proto eq 'udp') ? () : (Listen => 5)),
25             LocalAddr => $host,
26             LocalPort => $port,
27             Proto => $proto,
28             V6Only => 1,
29             (($^O eq 'MSWin32') ? () : (ReuseAddr => 1)),
30             );
31             }
32              
33             sub listen_socket {
34 1     1 1 3 my ($host, $proto) = @{$_[0]}{qw(host proto)};
  1         4  
35 1 50       3 $host = '127.0.0.1' unless defined $host;
36 1         4 return _listen_socket($host, undef, $proto);
37             }
38              
39             # get a empty port on 49152 .. 65535
40             # http://www.iana.org/assignments/port-numbers
41             sub empty_port {
42 24 100 100 24 1 10234 my ($host, $port, $proto) = @_ && ref $_[0] eq 'HASH' ? ($_[0]->{host}, $_[0]->{port}, $_[0]->{proto}) : (undef, @_);
43 24 100       105 $host = '127.0.0.1'
44             unless defined $host;
45 24 100       102 $proto = $proto ? lc($proto) : 'tcp';
46              
47 24 100       111 if (defined $port) {
48             # to ensure lower bound, check one by one in order
49 4 100 100     26 $port = 49152 unless $port =~ /^[0-9]+$/ && $port < 49152;
50 4         12 while ( $port++ < 65000 ) {
51             # Remote checks don't work on UDP, and Local checks would be redundant here...
52 4 50 66     17 next if ($proto eq 'tcp' && check_port({ host => $host, port => $port }));
53 4 50       16 return $port if can_bind($host, $port, $proto);
54             }
55             } else {
56             # kernel will select an unused port
57 20         136 while ( my $sock = _listen_socket($host, undef, $proto) ) {
58 20         20194 $port = $sock->sockport;
59 20         1615 $sock->close;
60 20 50 33     976 next if ($proto eq 'tcp' && check_port({ host => $host, port => $port }));
61 20         191 return $port;
62             }
63             }
64 0         0 die "empty port not found";
65             }
66              
67             sub check_port {
68 92 100 100 92 1 1143 my ($host, $port, $proto) = @_ && ref $_[0] eq 'HASH' ? ($_[0]->{host}, $_[0]->{port}, $_[0]->{proto}) : (undef, @_);
69 92 100       306 $host = '127.0.0.1'
70             unless defined $host;
71 92 100       310 $proto = $proto ? lc($proto) : 'tcp';
72              
73             # for TCP, we do a remote port check
74             # for UDP, we do a local port check, like empty_port does
75 92 0       1414 my $sock = ($proto eq 'tcp') ?
    50          
76             IO::Socket::IP->new(
77             Proto => 'tcp',
78             PeerAddr => $host,
79             PeerPort => $port,
80             V6Only => 1,
81             ) :
82             IO::Socket::IP->new(
83             Proto => $proto,
84             LocalAddr => $host,
85             LocalPort => $port,
86             V6Only => 1,
87             (($^O eq 'MSWin32') ? () : (ReuseAddr => 1)),
88             )
89             ;
90              
91 90 100       68318 if ($sock) {
92 14         471 close $sock;
93 14         164 return 1; # The port is used.
94             }
95             else {
96 76         547 return 0; # The port is not used.
97             }
98              
99             }
100              
101             sub _make_waiter {
102 19     19   77 my $max_wait = shift;
103 19         82 my $waited = 0;
104 19         52 my $sleep = 0.001;
105              
106             return sub {
107 71 100 100 71   606 return 0 if $max_wait >= 0 && $waited > $max_wait;
108              
109 67         17116999 Time::HiRes::sleep($sleep);
110 67         636 $waited += $sleep;
111 67         294 $sleep *= 2;
112              
113 67         438 return 1;
114 19         622 };
115             }
116              
117             sub wait_port {
118 19     19 1 2660 my ($host, $port, $max_wait, $proto);
119 19 100 100     503 if (@_ && ref $_[0] eq 'HASH') {
    100          
120 15         145 ($host, $port, $max_wait, $proto) = ($_[0]->{host}, $_[0]->{port}, $_[0]->{max_wait}, $_[0]->{proto});
121             } elsif (@_==4) {
122             # backward compat.
123 1         4 ($port, (my $sleep), (my $retry), $proto) = @_;
124 1         4 $max_wait = $sleep * $retry;
125             } else {
126 3         9 ($port, $max_wait, $proto) = @_;
127             }
128 19 100       133 $host = '127.0.0.1' unless defined $host;
129 19   100     144 $max_wait ||= 10;
130 19 100       292 $proto = $proto ? lc($proto) : 'tcp';
131 19         158 my $waiter = _make_waiter($max_wait);
132              
133 19         476 while ( $waiter->() ) {
134 67 50 33     1555 if ($^O eq 'MSWin32' && defined($port) ? `$^X -MTest::TCP::CheckPort -echeck_port $host $port $proto` : check_port({ host => $host, port => $port, proto => $proto })) {
    100          
135 14         160 return 1;
136             }
137             }
138 4         72 return 0;
139             }
140              
141             1;
142              
143             __END__