File Coverage

blib/lib/Net/Shaper.pm
Criterion Covered Total %
statement 73 74 98.6
branch 17 28 60.7
condition 3 6 50.0
subroutine 16 16 100.0
pod 0 9 0.0
total 109 133 81.9


line stmt bran cond sub pod time code
1             package Net::Shaper;
2              
3 2     2   11742 use 5.006;
  2         8  
  2         74  
4 2     2   10 use strict;
  2         4  
  2         56  
5 2     2   8 use warnings;
  2         22  
  2         56  
6              
7 2     2   1752 use IO::Socket;
  2         53852  
  2         8  
8 2     2   2878 use IO::Select;
  2         3784  
  2         94  
9 2     2   1934 use Time::HiRes;
  2         3890  
  2         10  
10              
11             our $VERSION = '0.3';
12              
13             sub new {
14 1     1 0 1002965 my($type, %args) = @_;
15 1   33     165 $type = ref $type || $type;
16              
17 1         69 return bless \%args, $type;
18             }
19              
20 2 50   2 0 80 sub LocalPort { @_ > 1 ? $_[0]->{LocalPort} = $_[1] : $_[0]->{LocalPort} }
21 1 50   1 0 38 sub LocalAddr { @_ > 1 ? $_[0]->{LocalAddr} = $_[1] : $_[0]->{LocalAddr} }
22 1 50   1 0 24 sub LocalHost { @_ > 1 ? $_[0]->{LocalHost} = $_[1] : $_[0]->{LocalHost} }
23 1 50   1 0 9 sub PeerPort { @_ > 1 ? $_[0]->{PeerPort} = $_[1] : $_[0]->{PeerPort} }
24 2 50   2 0 185 sub PeerAddr { @_ > 1 ? $_[0]->{PeerAddr} = $_[1] : $_[0]->{PeerAddr} }
25 1 50   1 0 18 sub PeerHost { @_ > 1 ? $_[0]->{PeerHost} = $_[1] : $_[0]->{PeerHost} }
26 1 50   1 0 13 sub Bps { @_ > 1 ? $_[0]->{Bps} = $_[1] : $_[0]->{Bps} }
27              
28             sub run {
29 1     1 0 25 my $this = shift;
30              
31 1         216 local $SIG{PIPE} = 'IGNORE';
32              
33 1         49 my @localArgs = map { $_ => $this->$_() } grep defined($this->$_()), qw(LocalPort LocalAddr LocalHost);
  1         158  
34 1         22 my @remoteArgs = map { $_ => $this->$_() } grep defined($this->$_()), qw(PeerPort PeerAddr PeerHost );
  1         5  
35              
36 1         70 my $src = IO::Socket::INET->new(@localArgs, Listen => SOMAXCONN, Reuse => 1, Proto => 'tcp');
37              
38 1         1308 my $select = IO::Select->new($src);
39              
40 1         124 my $bps = $this->Bps();
41              
42 1         2 my(@dest, $done);
43              
44 1     1   82 $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = sub { @dest = (); $done = 1;};
  1         2636  
  1         11  
45              
46 1         11 while (!$done) {
47 7 100       289 if ($select->can_read(0)) {
48 1         44 my $client = $src->accept();
49 1         206 push @dest, [$client => IO::Socket::INET->new(@remoteArgs, Proto => 'tcp')];
50             }
51              
52 7         1763 my $start = Time::HiRes::time();
53 7         27 my @recvBuf = my @sendBuf = ();
54 7         12 my $bytes = 0;
55 7 100 66     71 my $bytesToRead = $bps && @dest ? $bps / @dest : 32768;
56 7         21 for (my $i = 0; $i < @dest; $i++) {
57 6         10 my($client, $dest) = @{ $dest[$i] };
  6         14  
58 6         40 $client->recv($recvBuf[$i], $bytesToRead, IO::Socket::MSG_DONTWAIT);
59 6         144 $dest->recv ($sendBuf[$i], $bytesToRead, IO::Socket::MSG_DONTWAIT);
60 6         100 $bytes += length($recvBuf[$i]) + length($sendBuf[$i]);
61             }
62 7         22 my $now = Time::HiRes::time();
63              
64 7 100       18 unless ($bytes) {
65             # wait for something to be ready to read
66 3         27 my $sel = IO::Select->new();
67 3         49 $sel->add($_) for $src, map @$_, @dest;
68 3         218 my @ready = $sel->can_read();
69 3         998539 for my $fh (@ready) {
70 3         47 $fh->recv(my $buf, 1, IO::Socket::MSG_PEEK);
71 3 100       108 unless (length($buf)) {
72 2 0       9 @dest = grep { $_->[0] != $fh && $_->[1] != $fh } @dest;
  0         0  
73             }
74             }
75 3         197 next;
76             }
77              
78 4 50       18 if ($bps) {
79 4 50       29 unless ($bytes / ($now - $start) < $bps) {
80 4         4000263 Time::HiRes::sleep(($bytes - $bps * ($now - $start)) / $bps);
81             }
82             }
83              
84 4         51 for (my $i = 0; $i < @dest; $i++) {
85 4         9 my($client, $dest) = @{ $dest[$i] };
  4         93  
86 4         70 $dest->send($recvBuf[$i]);
87 4         391 $client->send($sendBuf[$i]);
88             }
89             }
90             }
91              
92             1;
93             __END__