File Coverage

blib/lib/IO/Socket/Timeout.pm
Criterion Covered Total %
statement 112 122 91.8
branch 23 36 63.8
condition 24 40 60.0
subroutine 23 27 85.1
pod 1 1 100.0
total 183 226 80.9


line stmt bran cond sub pod time code
1             #
2             # This file is part of IO-Socket-Timeout
3             #
4             # This software is copyright (c) 2013 by Damien "dams" Krotkine.
5             #
6             # This is free software; you can redistribute it and/or modify it under
7             # the same terms as the Perl 5 programming language system itself.
8             #
9             package IO::Socket::Timeout;
10             $IO::Socket::Timeout::VERSION = '0.32';
11 5     5   133591 use strict;
  5         11  
  5         129  
12 5     5   25 use warnings;
  5         8  
  5         130  
13 5     5   21 use Config;
  5         11  
  5         153  
14 5     5   22 use Carp;
  5         9  
  5         2367  
15              
16              
17             # ABSTRACT: IO::Socket with read/write timeout
18              
19              
20             sub import {
21 5     5   42 shift;
22 5         4421 foreach (@_) {
23 2         7 _create_composed_class( $_, 'IO::Socket::Timeout::Role::SetSockOpt');
24 2         4 _create_composed_class( $_, 'IO::Socket::Timeout::Role::PerlIO');
25             }
26             }
27              
28              
29             sub enable_timeouts_on {
30 8     8 1 265854 my ($class, $socket) = @_;
31 8 50       84 defined $socket
32             or return;
33 8 50       132 $socket->isa('IO::Socket')
34             or croak 'make_timeouts_aware can be used only on instances that inherit from IO::Socket';
35              
36 8         527 my $osname = $Config{osname};
37 8 100 33     234 if ( ! $ENV{PERL_IO_SOCKET_TIMEOUT_FORCE_SELECT}
      66        
38             && ( $osname eq 'darwin' || $osname eq 'linux' || $osname eq 'freebsd' ) ) {
39 5         60 _compose_roles($socket, 'IO::Socket::Timeout::Role::SetSockOpt');
40             } else {
41 3         105 require PerlIO::via::Timeout;
42 3     1   255 binmode($socket, ':via(Timeout)');
  1         10  
  1         2  
  1         41  
43 3         45143 _compose_roles($socket, 'IO::Socket::Timeout::Role::PerlIO');
44             }
45              
46 8         176 $socket->enable_timeout;
47 8         140 return $socket;
48             }
49              
50             sub _create_composed_class {
51 12     12   44 my ($class, @roles) = @_;
52 12         118 my $composed_class = $class . '__with__' . join('__and__', @roles);
53 12         43 my $path = $composed_class; $path =~ s|::|/|g; $path .= '.pm';
  12         232  
  12         47  
54 12 100       101 if ( ! exists $INC{$path}) {
55 5     5   26 no strict 'refs';
  5         10  
  5         1573  
56 8         34 *{"${composed_class}::ISA"} = [ $class, @roles ];
  8         1109  
57 8         39 $INC{$path} = __FILE__;
58             }
59 12         93954 return $composed_class;
60             }
61              
62             sub _compose_roles {
63 8     8   61 my ($instance, @roles) = @_;
64 8         98 bless $instance, _create_composed_class(ref $instance, @roles);
65             }
66              
67             # sysread FILEHANDLE,SCALAR,LENGTH,OFFSET
68             BEGIN {
69 5     5   47 my $osname = $Config{osname};
70 5 100 33     2820 if ( $ENV{PERL_IO_SOCKET_TIMEOUT_FORCE_SELECT} ||
      33        
      66        
71             $osname ne 'darwin' && $osname ne 'linux' && $osname ne 'freebsd'
72             ) {
73             # this variable avoids infinite recursion, because
74             # PerlIO::via::Timeout->READ calls sysread.
75 1         16 my $_prevent_deep_recursion;
76             *CORE::GLOBAL::sysread = sub {
77 31         24354 my $args_count = scalar(@_);
78 31 100 100     295 $_prevent_deep_recursion
    100 66        
79             || ! PerlIO::via::Timeout::has_timeout_layer($_[0])
80             || ! PerlIO::via::Timeout::timeout_enabled($_[0])
81             and return ( $args_count == 4 ? CORE::sysread($_[0], $_[1], $_[2], $_[3])
82             : CORE::sysread($_[0], $_[1], $_[2])
83             );
84 15         481 $_prevent_deep_recursion = 1;
85 15         92 my $ret_val = PerlIO::via::Timeout->READ($_[1], $_[2], $_[0]);
86 15         200530 $_prevent_deep_recursion = 0;
87 15         40 return $ret_val;
88             }
89 1         180 }
90             }
91              
92             # syswrite FILEHANDLE,SCALAR,LENGTH,OFFSET
93             BEGIN {
94 5     5   35 my $osname = $Config{osname};
95 5 100 33     228 if ( $ENV{PERL_IO_SOCKET_TIMEOUT_FORCE_SELECT} ||
      33        
      66        
96             $osname ne 'darwin' && $osname ne 'linux' && $osname ne 'freebsd'
97             ) {
98             # this variable avoids infinite recursion, because
99             # PerlIO::via::Timeout->WRITE calls syswrite.
100 1         2 my $_prevent_deep_recursion;
101             *CORE::GLOBAL::syswrite = sub {
102 12         11657 my $args_count = scalar(@_);
103 12 0 66     456 $_prevent_deep_recursion
    50 66        
    50          
104             || ! PerlIO::via::Timeout::has_timeout_layer($_[0])
105             || ! PerlIO::via::Timeout::timeout_enabled($_[0])
106             and return( $args_count == 4 ? CORE::syswrite($_[0], $_[1], $_[2], $_[3])
107             : $args_count == 3 ? CORE::syswrite($_[0], $_[1], $_[2])
108             : CORE::syswrite($_[0], $_[1])
109             );
110 6         244 $_prevent_deep_recursion = 1;
111 6         67 my $ret_val = PerlIO::via::Timeout->WRITE($_[1], $_[0]);
112 6         61 $_prevent_deep_recursion = 0;
113 6         18 return $ret_val;
114             }
115 1         42 }
116             }
117              
118             package IO::Socket::Timeout::Role::SetSockOpt;
119             $IO::Socket::Timeout::Role::SetSockOpt::VERSION = '0.32';
120 5     5   25 use Carp;
  5         11  
  5         621  
