File Coverage

blib/lib/IO/BindHandles.pm
Criterion Covered Total %
statement 89 102 87.2
branch 22 40 55.0
condition 20 42 47.6
subroutine 9 10 90.0
pod 5 5 100.0
total 145 199 72.8


line stmt bran cond sub pod time code
1             package IO::BindHandles;
2             # ABSTRACT: Bind a set of handles for buffered tunneling
3              
4 5     5   3174 use strict;
  5         10  
  5         155  
5 5     5   25 use warnings;
  5         10  
  5         121  
6 5     5   5255 use IO::Handle;
  5         39697  
  5         264  
7 5     5   7370 use IO::Select;
  5         21273  
  5         2251  
8              
9             sub new {
10 3     3 1 1004708 my $class = shift;
11 3   33     58 $class = ref $class || $class;
12 3         27 my %options = @_;
13              
14 3         7 my $timeout = 0.5;
15 3 50       39 $timeout = $options{timeout} if exists $options{timeout};
16 3 50       67 $timeout += 0 if defined $timeout;
17              
18 3         9 my $handles = $options{handles};
19 3 50       26 die "handles must be an ARRAY ref" if ref $handles ne 'ARRAY';
20 3 50       11 warn "odd number of handles in BindHandles" if scalar(@$handles) & 1;
21              
22 3         16 my @all_handles;
23 3         15 for (my $i = 0; $i < scalar @$handles; $i += 2) {
24 4         14 my $read = $handles->[$i];
25 4         12 my $write = $handles->[$i+1];
26 4         18 $read->autoflush(1);
27 4         116 $write->autoflush(1);
28 4         111 my $buffer = '';
29 4         18 push @all_handles, [ $read, $write, $buffer ];
30             }
31              
32 3         21 my $self = {
33             timeout => $timeout,
34             handles => \@all_handles,
35             };
36              
37 3         13 bless $self, $class;
38 3         11 return $self;
39             }
40              
41              
42             sub loop {
43 0     0 1 0 my $self = shift;
44 0 0 0     0 $self->timeout(0.1) if defined $self->timeout && $self->timeout == 0;
45             #warn "In loop...";
46 0         0 while ($self->bound()) {
47             #warn "rwcycle...";
48 0         0 $self->rwcycle();
49             }
50             #warn "Out loop...";
51             }
52              
53              
54             sub bound {
55 9     9 1 1495 my $self = shift;
56             #warn "in bound...";
57 14 100 33     287 my @list =
      33        
      100        
      66        
      66        
58             grep {
59 9         188 (
60             $_->[0]->opened && $_->[1]->opened &&
61             !$_->[0]->error && !$_->[1]->error
62             )
63             ||
64             ( $_->[2] && $_->[1]->opened && !$_->[1]->error )
65 9         45 } @{$self->{handles}};
66             #warn "out bound...";
67             return
68 9         622 scalar @list;
69             }
70              
71              
72             sub rwcycle {
73 7     7 1 32 my $self = shift;
74 5     5   5558 use bytes;
  5         55  
  5         28  
75             #warn "in rwcycle...";
76              
77             # we listen on all handles all the time for reading...
78 11 50       119 my @read_handles = grep { $_->opened && !$_->error }
  11         240  
79 11 50       91 map { $_->[0] }
80 7         29 grep { $_->[1]->opened && !$_->[1]->error }
81 7         21 @{$self->{handles}};
82             #warn "Selecting read on ".join ', ', @read_handles;
83 7         323 my $read_select = IO::Select->new(@read_handles);
84              
85              
86             # but we only listen for writing on those that we have
87             # something to write to.
88 3 50       14 my @write_handles = grep { $_->opened && !$_->error }
  3         8  
89 11         47 map { $_->[1] }
90 7         762 grep { $_->[2] } @{$self->{handles}};
  7         30  
91             #warn "Selecting write on ".join ', ', @write_handles;
92 7         75 my $write_select = IO::Select->new(@write_handles);
93              
94              
95             # we check for exception in all handles;
96 7         162 my @except_handles = (@read_handles, @write_handles);
97             #warn "Selecting exception on ".join ', ', @except_handles;
98 7         24 my $except_select = IO::Select->new(@except_handles);
99              
100              
101             # now let's see if there's something to be read or written
102             #warn "Select...";
103 7         390 my ($r_r, $r_w, $e) = IO::Select::select($read_select, $write_select, $except_select, $self->timeout);
104             #warn "done...";
105              
106 7 100 50     74395 if ($r_r && scalar @$r_r) {
107 5         21 foreach my $h (@$r_r) {
108 5         16 my ($h_desc) = grep { $_->[0] eq $h } @{$self->{handles}};
  8         51  
  5         22  
109 5         12 my $handle = $h_desc->[0];
110 5         9 my $temp_buf;
111             #warn "Reading from $handle.";
112 5         71 my $num_read = $handle->sysread($temp_buf, 1024);
113 5 100 66     200 if (defined $num_read && $num_read > 0) {
114             #warn "Read $num_read";
115 3         25 $h_desc->[2] .= substr($temp_buf, 0, $num_read);
116             } else {
117             #warn "Error...";
118 2         47 $handle->close;
119 2         115 $h_desc->[3] = 1;
120 2 100       15 $h_desc->[1]->close unless $h_desc->[2];
121             }
122             }
123             }
124              
125 7 100 50     72 if ($r_w && scalar @$r_w) {
126 3         9 foreach my $h (@$r_w) {
127 3         6 my ($h_desc) = grep { $_->[1] eq $h } @{$self->{handles}};
  5         18  
  3         9  
128 3         12 my $handle = $h_desc->[1];
129             #warn "Writing on $handle.";
130 3         42 my $num_write = $handle->syswrite($h_desc->[2], length($h_desc->[2]));
131 3 50 33     114 if (defined $num_write && $num_write >= 0) {
132             #warn "Wrote $num_write.";
133 3         17 substr($h_desc->[2],0,$num_write,'');
134             } else {
135             #warn "Error.";
136 0         0 $handle->close;
137 0         0 $h_desc->[0]->close;
138             }
139             }
140             }
141 7 50 50     96 if ($e && scalar @$e) {
142 0         0 foreach my $h (@$r_w) {
143             #warn "Exception in $h";
144 0 0       0 my ($h_desc) = grep { $_->[1] eq $h || $_->[0] eq $h } @{$self->{handles}};
  0         0  
  0         0  
145             # we close the writing handles unless the
146             # exception was in the reading handle and there
147             # is still buffered content to be sent.
148 0 0 0     0 unless ($h eq $h_desc->[0] && $h_desc->[2]) {
149 0         0 $h_desc->[1]->close;
150             }
151             # in any case we won't read anymore
152 0         0 $h_desc->[0]->close;
153             }
154             }
155              
156 7         13 foreach my $h (@{$self->{handles}}) {
  7         42  
157 11 50 66     253 if (exists $h->[3] && $h->[3] && !$h->[2]) {
      66        
158 3         23 $h->[1]->close()
159 2 50       11 unless grep { $_->[0] eq $h->[1] } @{$self->{handles}}
  2         6  
160             ;
161             }
162             }
163              
164             }
165              
166              
167             sub timeout {
168 7     7 1 10 my $self = shift;
169 7         35 my $ret = $self->{timeout};
170 7 50       50 $self->{timeout} = shift if @_;
171 7         68 return $ret;
172             }
173              
174              
175             1;
176              
177             __END__