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.29';
11 10     10   212399 use strict;
  10         20  
  10         296  
12 10     10   37 use warnings;
  10         15  
  10         234  
13 10     10   32 use Config;
  10         10  
  10         276  
14 10     10   31 use Carp;
  10         14  
  10         3064  
15              
16              
17             # ABSTRACT: IO::Socket with read/write timeout
18              
19              
20             sub import {
21 10     10   72 shift;
22 10         19874 foreach (@_) {
23 2         4 _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 31     31 1 17496829 my ($class, $socket) = @_;
31 31 50       213 defined $socket
32             or return;
33 31 50       310 $socket->isa('IO::Socket')
34             or croak 'make_timeouts_aware can be used only on instances that inherit from IO::Socket';
35              
36 31         816 my $osname = $Config{osname};
37 31 100 33     556 if ( ! $ENV{PERL_IO_SOCKET_TIMEOUT_FORCE_SELECT}
      66        
38             && ( $osname eq 'darwin' || $osname eq 'linux' || $osname eq 'freebsd' ) ) {
39 28         159 _compose_roles($socket, 'IO::Socket::Timeout::Role::SetSockOpt');
40             } else {
41 3         59 require PerlIO::via::Timeout;
42 3     1   282 binmode($socket, ':via(Timeout)');
  1         17  
  1         3  
  1         43  
43 3         2075 _compose_roles($socket, 'IO::Socket::Timeout::Role::PerlIO');
44             }
45              
46 31         230 $socket->enable_timeout;
47 31         180 return $socket;
48             }
49              
50             sub _create_composed_class {
51 35     35   105 my ($class, @roles) = @_;
52 35         162 my $composed_class = $class . '__with__' . join('__and__', @roles);
53 35         68 my $path = $composed_class; $path =~ s|::|/|g; $path .= '.pm';
  35         294  
  35         49  
54 35 100       154 if ( ! exists $INC{$path}) {
55 10     10   54 no strict 'refs';
  10         11  
  10         2284  
56 13         21 *{"${composed_class}::ISA"} = [ $class, @roles ];
  13         492  
57 13         46 $INC{$path} = __FILE__;
58             }
59 35         69223 return $composed_class;
60             }
61              
62             sub _compose_roles {
63 31     31   94 my ($instance, @roles) = @_;
64 31         122 bless $instance, _create_composed_class(ref $instance, @roles);
65             }
66              
67             # sysread FILEHANDLE,SCALAR,LENGTH,OFFSET
68             BEGIN {
69 10     10   95 my $osname = $Config{osname};
70 10 100 33     1326 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         1 my $_prevent_deep_recursion;
76             *CORE::GLOBAL::sysread = sub {
77 31         12876 my $args_count = scalar(@_);
78 31 100 100     195 $_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         351 $_prevent_deep_recursion = 1;
85 15         56 my $ret_val = PerlIO::via::Timeout->READ($_[1], $_[2], $_[0]);
86 15         200442 $_prevent_deep_recursion = 0;
87 15         21 return $ret_val;
88             }
89 1         237 }
90             }
91              
92             # syswrite FILEHANDLE,SCALAR,LENGTH,OFFSET
93             BEGIN {
94 10     10   125 my $osname = $Config{osname};
95 10 100 33     398 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         1 my $_prevent_deep_recursion;
101             *CORE::GLOBAL::syswrite = sub {
102 12         4831 my $args_count = scalar(@_);
103 12 0 66     759 $_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         142 $_prevent_deep_recursion = 1;
111 6         29 my $ret_val = PerlIO::via::Timeout->WRITE($_[1], $_[0]);
112 6         52 $_prevent_deep_recursion = 0;
113 6         11 return $ret_val;
114             }
115 1         46 }
116             }
117              
118             package IO::Socket::Timeout::Role::SetSockOpt;
119             $IO::Socket::Timeout::Role::SetSockOpt::VERSION = '0.29';
120 10     10   43 use Carp;
  10         14  
  10         591  
121 10     10   1689 use Socket;
  10         9481  
  10         8463  
122              
123             sub _check_attributes {
124 41     72   52 my ($self) = @_;
125 41 50       77 grep { $_ < 0 } grep { defined } map { ${*$self}{$_} } qw(ReadTimeout WriteTimeout)
  61         213  
  82         141  
  82         75  
  82         245  
126             and croak "if defined, 'ReadTimeout' and 'WriteTimeout' attributes should be >= 0";
127             }
128              
129             sub read_timeout {
130 21     33   193 my ($self) = @_;
131 21 50       77 @_ > 1 and ${*$self}{ReadTimeout} = $_[1], $self->_check_attributes, $self->_set_sock_opt;
  21         252  
132 21         121 ${*$self}{ReadTimeout}
  21         55  
133             }
134              
135             sub write_timeout {
136 20     20   108 my ($self) = @_;
137 20 50       68 @_ > 1 and ${*$self}{WriteTimeout} = $_[1], $self->_check_attributes, $self->_set_sock_opt;
  20         108  
138 20         147 ${*$self}{WriteTimeout}
  20         42  
139             }
140              
141 28     28   200 sub enable_timeout { $_[0]->timeout_enabled(1) }
142 0     0   0 sub disable_timeout { $_[0]->timeout_enabled(0) }
143             sub timeout_enabled {
144 28     28   38 my ($self) = @_;
145 28 50       100 @_ > 1 and ${*$self}{TimeoutEnabled} = !!$_[1], $self->_set_sock_opt;
  28         248  
146 28         208 ${*$self}{TimeoutEnabled}
  28         74  
147             }
148              
149             sub _set_sock_opt {
150 69     69   82 my ($self) = @_;
151 69         221 my $read_seconds;
152             my $read_useconds;
153 0         0 my $write_seconds;
154 0         0 my $write_useconds;
155 69 50       66 if (${*$self}{TimeoutEnabled}) {
  69         179  
156 69   100     48 my $read_timeout = ${*$self}{ReadTimeout} || 0;
157 69         106 $read_seconds = int( $read_timeout );
158 69         117 $read_useconds = int( 1_000_000 * ( $read_timeout - $read_seconds ));
159 69   100     81 my $write_timeout = ${*$self}{WriteTimeout} || 0;
160 69         71 $write_seconds = int( $write_timeout );
161 69         82 $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 69         291 my $read_struct = pack( 'l!l!', $read_seconds, $read_useconds );
167 69         112 my $write_struct = pack( 'l!l!', $write_seconds, $write_useconds );
168              
169 69 50       348 $self->setsockopt( SOL_SOCKET, SO_RCVTIMEO, $read_struct )
170             or croak "setsockopt(SO_RCVTIMEO): $!";
171              
172 69 50       684 $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.29';
178 10     10   4946 use PerlIO::via::Timeout;
  10         34194  
  10         1179  
179              
180 2     2   87 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   57 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__