121 5     5   2572 use Socket;
  5         14074  
  5         6222  
122              
123             sub _check_attributes {
124 7     38   21 my ($self) = @_;
125 7 50       45 grep { $_ < 0 } grep { defined } map { ${*$self}{$_} } qw(ReadTimeout WriteTimeout)
  10         74  
  14         45  
  14         29  
  14         78  
126             and croak "if defined, 'ReadTimeout' and 'WriteTimeout' attributes should be >= 0";
127             }
128              
129             sub read_timeout {
130 4     16   107 my ($self) = @_;
131 4 50       40 @_ > 1 and ${*$self}{ReadTimeout} = $_[1], $self->_check_attributes, $self->_set_sock_opt;
  4         73  
132 4         15 ${*$self}{ReadTimeout}
133 4         37 }
134              
135             sub write_timeout {
136 3     3   36 my ($self) = @_;
137 3 50       14 @_ > 1 and ${*$self}{WriteTimeout} = $_[1], $self->_check_attributes, $self->_set_sock_opt;
  3         26  
138 3         14 ${*$self}{WriteTimeout}
139 3         25 }
140              
141 5     5   67 sub enable_timeout { $_[0]->timeout_enabled(1) }
142 0     0   0 sub disable_timeout { $_[0]->timeout_enabled(0) }
143             sub timeout_enabled {
144 5     5   14 my ($self) = @_;
145 5 50       30 @_ > 1 and ${*$self}{TimeoutEnabled} = !!$_[1], $self->_set_sock_opt;
  5         111  
146 5         15 ${*$self}{TimeoutEnabled}
147 5         70 }
148              
149             sub _set_sock_opt {
150 12     12   23 my ($self) = @_;
151 12         130 my $read_seconds;
152             my $read_useconds;
153 0         0 my $write_seconds;
154 0         0 my $write_useconds;
155 12 50       20 if (${*$self}{TimeoutEnabled}) {
  12         47  
156 12   100     15 my $read_timeout = ${*$self}{ReadTimeout} || 0;
157 12         29 $read_seconds = int( $read_timeout );
158 12         31 $read_useconds = int( 1_000_000 * ( $read_timeout - $read_seconds ));
159 12   100     18 my $write_timeout = ${*$self}{WriteTimeout} || 0;
160 12         19 $write_seconds = int( $write_timeout );
161 12         29 $write_useconds = int( 1_000_000 * ( $write_timeout - $write_seconds ));
162             } else {
163 0         0 $read_seconds = 0; $read_useconds = 0;
  0         0  
164 0         0 $write_seconds = 0; $write_useconds = 0;
  0         0  
165             }
166 12         85 my $read_struct = pack( 'l!l!', $read_seconds, $read_useconds );
167 12         36 my $write_struct = pack( 'l!l!', $write_seconds, $write_useconds );
168              
169 12 50       97 $self->setsockopt( SOL_SOCKET, SO_RCVTIMEO, $read_struct )
170             or croak "setsockopt(SO_RCVTIMEO): $!";
171              
172 12 50       195 $self->setsockopt( SOL_SOCKET, SO_SNDTIMEO, $write_struct )
173             or croak "setsockopt(SO_SNDTIMEO): $!";
174             }
175              
176             package IO::Socket::Timeout::Role::PerlIO;
177             $IO::Socket::Timeout::Role::PerlIO::VERSION = '0.32';
178 5     5   4042 use PerlIO::via::Timeout;
  5         25106  
  5         683  
179              
180 2     2   114 sub read_timeout { goto &PerlIO::via::Timeout::read_timeout }
181 0     0   0 sub write_timeout { goto &PerlIO::via::Timeout::write_timeout }
182 3     3   119 sub enable_timeout { goto &PerlIO::via::Timeout::enable_timeout }
183 0     0   0 sub disable_timeout { goto &PerlIO::via::Timeout::disable_timeout }
184 0     0   0 sub timeout_enabled { goto &PerlIO::via::Timeout::timeout_enabled }
185              
186             1;
187              
188             __END__