File Coverage

blib/lib/LWP/ConnCache.pm
Criterion Covered Total %
statement 32 99 32.3
branch 4 36 11.1
condition 1 20 5.0
subroutine 6 17 35.2
pod 11 11 100.0
total 54 183 29.5


line stmt bran cond sub pod time code
1             package LWP::ConnCache;
2              
3 1     1   6 use strict;
  1         2  
  1         826  
4              
5             our $VERSION = '6.29';
6             our $DEBUG;
7              
8             sub new {
9 1     1 1 4 my($class, %cnf) = @_;
10              
11 1         1 my $total_capacity = 1;
12 1 50       4 if (exists $cnf{total_capacity}) {
13 1         2 $total_capacity = delete $cnf{total_capacity};
14             }
15 1 0 33     3 if (%cnf && $^W) {
16 0         0 require Carp;
17 0         0 Carp::carp("Unrecognised options: @{[sort keys %cnf]}")
  0         0  
18             }
19 1         2 my $self = bless { cc_conns => [] }, $class;
20 1         3 $self->total_capacity($total_capacity);
21 1         2 $self;
22             }
23              
24              
25             sub deposit {
26 0     0 1 0 my($self, $type, $key, $conn) = @_;
27 0         0 push(@{$self->{cc_conns}}, [$conn, $type, $key, time]);
  0         0  
28 0         0 $self->enforce_limits($type);
29 0         0 return;
30             }
31              
32              
33             sub withdraw {
34 1     1 1 2 my($self, $type, $key) = @_;
35 1         5 my $conns = $self->{cc_conns};
36 1         5 for my $i (0 .. @$conns - 1) {
37 0         0 my $c = $conns->[$i];
38 0 0 0     0 next unless $c->[1] eq $type && $c->[2] eq $key;
39 0         0 splice(@$conns, $i, 1); # remove it
40 0         0 return $c->[0];
41             }
42 1         5 return undef;
43             }
44              
45              
46             sub total_capacity {
47 1     1 1 2 my $self = shift;
48 1         5 my $old = $self->{cc_limit_total};
49 1 50       7 if (@_) {
50 1         2 $self->{cc_limit_total} = shift;
51 1         3 $self->enforce_limits;
52             }
53 1         2 $old;
54             }
55              
56              
57             sub capacity {
58 0     0 1 0 my $self = shift;
59 0         0 my $type = shift;
60 0         0 my $old = $self->{cc_limit}{$type};
61 0 0       0 if (@_) {
62 0         0 $self->{cc_limit}{$type} = shift;
63 0         0 $self->enforce_limits($type);
64             }
65 0         0 $old;
66             }
67              
68              
69             sub enforce_limits {
70 1     1 1 2 my($self, $type) = @_;
71 1         2 my $conns = $self->{cc_conns};
72              
73 1 50       4 my @types = $type ? ($type) : ($self->get_types);
74 1         2 for $type (@types) {
75 0 0       0 next unless $self->{cc_limit};
76 0         0 my $limit = $self->{cc_limit}{$type};
77 0 0       0 next unless defined $limit;
78 0         0 for my $i (reverse 0 .. @$conns - 1) {
79 0 0       0 next unless $conns->[$i][1] eq $type;
80 0 0       0 if (--$limit < 0) {
81 0         0 $self->dropping(splice(@$conns, $i, 1), "$type capacity exceeded");
82             }
83             }
84             }
85              
86 1 50       3 if (defined(my $total = $self->{cc_limit_total})) {
87 1         3 while (@$conns > $total) {
88 0         0 $self->dropping(shift(@$conns), "Total capacity exceeded");
89             }
90             }
91             }
92              
93              
94             sub dropping {
95 0     0 1 0 my($self, $c, $reason) = @_;
96 0 0       0 print "DROPPING @$c [$reason]\n" if $DEBUG;
97             }
98              
99              
100             sub drop {
101 0     0 1 0 my($self, $checker, $reason) = @_;
102 0 0       0 if (ref($checker) ne "CODE") {
103             # make it so
104 0 0       0 if (!defined $checker) {
    0          
105 0     0   0 $checker = sub { 1 }; # drop all of them
  0         0  
106             }
107             elsif (_looks_like_number($checker)) {
108 0         0 my $age_limit = $checker;
109 0         0 my $time_limit = time - $age_limit;
110 0   0     0 $reason ||= "older than $age_limit";
111 0     0   0 $checker = sub { $_[3] < $time_limit };
  0         0  
112             }
113             else {
114 0         0 my $type = $checker;
115 0   0     0 $reason ||= "drop $type";
116 0     0   0 $checker = sub { $_[1] eq $type }; # match on type
  0         0  
117             }
118             }
119 0   0     0 $reason ||= "drop";
120              
121 0         0 local $SIG{__DIE__}; # don't interfere with eval below
122 0         0 local $@;
123 0         0 my @c;
124 0         0 for (@{$self->{cc_conns}}) {
  0         0  
125 0         0 my $drop;
126 0         0 eval {
127 0 0       0 if (&$checker(@$_)) {
128 0         0 $self->dropping($_, $reason);
129 0         0 $drop++;
130             }
131             };
132 0 0       0 push(@c, $_) unless $drop;
133             }
134 0         0 @{$self->{cc_conns}} = @c;
  0         0  
135             }
136              
137              
138             sub prune {
139 0     0 1 0 my $self = shift;
140 0     0   0 $self->drop(sub { !shift->ping }, "ping");
  0         0  
141             }
142              
143              
144             sub get_types {
145 1     1 1 2 my $self = shift;
146 1         1 my %t;
147 1         1 $t{$_->[1]}++ for @{$self->{cc_conns}};
  1         3  
148 1         3 return keys %t;
149             }
150              
151              
152             sub get_connections {
153 0     0 1   my($self, $type) = @_;
154 0           my @c;
155 0           for (@{$self->{cc_conns}}) {
  0            
156 0 0 0       push(@c, $_->[0]) if !$type || ($type && $type eq $_->[1]);
      0        
157             }
158 0           @c;
159             }
160              
161              
162             sub _looks_like_number {
163 0     0     $_[0] =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/;
164             }
165              
166             1;
167              
168              
169             __END__