line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Mirror::JSON; |
2
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
25175
|
use 5.005; |
|
2
|
|
|
|
|
7
|
|
|
2
|
|
|
|
|
74
|
|
4
|
2
|
|
|
2
|
|
10
|
use strict; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
60
|
|
5
|
2
|
|
|
2
|
|
9
|
use Carp qw{ croak }; |
|
2
|
|
|
|
|
12
|
|
|
2
|
|
|
|
|
118
|
|
6
|
2
|
|
|
2
|
|
1769
|
use Params::Util qw{ _STRING _POSINT _ARRAY0 _INSTANCE }; |
|
2
|
|
|
|
|
8953
|
|
|
2
|
|
|
|
|
159
|
|
7
|
2
|
|
|
2
|
|
1147
|
use JSON (); |
|
2
|
|
|
|
|
13193
|
|
|
2
|
|
|
|
|
42
|
|
8
|
2
|
|
|
2
|
|
8104
|
use URI (); |
|
2
|
|
|
|
|
12582
|
|
|
2
|
|
|
|
|
45
|
|
9
|
2
|
|
|
2
|
|
2339
|
use Time::HiRes (); |
|
2
|
|
|
|
|
4707
|
|
|
2
|
|
|
|
|
63
|
|
10
|
2
|
|
|
2
|
|
3086
|
use Time::Local (); |
|
2
|
|
|
|
|
4232
|
|
|
2
|
|
|
|
|
47
|
|
11
|
2
|
|
|
2
|
|
2266
|
use LWP::Simple (); |
|
2
|
|
|
|
|
171531
|
|
|
2
|
|
|
|
|
67
|
|
12
|
2
|
|
|
2
|
|
1447
|
use Mirror::JSON::URI (); |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
47
|
|
13
|
|
|
|
|
|
|
|
14
|
2
|
|
|
2
|
|
14
|
use constant ONE_DAY => 86700; # 1 day plus 5 minutes fudge factor |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
137
|
|
15
|
2
|
|
|
2
|
|
10
|
use constant TWO_DAYS => 172800; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
85
|
|
16
|
2
|
|
|
2
|
|
9
|
use constant THIRTY_DAYS => 2592000; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
83
|
|
17
|
|
|
|
|
|
|
|
18
|
2
|
|
|
2
|
|
8
|
use vars qw{$VERSION}; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
76
|
|
19
|
|
|
|
|
|
|
BEGIN { |
20
|
2
|
|
|
2
|
|
2921
|
$VERSION = '0.01'; |
21
|
|
|
|
|
|
|
} |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
##################################################################### |
28
|
|
|
|
|
|
|
# Wrapper for the JSON::Tiny methods |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
sub new { |
31
|
1
|
|
|
1
|
0
|
3
|
my $class = shift; |
32
|
1
|
|
|
|
|
7
|
my $self = bless { @_ }, $class; |
33
|
1
|
50
|
|
|
|
21
|
if ( _STRING($self->{uri}) ) { |
34
|
1
|
|
|
|
|
14
|
$self->{uri} = URI->new($self->{uri}); |
35
|
|
|
|
|
|
|
} |
36
|
1
|
50
|
33
|
|
|
10376
|
if ( _STRING($self->{timestamp}) and ! _POSINT($self->{timestamp}) ) { |
37
|
1
|
50
|
|
|
|
18
|
unless ( $self->{timestamp} =~ /^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z$/ ) { |
38
|
0
|
|
|
|
|
0
|
return undef; |
39
|
|
|
|
|
|
|
} |
40
|
1
|
|
|
|
|
12
|
$self->{timestamp} = Time::Local::timegm( $6, $5, $4, $3, $2 - 1, $1 ); |
41
|
|
|
|
|
|
|
} |
42
|
1
|
50
|
|
|
|
48
|
unless ( _ARRAY0($self->{mirrors}) ) { |
43
|
0
|
|
|
|
|
0
|
return undef; |
44
|
|
|
|
|
|
|
} |
45
|
1
|
|
|
|
|
2
|
foreach ( @{$self->{mirrors}} ) { |
|
1
|
|
|
|
|
3
|
|
46
|
1
|
50
|
|
|
|
5
|
if ( _STRING($_->{uri}) ) { |
47
|
1
|
|
|
|
|
6
|
$_->{uri} = URI->new($_->{uri}); |
48
|
1
|
50
|
|
|
|
67
|
$_ = Mirror::JSON::URI->new( %$_ ) or return undef; |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
} |
51
|
1
|
|
|
|
|
13
|
return $self; |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
sub read { |
55
|
1
|
|
|
1
|
0
|
583
|
my $class = shift; |
56
|
1
|
|
|
|
|
5
|
my $file = shift; |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# Read in the file |
59
|
1
|
|
|
|
|
7
|
local *MIRROR; |
60
|
1
|
|
|
|
|
12
|
local $/ = undef; |
61
|
1
|
50
|
|
|
|
82
|
open( MIRROR, $file ) or croak("open: $!"); |
62
|
1
|
|
|
|
|
38
|
my $buffer = ; |
63
|
1
|
50
|
|
|
|
20
|
close( MIRROR ) or croak("close: $!"); |
64
|
|
|
|
|
|
|
|
65
|
1
|
|
|
|
|
9
|
$class->read_string( $buffer ); |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
sub read_string { |
69
|
1
|
|
|
1
|
0
|
2
|
my $class = shift; |
70
|
1
|
|
|
|
|
49
|
my $json = JSON->new->decode( shift ); |
71
|
1
|
|
|
|
|
20
|
$class->new( %$json ); |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub write { |
75
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
76
|
0
|
0
|
|
|
|
0
|
my $file = shift or return croak('No file name provided'); |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# Write it to the file |
79
|
0
|
0
|
|
|
|
0
|
open( CFG, '>' . $file ) or croak("Failed to open file '$file' for writing: $!"); |
80
|
0
|
|
|
|
|
0
|
print CFG $self->write_string; |
81
|
0
|
|
|
|
|
0
|
close CFG; |
82
|
|
|
|
|
|
|
|
83
|
0
|
|
|
|
|
0
|
return 1; |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
sub write_string { |
87
|
0
|
|
|
0
|
0
|
0
|
JSON->new->pretty->encode( $_[0]->as_scalar ); |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub as_scalar { |
91
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
92
|
0
|
|
|
|
|
0
|
my $hash = { %$self }; |
93
|
0
|
0
|
|
|
|
0
|
if ( defined $hash->{source} ) { |
94
|
0
|
|
|
|
|
0
|
$hash->{source} = "$hash->{source}"; |
95
|
|
|
|
|
|
|
} |
96
|
0
|
|
|
|
|
0
|
$hash; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
##################################################################### |
104
|
|
|
|
|
|
|
# Mirror::JSON Methods |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
sub name { |
107
|
1
|
|
|
1
|
0
|
755
|
$_[0]->{name}; |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub uri { |
111
|
2
|
|
|
2
|
0
|
14
|
$_[0]->{uri}; |
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
sub timestamp { |
115
|
1
|
|
|
1
|
0
|
6
|
$_[0]->{timestamp}; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
sub age { |
119
|
1
|
50
|
|
1
|
0
|
24
|
$_[0]->{age} or time - $_[0]->{timestamp}; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub benchmark { |
123
|
0
|
|
|
0
|
0
|
0
|
$_[0]->{benchmark}; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
sub mirrors { |
127
|
3
|
|
|
3
|
0
|
10
|
@{ $_[0]->{mirrors} }; |
|
3
|
|
|
|
|
19
|
|
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
##################################################################### |
135
|
|
|
|
|
|
|
# Main Methods |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
sub check_mirrors { |
138
|
2
|
|
|
2
|
0
|
7
|
my $self = shift; |
139
|
2
|
|
|
|
|
48
|
foreach my $mirror ( $self->mirrors ) { |
140
|
2
|
100
|
|
|
|
13
|
next if defined $mirror->{live}; |
141
|
1
|
|
|
|
|
9
|
$mirror->get; |
142
|
|
|
|
|
|
|
} |
143
|
2
|
|
|
|
|
9
|
return 1; |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
# Does the mirror with the newest timestamp newer than ours |
147
|
|
|
|
|
|
|
# have a different master? If so, update our master server. |
148
|
|
|
|
|
|
|
# This lets us survive major reorgansations, as long as some |
149
|
|
|
|
|
|
|
# of the existing mirrors are retained. |
150
|
|
|
|
|
|
|
sub check_master { |
151
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# Make sure we have checked the mirrors |
154
|
0
|
|
|
|
|
0
|
$self->check_mirrors; |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
# Anti-hijacking measure: Only do this if our current |
157
|
|
|
|
|
|
|
# age is more than 30 days. We can almost certainly |
158
|
|
|
|
|
|
|
# handle a 1 month changeover period, otherwise things |
159
|
|
|
|
|
|
|
# will only be bad for a month. |
160
|
0
|
0
|
|
|
|
0
|
if ( $self->age < THIRTY_DAYS ) { |
161
|
0
|
|
|
|
|
0
|
return 1; |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
# Find all the servers updated in the last 2 days. |
165
|
|
|
|
|
|
|
# All of them except 1 must agree (prevent hijacking, |
166
|
|
|
|
|
|
|
# and handle accidents or anti-update attack from older server) |
167
|
0
|
|
|
|
|
0
|
my %uri = (); |
168
|
0
|
0
|
|
|
|
0
|
map { $uri{$_->uri}++ } grep { $_->age >= 0 and $_->age < TWO_DAYS } $self->mirrors; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
169
|
0
|
|
|
|
|
0
|
my @uris = sort { $uri{$b} <=> $uri{$a} } keys %uri; |
|
0
|
|
|
|
|
0
|
|
170
|
0
|
0
|
0
|
|
|
0
|
unless ( scalar(@uris) <= 2 and $uris[0] and $uris[0] >= (scalar($self->mirrors) - 1) ) { |
|
|
|
0
|
|
|
|
|
171
|
|
|
|
|
|
|
# Data is weird or currupt |
172
|
0
|
|
|
|
|
0
|
return 1; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# Master has moved. |
176
|
|
|
|
|
|
|
# Pull the new master server mirror.json |
177
|
0
|
0
|
|
|
|
0
|
my $new_uri = Mirror::JSON::URI->new( |
178
|
|
|
|
|
|
|
uri => URI->new( $uris[0] ), |
179
|
|
|
|
|
|
|
) or return 1; |
180
|
0
|
0
|
|
|
|
0
|
$new_uri->get or return 1; |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
# To avoid pulling a whole bunch of mirror.json files again |
183
|
|
|
|
|
|
|
# copy any mirrors from our set to the new |
184
|
0
|
0
|
|
|
|
0
|
my $new = $new_uri->json or return 1; |
185
|
0
|
|
|
|
|
0
|
my %old = map { $_->uri => $_ } $self->mirrors; |
|
0
|
|
|
|
|
0
|
|
186
|
0
|
|
|
|
|
0
|
foreach ( @{ $new->{mirrors} } ) { |
|
0
|
|
|
|
|
0
|
|
187
|
0
|
0
|
|
|
|
0
|
if ( $old{$_->uri} ) { |
188
|
0
|
|
|
|
|
0
|
$_ = $old{$_->uri}; |
189
|
|
|
|
|
|
|
} else { |
190
|
0
|
|
|
|
|
0
|
$_->get; |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# Now overwrite ourself with the new one |
195
|
0
|
|
|
|
|
0
|
%$self = %$new; |
196
|
|
|
|
|
|
|
|
197
|
0
|
|
|
|
|
0
|
return 1; |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
# Select the "best" mirrors |
201
|
|
|
|
|
|
|
sub select_mirrors { |
202
|
1
|
|
|
1
|
0
|
951
|
my $self = shift; |
203
|
1
|
|
50
|
|
|
50
|
my $wanted = _POSINT(shift) || 3; |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# Check the mirrors |
206
|
1
|
|
|
|
|
18
|
$self->check_mirrors; |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# Start with the list of all live mirrors, and create |
209
|
|
|
|
|
|
|
# some interesting subsets. |
210
|
0
|
|
|
|
|
0
|
my @live = sort { $a->lag <=> $b->lag } |
|
1
|
|
|
|
|
7
|
|
211
|
1
|
|
|
|
|
4
|
grep { $_->live } $self->mirrors; |
212
|
1
|
|
|
|
|
5
|
my @current = grep { $_->json->age < ONE_DAY } @live; |
|
0
|
|
|
|
|
0
|
|
213
|
1
|
|
|
|
|
3
|
my @ideal = grep { $_->lag < 2 } @current; |
|
0
|
|
|
|
|
0
|
|
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# If there are enough fast and up-to-date mirrors |
216
|
|
|
|
|
|
|
# (which should be common for many people) return them. |
217
|
1
|
50
|
|
|
|
6
|
if ( @ideal >= $wanted ) { |
218
|
0
|
|
|
|
|
0
|
return map { $_->uri } @ideal[0 .. $wanted]; |
|
0
|
|
|
|
|
0
|
|
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# If there are enough up-to-date mirrors |
222
|
|
|
|
|
|
|
# (which should be common) return them. |
223
|
1
|
50
|
|
|
|
5
|
if ( @current >= $wanted ) { |
224
|
0
|
|
|
|
|
0
|
return map { $_->uri } @current[0 .. $wanted]; |
|
0
|
|
|
|
|
0
|
|
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
# Are there ANY that are up to date |
228
|
1
|
50
|
|
|
|
10
|
if ( @current ) { |
229
|
0
|
|
|
|
|
0
|
return map { $_->uri } @current; |
|
0
|
|
|
|
|
0
|
|
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
# Something is weird, just use the master site |
233
|
1
|
|
|
|
|
6
|
return ( $self->uri ); |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
1; |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
__END__ |