line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
1
|
|
|
1
|
|
28822
|
use 5.008007; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
87
|
|
2
|
|
|
|
|
|
|
package Test::ModuleVersion; |
3
|
|
|
|
|
|
|
our $VERSION = '0.17'; |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package |
6
|
|
|
|
|
|
|
Test::ModuleVersion::Object::Simple; |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = '3.0626'; |
9
|
|
|
|
|
|
|
|
10
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
31
|
|
11
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
43
|
|
12
|
1
|
|
|
1
|
|
6
|
no warnings 'redefine'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
39
|
|
13
|
|
|
|
|
|
|
|
14
|
1
|
|
|
1
|
|
5
|
use Carp (); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
73
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
sub import { |
17
|
1
|
|
|
1
|
|
10
|
my ($class, @methods) = @_; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
# Caller |
20
|
1
|
|
|
|
|
3
|
my $caller = caller; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# Base |
23
|
1
|
50
|
50
|
|
|
10
|
if ((my $flag = $methods[0] || '') eq '-base') { |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
# Can haz? |
26
|
1
|
|
|
1
|
|
5
|
no strict 'refs'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
30
|
|
27
|
1
|
|
|
1
|
|
4
|
no warnings 'redefine'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
319
|
|
28
|
0
|
|
|
0
|
|
0
|
*{"${caller}::has"} = sub { attr($caller, @_) }; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# Inheritance |
31
|
0
|
0
|
|
|
|
0
|
if (my $module = $methods[1]) { |
32
|
0
|
|
|
|
|
0
|
$module =~ s/::|'/\//g; |
33
|
0
|
0
|
|
|
|
0
|
require "$module.pm" unless $module->can('new'); |
34
|
0
|
|
|
|
|
0
|
push @{"${caller}::ISA"}, $module; |
|
0
|
|
|
|
|
0
|
|
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
else { |
37
|
0
|
|
|
|
|
0
|
push @{"${caller}::ISA"}, $class; |
|
0
|
|
|
|
|
0
|
|
38
|
|
|
|
|
|
|
} |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# strict! |
41
|
0
|
|
|
|
|
0
|
strict->import; |
42
|
0
|
|
|
|
|
0
|
warnings->import; |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# Modern! |
45
|
0
|
0
|
|
|
|
0
|
feature->import(':5.10') if $] >= 5.010; |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
# Method export |
48
|
|
|
|
|
|
|
else { |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
# Exports |
51
|
1
|
|
|
|
|
2
|
my %exports = map { $_ => 1 } qw/new attr class_attr dual_attr/; |
|
4
|
|
|
|
|
9
|
|
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# Export methods |
54
|
1
|
|
|
|
|
1955
|
foreach my $method (@methods) { |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
# Can be Exported? |
57
|
0
|
0
|
|
|
|
0
|
Carp::croak("Cannot export '$method'.") |
58
|
|
|
|
|
|
|
unless $exports{$method}; |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# Export |
61
|
1
|
|
|
1
|
|
6
|
no strict 'refs'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
567
|
|
62
|
0
|
|
|
|
|
0
|
*{"${caller}::$method"} = \&{"$method"}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub new { |
68
|
2
|
|
|
2
|
|
1178
|
my $class = shift; |
69
|
2
|
0
|
33
|
|
|
23
|
bless @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {}, ref $class || $class; |
|
0
|
50
|
|
|
|
0
|
|
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
sub attr { |
73
|
10
|
|
|
10
|
|
20
|
my ($self, @args) = @_; |
74
|
|
|
|
|
|
|
|
75
|
10
|
|
33
|
|
|
33
|
my $class = ref $self || $self; |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# Fix argument |
78
|
10
|
100
|
|
|
|
22
|
unshift @args, (shift @args, undef) if @args % 2; |
79
|
|
|
|
|
|
|
|
80
|
10
|
|
|
|
|
47
|
for (my $i = 0; $i < @args; $i += 2) { |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# Attribute name |
83
|
10
|
|
|
|
|
12
|
my $attrs = $args[$i]; |
84
|
10
|
50
|
|
|
|
25
|
$attrs = [$attrs] unless ref $attrs eq 'ARRAY'; |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# Default |
87
|
10
|
|
|
|
|
17
|
my $default = $args[$i + 1]; |
88
|
|
|
|
|
|
|
|
89
|
10
|
|
|
|
|
16
|
foreach my $attr (@$attrs) { |
90
|
|
|
|
|
|
|
|
91
|
10
|
50
|
66
|
|
|
35
|
Carp::croak("Default value of attr must be string or number " . |
92
|
|
|
|
|
|
|
"or code reference (${class}::$attr)") |
93
|
|
|
|
|
|
|
unless !ref $default || ref $default eq 'CODE'; |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# Code |
96
|
10
|
|
|
|
|
9
|
my $code; |
97
|
10
|
100
|
100
|
|
|
40
|
if (defined $default && ref $default) { |
|
|
100
|
66
|
|
|
|
|
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
$code = sub { |
102
|
23
|
100
|
|
23
|
|
92
|
if(@_ == 1) { |
103
|
20
|
100
|
|
|
|
73
|
return $_[0]->{$attr} = $default->($_[0]) unless exists $_[0]->{$attr}; |
104
|
15
|
|
|
|
|
71
|
return $_[0]->{$attr}; |
105
|
|
|
|
|
|
|
} |
106
|
3
|
|
|
|
|
14
|
$_[0]->{$attr} = $_[1]; |
107
|
3
|
|
|
|
|
5
|
$_[0]; |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
7
|
|
|
|
|
22
|
} |
111
|
|
|
|
|
|
|
elsif (defined $default && ! ref $default) { |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
$code = sub { |
116
|
4
|
50
|
|
4
|
|
15
|
if(@_ == 1) { |
117
|
4
|
100
|
|
|
|
18
|
return $_[0]->{$attr} = $default unless exists $_[0]->{$attr}; |
118
|
2
|
|
|
|
|
8
|
return $_[0]->{$attr}; |
119
|
|
|
|
|
|
|
} |
120
|
0
|
|
|
|
|
0
|
$_[0]->{$attr} = $_[1]; |
121
|
0
|
|
|
|
|
0
|
$_[0]; |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
|
126
|
2
|
|
|
|
|
6
|
} |
127
|
|
|
|
|
|
|
else { |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
$code = sub { |
132
|
0
|
0
|
|
0
|
|
0
|
return $_[0]->{$attr} if @_ == 1; |
133
|
0
|
|
|
|
|
0
|
$_[0]->{$attr} = $_[1]; |
134
|
0
|
|
|
|
|
0
|
$_[0]; |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
|
139
|
1
|
|
|
|
|
4
|
} |
140
|
|
|
|
|
|
|
|
141
|
1
|
|
|
1
|
|
6
|
no strict 'refs'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
88
|
|
142
|
10
|
|
|
|
|
10
|
*{"${class}::$attr"} = $code; |
|
10
|
|
|
|
|
83
|
|
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
package |
148
|
|
|
|
|
|
|
Test::ModuleVersion::HTTP::Tiny; |
149
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
32
|
|
150
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
79
|
|
151
|
|
|
|
|
|
|
# ABSTRACT: A small, simple, correct HTTP/1.1 client |
152
|
|
|
|
|
|
|
our $VERSION = '0.016'; # VERSION |
153
|
|
|
|
|
|
|
|
154
|
1
|
|
|
1
|
|
22
|
use Carp (); |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
42
|
|
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
my @attributes; |
158
|
|
|
|
|
|
|
BEGIN { |
159
|
1
|
|
|
1
|
|
15
|
@attributes = qw(agent default_headers max_redirect max_size proxy timeout); |
160
|
1
|
|
|
1
|
|
5
|
no strict 'refs'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
78
|
|
161
|
1
|
|
|
|
|
4
|
for my $accessor ( @attributes ) { |
162
|
6
|
|
|
|
|
283
|
*{$accessor} = sub { |
163
|
0
|
0
|
|
0
|
|
0
|
@_ > 1 ? $_[0]->{$accessor} = $_[1] : $_[0]->{$accessor}; |
164
|
6
|
|
|
|
|
16
|
}; |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
sub new { |
169
|
0
|
|
|
0
|
|
0
|
my($class, %args) = @_; |
170
|
0
|
|
|
|
|
0
|
(my $agent = $class) =~ s{::}{-}g; |
171
|
0
|
|
0
|
|
|
0
|
my $self = { |
172
|
|
|
|
|
|
|
agent => $agent . "/" . ($class->VERSION || 0), |
173
|
|
|
|
|
|
|
max_redirect => 5, |
174
|
|
|
|
|
|
|
timeout => 60, |
175
|
|
|
|
|
|
|
}; |
176
|
0
|
|
|
|
|
0
|
for my $key ( @attributes ) { |
177
|
0
|
0
|
|
|
|
0
|
$self->{$key} = $args{$key} if exists $args{$key} |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
# Never override proxy argument as this breaks backwards compat. |
181
|
0
|
0
|
0
|
|
|
0
|
if (!exists $self->{proxy} && (my $http_proxy = $ENV{http_proxy})) { |
182
|
0
|
0
|
|
|
|
0
|
if ($http_proxy =~ m{\Ahttp://[^/?#:@]+:\d+/?\z}) { |
183
|
0
|
|
|
|
|
0
|
$self->{proxy} = $http_proxy; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
else { |
186
|
0
|
|
|
|
|
0
|
Carp::croak(qq{Environment 'http_proxy' must be in format http://:/\n}); |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
0
|
|
|
|
|
0
|
return bless $self, $class; |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
for my $sub_name ( qw/get head put post delete/ ) { |
195
|
|
|
|
|
|
|
my $req_method = uc $sub_name; |
196
|
1
|
|
|
1
|
|
5
|
no strict 'refs'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
2875
|
|
197
|
0
|
0
|
0
|
0
|
|
0
|
eval <<"HERE"; |
|
0
|
0
|
0
|
0
|
|
0
|
|
|
0
|
0
|
0
|
0
|
|
0
|
|
|
0
|
0
|
0
|
0
|
|
0
|
|
|
0
|
0
|
0
|
0
|
|
0
|
|
|
0
|
|
0
|
|
|
0
|
|
|
0
|
|
0
|
|
|
0
|
|
|
0
|
|
0
|
|
|
0
|
|
|
0
|
|
0
|
|
|
0
|
|
|
0
|
|
0
|
|
|
0
|
|
|
0
|
|
0
|
|
|
0
|
|
|
0
|
|
0
|
|
|
0
|
|
|
0
|
|
0
|
|
|
0
|
|
|
0
|
|
0
|
|
|
0
|
|
|
0
|
|
0
|
|
|
0
|
|
198
|
|
|
|
|
|
|
sub $sub_name { |
199
|
|
|
|
|
|
|
my (\$self, \$url, \$args) = \@_; |
200
|
|
|
|
|
|
|
\@_ == 2 || (\@_ == 3 && ref \$args eq 'HASH') |
201
|
|
|
|
|
|
|
or Carp::croak(q/Usage: \$http->$sub_name(URL, [HASHREF])/ . "\n"); |
202
|
|
|
|
|
|
|
return \$self->request('$req_method', \$url, \$args || {}); |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
HERE |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub post_form { |
209
|
0
|
|
|
0
|
|
0
|
my ($self, $url, $data, $args) = @_; |
210
|
0
|
0
|
0
|
|
|
0
|
(@_ == 3 || @_ == 4 && ref $args eq 'HASH') |
|
|
|
0
|
|
|
|
|
211
|
|
|
|
|
|
|
or Carp::croak(q/Usage: $http->post_form(URL, DATAREF, [HASHREF])/ . "\n"); |
212
|
|
|
|
|
|
|
|
213
|
0
|
|
|
|
|
0
|
my $headers = {}; |
214
|
0
|
0
|
|
|
|
0
|
while ( my ($key, $value) = each %{$args->{headers} || {}} ) { |
|
0
|
|
|
|
|
0
|
|
215
|
0
|
|
|
|
|
0
|
$headers->{lc $key} = $value; |
216
|
|
|
|
|
|
|
} |
217
|
0
|
|
|
|
|
0
|
delete $args->{headers}; |
218
|
|
|
|
|
|
|
|
219
|
0
|
|
|
|
|
0
|
return $self->request('POST', $url, { |
220
|
|
|
|
|
|
|
%$args, |
221
|
|
|
|
|
|
|
content => $self->www_form_urlencode($data), |
222
|
|
|
|
|
|
|
headers => { |
223
|
|
|
|
|
|
|
%$headers, |
224
|
|
|
|
|
|
|
'content-type' => 'application/x-www-form-urlencoded' |
225
|
|
|
|
|
|
|
}, |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
); |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
sub mirror { |
232
|
0
|
|
|
0
|
|
0
|
my ($self, $url, $file, $args) = @_; |
233
|
0
|
0
|
0
|
|
|
0
|
@_ == 3 || (@_ == 4 && ref $args eq 'HASH') |
|
|
|
0
|
|
|
|
|
234
|
|
|
|
|
|
|
or Carp::croak(q/Usage: $http->mirror(URL, FILE, [HASHREF])/ . "\n"); |
235
|
0
|
0
|
0
|
|
|
0
|
if ( -e $file and my $mtime = (stat($file))[9] ) { |
236
|
0
|
|
0
|
|
|
0
|
$args->{headers}{'if-modified-since'} ||= $self->_http_date($mtime); |
237
|
|
|
|
|
|
|
} |
238
|
0
|
|
|
|
|
0
|
my $tempfile = $file . int(rand(2**31)); |
239
|
0
|
0
|
|
|
|
0
|
open my $fh, ">", $tempfile |
240
|
|
|
|
|
|
|
or Carp::croak(qq/Error: Could not open temporary file $tempfile for downloading: $!\n/); |
241
|
0
|
|
|
|
|
0
|
binmode $fh; |
242
|
0
|
|
|
0
|
|
0
|
$args->{data_callback} = sub { print {$fh} $_[0] }; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
243
|
0
|
|
|
|
|
0
|
my $response = $self->request('GET', $url, $args); |
244
|
0
|
0
|
|
|
|
0
|
close $fh |
245
|
|
|
|
|
|
|
or Carp::croak(qq/Error: Could not close temporary file $tempfile: $!\n/); |
246
|
0
|
0
|
|
|
|
0
|
if ( $response->{success} ) { |
247
|
0
|
0
|
|
|
|
0
|
rename $tempfile, $file |
248
|
|
|
|
|
|
|
or Carp::croak(qq/Error replacing $file with $tempfile: $!\n/); |
249
|
0
|
|
|
|
|
0
|
my $lm = $response->{headers}{'last-modified'}; |
250
|
0
|
0
|
0
|
|
|
0
|
if ( $lm and my $mtime = $self->_parse_http_date($lm) ) { |
251
|
0
|
|
|
|
|
0
|
utime $mtime, $mtime, $file; |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
} |
254
|
0
|
|
0
|
|
|
0
|
$response->{success} ||= $response->{status} eq '304'; |
255
|
0
|
|
|
|
|
0
|
unlink $tempfile; |
256
|
0
|
|
|
|
|
0
|
return $response; |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
my %idempotent = map { $_ => 1 } qw/GET HEAD PUT DELETE OPTIONS TRACE/; |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
sub request { |
263
|
0
|
|
|
0
|
|
0
|
my ($self, $method, $url, $args) = @_; |
264
|
0
|
0
|
0
|
|
|
0
|
@_ == 3 || (@_ == 4 && ref $args eq 'HASH') |
|
|
|
0
|
|
|
|
|
265
|
|
|
|
|
|
|
or Carp::croak(q/Usage: $http->request(METHOD, URL, [HASHREF])/ . "\n"); |
266
|
0
|
|
0
|
|
|
0
|
$args ||= {}; # we keep some state in this during _request |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
# RFC 2616 Section 8.1.4 mandates a single retry on broken socket |
269
|
0
|
|
|
|
|
0
|
my $response; |
270
|
0
|
|
|
|
|
0
|
for ( 0 .. 1 ) { |
271
|
0
|
|
|
|
|
0
|
$response = eval { $self->_request($method, $url, $args) }; |
|
0
|
|
|
|
|
0
|
|
272
|
0
|
0
|
0
|
|
|
0
|
last unless $@ && $idempotent{$method} |
|
|
|
0
|
|
|
|
|
273
|
|
|
|
|
|
|
&& $@ =~ m{^(?:Socket closed|Unexpected end)}; |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
|
276
|
0
|
0
|
|
|
|
0
|
if (my $e = "$@") { |
277
|
0
|
|
|
|
|
0
|
$response = { |
278
|
|
|
|
|
|
|
success => q{}, |
279
|
|
|
|
|
|
|
status => 599, |
280
|
|
|
|
|
|
|
reason => 'Internal Exception', |
281
|
|
|
|
|
|
|
content => $e, |
282
|
|
|
|
|
|
|
headers => { |
283
|
|
|
|
|
|
|
'content-type' => 'text/plain', |
284
|
|
|
|
|
|
|
'content-length' => length $e, |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
}; |
287
|
|
|
|
|
|
|
} |
288
|
0
|
|
|
|
|
0
|
return $response; |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
sub www_form_urlencode { |
293
|
0
|
|
|
0
|
|
0
|
my ($self, $data) = @_; |
294
|
0
|
0
|
0
|
|
|
0
|
(@_ == 2 && ref $data) |
295
|
|
|
|
|
|
|
or Carp::croak(q/Usage: $http->www_form_urlencode(DATAREF)/ . "\n"); |
296
|
0
|
0
|
0
|
|
|
0
|
(ref $data eq 'HASH' || ref $data eq 'ARRAY') |
297
|
|
|
|
|
|
|
or Carp::croak("form data must be a hash or array reference"); |
298
|
|
|
|
|
|
|
|
299
|
0
|
0
|
|
|
|
0
|
my @params = ref $data eq 'HASH' ? %$data : @$data; |
300
|
0
|
0
|
|
|
|
0
|
@params % 2 == 0 |
301
|
|
|
|
|
|
|
or Carp::croak("form data reference must have an even number of terms\n"); |
302
|
|
|
|
|
|
|
|
303
|
0
|
|
|
|
|
0
|
my @terms; |
304
|
0
|
|
|
|
|
0
|
while( @params ) { |
305
|
0
|
|
|
|
|
0
|
my ($key, $value) = splice(@params, 0, 2); |
306
|
0
|
0
|
|
|
|
0
|
if ( ref $value eq 'ARRAY' ) { |
307
|
0
|
|
|
|
|
0
|
unshift @params, map { $key => $_ } @$value; |
|
0
|
|
|
|
|
0
|
|
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
else { |
310
|
0
|
|
|
|
|
0
|
push @terms, join("=", map { $self->_uri_escape($_) } $key, $value); |
|
0
|
|
|
|
|
0
|
|
311
|
|
|
|
|
|
|
} |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
|
314
|
0
|
|
|
|
|
0
|
return join("&", sort @terms); |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
318
|
|
|
|
|
|
|
# private methods |
319
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
my %DefaultPort = ( |
322
|
|
|
|
|
|
|
http => 80, |
323
|
|
|
|
|
|
|
https => 443, |
324
|
|
|
|
|
|
|
); |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
sub _request { |
327
|
0
|
|
|
0
|
|
0
|
my ($self, $method, $url, $args) = @_; |
328
|
|
|
|
|
|
|
|
329
|
0
|
|
|
|
|
0
|
my ($scheme, $host, $port, $path_query) = $self->_split_url($url); |
330
|
|
|
|
|
|
|
|
331
|
0
|
0
|
|
|
|
0
|
my $request = { |
332
|
|
|
|
|
|
|
method => $method, |
333
|
|
|
|
|
|
|
scheme => $scheme, |
334
|
|
|
|
|
|
|
host_port => ($port == $DefaultPort{$scheme} ? $host : "$host:$port"), |
335
|
|
|
|
|
|
|
uri => $path_query, |
336
|
|
|
|
|
|
|
headers => {}, |
337
|
|
|
|
|
|
|
}; |
338
|
|
|
|
|
|
|
|
339
|
0
|
|
|
|
|
0
|
my $handle = Test::ModuleVersion::HTTP::Tiny::Handle->new(timeout => $self->{timeout}); |
340
|
|
|
|
|
|
|
|
341
|
0
|
0
|
|
|
|
0
|
if ($self->{proxy}) { |
342
|
0
|
|
|
|
|
0
|
$request->{uri} = "$scheme://$request->{host_port}$path_query"; |
343
|
0
|
0
|
|
|
|
0
|
die(qq/HTTPS via proxy is not supported\n/) |
344
|
|
|
|
|
|
|
if $request->{scheme} eq 'https'; |
345
|
0
|
|
|
|
|
0
|
$handle->connect(($self->_split_url($self->{proxy}))[0..2]); |
346
|
|
|
|
|
|
|
} |
347
|
|
|
|
|
|
|
else { |
348
|
0
|
|
|
|
|
0
|
$handle->connect($scheme, $host, $port); |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
|
351
|
0
|
|
|
|
|
0
|
$self->_prepare_headers_and_cb($request, $args); |
352
|
0
|
|
|
|
|
0
|
$handle->write_request($request); |
353
|
|
|
|
|
|
|
|
354
|
0
|
|
|
|
|
0
|
my $response; |
355
|
0
|
|
|
|
|
0
|
do { $response = $handle->read_response_header } |
|
0
|
|
|
|
|
0
|
|
356
|
|
|
|
|
|
|
until (substr($response->{status},0,1) ne '1'); |
357
|
|
|
|
|
|
|
|
358
|
0
|
0
|
|
|
|
0
|
if ( my @redir_args = $self->_maybe_redirect($request, $response, $args) ) { |
359
|
0
|
|
|
|
|
0
|
$handle->close; |
360
|
0
|
|
|
|
|
0
|
return $self->_request(@redir_args, $args); |
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
|
363
|
0
|
0
|
0
|
|
|
0
|
if ($method eq 'HEAD' || $response->{status} =~ /^[23]04/) { |
364
|
|
|
|
|
|
|
# response has no message body |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
else { |
367
|
0
|
|
|
|
|
0
|
my $data_cb = $self->_prepare_data_cb($response, $args); |
368
|
0
|
|
|
|
|
0
|
$handle->read_body($data_cb, $response); |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
|
371
|
0
|
|
|
|
|
0
|
$handle->close; |
372
|
0
|
|
|
|
|
0
|
$response->{success} = substr($response->{status},0,1) eq '2'; |
373
|
0
|
|
|
|
|
0
|
return $response; |
374
|
|
|
|
|
|
|
} |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
sub _prepare_headers_and_cb { |
377
|
0
|
|
|
0
|
|
0
|
my ($self, $request, $args) = @_; |
378
|
|
|
|
|
|
|
|
379
|
0
|
|
|
|
|
0
|
for ($self->{default_headers}, $args->{headers}) { |
380
|
0
|
0
|
|
|
|
0
|
next unless defined; |
381
|
0
|
|
|
|
|
0
|
while (my ($k, $v) = each %$_) { |
382
|
0
|
|
|
|
|
0
|
$request->{headers}{lc $k} = $v; |
383
|
|
|
|
|
|
|
} |
384
|
|
|
|
|
|
|
} |
385
|
0
|
|
|
|
|
0
|
$request->{headers}{'host'} = $request->{host_port}; |
386
|
0
|
|
|
|
|
0
|
$request->{headers}{'connection'} = "close"; |
387
|
0
|
|
0
|
|
|
0
|
$request->{headers}{'user-agent'} ||= $self->{agent}; |
388
|
|
|
|
|
|
|
|
389
|
0
|
0
|
|
|
|
0
|
if (defined $args->{content}) { |
390
|
0
|
|
0
|
|
|
0
|
$request->{headers}{'content-type'} ||= "application/octet-stream"; |
391
|
0
|
0
|
|
|
|
0
|
if (ref $args->{content} eq 'CODE') { |
392
|
0
|
0
|
0
|
|
|
0
|
$request->{headers}{'transfer-encoding'} = 'chunked' |
393
|
|
|
|
|
|
|
unless $request->{headers}{'content-length'} |
394
|
|
|
|
|
|
|
|| $request->{headers}{'transfer-encoding'}; |
395
|
0
|
|
|
|
|
0
|
$request->{cb} = $args->{content}; |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
else { |
398
|
0
|
|
|
|
|
0
|
my $content = $args->{content}; |
399
|
0
|
0
|
|
|
|
0
|
if ( $] ge '5.008' ) { |
400
|
0
|
0
|
|
|
|
0
|
utf8::downgrade($content, 1) |
401
|
|
|
|
|
|
|
or die(qq/Wide character in request message body\n/); |
402
|
|
|
|
|
|
|
} |
403
|
0
|
0
|
0
|
|
|
0
|
$request->{headers}{'content-length'} = length $content |
404
|
|
|
|
|
|
|
unless $request->{headers}{'content-length'} |
405
|
|
|
|
|
|
|
|| $request->{headers}{'transfer-encoding'}; |
406
|
0
|
|
|
0
|
|
0
|
$request->{cb} = sub { substr $content, 0, length $content, '' }; |
|
0
|
|
|
|
|
0
|
|
407
|
|
|
|
|
|
|
} |
408
|
0
|
0
|
|
|
|
0
|
$request->{trailer_cb} = $args->{trailer_callback} |
409
|
|
|
|
|
|
|
if ref $args->{trailer_callback} eq 'CODE'; |
410
|
|
|
|
|
|
|
} |
411
|
0
|
|
|
|
|
0
|
return; |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
sub _prepare_data_cb { |
415
|
0
|
|
|
0
|
|
0
|
my ($self, $response, $args) = @_; |
416
|
0
|
|
|
|
|
0
|
my $data_cb = $args->{data_callback}; |
417
|
0
|
|
|
|
|
0
|
$response->{content} = ''; |
418
|
|
|
|
|
|
|
|
419
|
0
|
0
|
0
|
|
|
0
|
if (!$data_cb || $response->{status} !~ /^2/) { |
420
|
0
|
0
|
|
|
|
0
|
if (defined $self->{max_size}) { |
421
|
|
|
|
|
|
|
$data_cb = sub { |
422
|
0
|
|
|
0
|
|
0
|
$_[1]->{content} .= $_[0]; |
423
|
0
|
0
|
|
|
|
0
|
die(qq/Size of response body exceeds the maximum allowed of $self->{max_size}\n/) |
424
|
|
|
|
|
|
|
if length $_[1]->{content} > $self->{max_size}; |
425
|
0
|
|
|
|
|
0
|
}; |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
else { |
428
|
0
|
|
|
0
|
|
0
|
$data_cb = sub { $_[1]->{content} .= $_[0] }; |
|
0
|
|
|
|
|
0
|
|
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
} |
431
|
0
|
|
|
|
|
0
|
return $data_cb; |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
sub _maybe_redirect { |
435
|
0
|
|
|
0
|
|
0
|
my ($self, $request, $response, $args) = @_; |
436
|
0
|
|
|
|
|
0
|
my $headers = $response->{headers}; |
437
|
0
|
|
|
|
|
0
|
my ($status, $method) = ($response->{status}, $request->{method}); |
438
|
0
|
0
|
0
|
|
|
0
|
if (($status eq '303' or ($status =~ /^30[127]/ && $method =~ /^GET|HEAD$/)) |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
439
|
|
|
|
|
|
|
and $headers->{location} |
440
|
|
|
|
|
|
|
and ++$args->{redirects} <= $self->{max_redirect} |
441
|
|
|
|
|
|
|
) { |
442
|
0
|
0
|
|
|
|
0
|
my $location = ($headers->{location} =~ /^\//) |
443
|
|
|
|
|
|
|
? "$request->{scheme}://$request->{host_port}$headers->{location}" |
444
|
|
|
|
|
|
|
: $headers->{location} ; |
445
|
0
|
0
|
|
|
|
0
|
return (($status eq '303' ? 'GET' : $method), $location); |
446
|
|
|
|
|
|
|
} |
447
|
0
|
|
|
|
|
0
|
return; |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
sub _split_url { |
451
|
0
|
|
|
0
|
|
0
|
my $url = pop; |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
# URI regex adapted from the URI module |
454
|
0
|
0
|
|
|
|
0
|
my ($scheme, $authority, $path_query) = $url =~ m<\A([^:/?#]+)://([^/?#]*)([^#]*)> |
455
|
|
|
|
|
|
|
or die(qq/Cannot parse URL: '$url'\n/); |
456
|
|
|
|
|
|
|
|
457
|
0
|
|
|
|
|
0
|
$scheme = lc $scheme; |
458
|
0
|
0
|
|
|
|
0
|
$path_query = "/$path_query" unless $path_query =~ m<\A/>; |
459
|
|
|
|
|
|
|
|
460
|
0
|
0
|
|
|
|
0
|
my $host = (length($authority)) ? lc $authority : 'localhost'; |
461
|
0
|
|
|
|
|
0
|
$host =~ s/\A[^@]*@//; # userinfo |
462
|
0
|
|
|
|
|
0
|
my $port = do { |
463
|
0
|
0
|
0
|
|
|
0
|
$host =~ s/:([0-9]*)\z// && length $1 |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
464
|
|
|
|
|
|
|
? $1 |
465
|
|
|
|
|
|
|
: ($scheme eq 'http' ? 80 : $scheme eq 'https' ? 443 : undef); |
466
|
|
|
|
|
|
|
}; |
467
|
|
|
|
|
|
|
|
468
|
0
|
|
|
|
|
0
|
return ($scheme, $host, $port, $path_query); |
469
|
|
|
|
|
|
|
} |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
# Date conversions adapted from HTTP::Date |
472
|
|
|
|
|
|
|
my $DoW = "Sun|Mon|Tue|Wed|Thu|Fri|Sat"; |
473
|
|
|
|
|
|
|
my $MoY = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec"; |
474
|
|
|
|
|
|
|
sub _http_date { |
475
|
0
|
|
|
0
|
|
0
|
my ($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime($_[1]); |
476
|
0
|
|
|
|
|
0
|
return sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT", |
477
|
|
|
|
|
|
|
substr($DoW,$wday*4,3), |
478
|
|
|
|
|
|
|
$mday, substr($MoY,$mon*4,3), $year+1900, |
479
|
|
|
|
|
|
|
$hour, $min, $sec |
480
|
|
|
|
|
|
|
); |
481
|
|
|
|
|
|
|
} |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
sub _parse_http_date { |
484
|
0
|
|
|
0
|
|
0
|
my ($self, $str) = @_; |
485
|
0
|
|
|
|
|
0
|
require Time::Local; |
486
|
0
|
|
|
|
|
0
|
my @tl_parts; |
487
|
0
|
0
|
|
|
|
0
|
if ($str =~ /^[SMTWF][a-z]+, +(\d{1,2}) ($MoY) +(\d\d\d\d) +(\d\d):(\d\d):(\d\d) +GMT$/) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
488
|
0
|
|
|
|
|
0
|
@tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3); |
489
|
|
|
|
|
|
|
} |
490
|
|
|
|
|
|
|
elsif ($str =~ /^[SMTWF][a-z]+, +(\d\d)-($MoY)-(\d{2,4}) +(\d\d):(\d\d):(\d\d) +GMT$/ ) { |
491
|
0
|
|
|
|
|
0
|
@tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3); |
492
|
|
|
|
|
|
|
} |
493
|
|
|
|
|
|
|
elsif ($str =~ /^[SMTWF][a-z]+ +($MoY) +(\d{1,2}) +(\d\d):(\d\d):(\d\d) +(?:[^0-9]+ +)?(\d\d\d\d)$/ ) { |
494
|
0
|
|
|
|
|
0
|
@tl_parts = ($5, $4, $3, $2, (index($MoY,$1)/4), $6); |
495
|
|
|
|
|
|
|
} |
496
|
0
|
|
|
|
|
0
|
return eval { |
497
|
0
|
0
|
|
|
|
0
|
my $t = @tl_parts ? Time::Local::timegm(@tl_parts) : -1; |
498
|
0
|
0
|
|
|
|
0
|
$t < 0 ? undef : $t; |
499
|
|
|
|
|
|
|
}; |
500
|
|
|
|
|
|
|
} |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
# URI escaping adapted from URI::Escape |
503
|
|
|
|
|
|
|
# c.f. http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 |
504
|
|
|
|
|
|
|
# perl 5.6 ready UTF-8 encoding adapted from Test::ModuleVersion::JSON::PP |
505
|
|
|
|
|
|
|
my %escapes = map { chr($_) => sprintf("%%%02X", $_) } 0..255; |
506
|
|
|
|
|
|
|
$escapes{' '}="+"; |
507
|
|
|
|
|
|
|
my $unsafe_char = qr/[^A-Za-z0-9\-\._~]/; |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
sub _uri_escape { |
510
|
0
|
|
|
0
|
|
0
|
my ($self, $str) = @_; |
511
|
0
|
0
|
|
|
|
0
|
if ( $] ge '5.008' ) { |
512
|
0
|
|
|
|
|
0
|
utf8::encode($str); |
513
|
|
|
|
|
|
|
} |
514
|
|
|
|
|
|
|
else { |
515
|
|
|
|
|
|
|
$str = pack("U*", unpack("C*", $str)) # UTF-8 encode a byte string |
516
|
1
|
0
|
|
1
|
|
1316
|
if ( length $str == do { use bytes; length $str } ); |
|
1
|
|
|
|
|
13
|
|
|
1
|
|
|
|
|
5
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
517
|
0
|
|
|
|
|
0
|
$str = pack("C*", unpack("C*", $str)); # clear UTF-8 flag |
518
|
|
|
|
|
|
|
} |
519
|
0
|
|
|
|
|
0
|
$str =~ s/($unsafe_char)/$escapes{$1}/ge; |
|
0
|
|
|
|
|
0
|
|
520
|
0
|
|
|
|
|
0
|
return $str; |
521
|
|
|
|
|
|
|
} |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
package |
524
|
|
|
|
|
|
|
Test::ModuleVersion::HTTP::Tiny::Handle; # hide from PAUSE/indexers |
525
|
1
|
|
|
1
|
|
113
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
35
|
|
526
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
40
|
|
527
|
|
|
|
|
|
|
|
528
|
1
|
|
|
1
|
|
947
|
use Errno qw[EINTR EPIPE]; |
|
1
|
|
|
|
|
1451
|
|
|
1
|
|
|
|
|
139
|
|
529
|
1
|
|
|
1
|
|
1380
|
use IO::Socket qw[SOCK_STREAM]; |
|
1
|
|
|
|
|
27595
|
|
|
1
|
|
|
|
|
5
|
|
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
sub BUFSIZE () { 32768 } |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
my $Printable = sub { |
534
|
|
|
|
|
|
|
local $_ = shift; |
535
|
|
|
|
|
|
|
s/\r/\\r/g; |
536
|
|
|
|
|
|
|
s/\n/\\n/g; |
537
|
|
|
|
|
|
|
s/\t/\\t/g; |
538
|
|
|
|
|
|
|
s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge; |
539
|
|
|
|
|
|
|
$_; |
540
|
|
|
|
|
|
|
}; |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
my $Token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/; |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
sub new { |
545
|
0
|
|
|
0
|
|
0
|
my ($class, %args) = @_; |
546
|
0
|
|
|
|
|
0
|
return bless { |
547
|
|
|
|
|
|
|
rbuf => '', |
548
|
|
|
|
|
|
|
timeout => 60, |
549
|
|
|
|
|
|
|
max_line_size => 16384, |
550
|
|
|
|
|
|
|
max_header_lines => 64, |
551
|
|
|
|
|
|
|
%args |
552
|
|
|
|
|
|
|
}, $class; |
553
|
|
|
|
|
|
|
} |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
my $ssl_verify_args = { |
556
|
|
|
|
|
|
|
check_cn => "when_only", |
557
|
|
|
|
|
|
|
wildcards_in_alt => "anywhere", |
558
|
|
|
|
|
|
|
wildcards_in_cn => "anywhere" |
559
|
|
|
|
|
|
|
}; |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
sub connect { |
562
|
0
|
0
|
|
0
|
|
0
|
@_ == 4 || die(q/Usage: $handle->connect(scheme, host, port)/ . "\n"); |
563
|
0
|
|
|
|
|
0
|
my ($self, $scheme, $host, $port) = @_; |
564
|
|
|
|
|
|
|
|
565
|
0
|
0
|
|
|
|
0
|
if ( $scheme eq 'https' ) { |
|
|
0
|
|
|
|
|
|
566
|
0
|
0
|
|
|
|
0
|
eval "require IO::Socket::SSL" |
567
|
|
|
|
|
|
|
unless exists $INC{'IO/Socket/SSL.pm'}; |
568
|
0
|
0
|
|
|
|
0
|
die(qq/IO::Socket::SSL must be installed for https support\n/) |
569
|
|
|
|
|
|
|
unless $INC{'IO/Socket/SSL.pm'}; |
570
|
|
|
|
|
|
|
} |
571
|
|
|
|
|
|
|
elsif ( $scheme ne 'http' ) { |
572
|
0
|
|
|
|
|
0
|
die(qq/Unsupported URL scheme '$scheme'\n/); |
573
|
|
|
|
|
|
|
} |
574
|
|
|
|
|
|
|
|
575
|
0
|
0
|
|
|
|
0
|
$self->{fh} = 'IO::Socket::INET'->new( |
576
|
|
|
|
|
|
|
PeerHost => $host, |
577
|
|
|
|
|
|
|
PeerPort => $port, |
578
|
|
|
|
|
|
|
Proto => 'tcp', |
579
|
|
|
|
|
|
|
Type => SOCK_STREAM, |
580
|
|
|
|
|
|
|
Timeout => $self->{timeout} |
581
|
|
|
|
|
|
|
) or die(qq/Could not connect to '$host:$port': $@\n/); |
582
|
|
|
|
|
|
|
|
583
|
0
|
0
|
|
|
|
0
|
binmode($self->{fh}) |
584
|
|
|
|
|
|
|
or die(qq/Could not binmode() socket: '$!'\n/); |
585
|
|
|
|
|
|
|
|
586
|
0
|
0
|
|
|
|
0
|
if ( $scheme eq 'https') { |
587
|
0
|
|
|
|
|
0
|
IO::Socket::SSL->start_SSL($self->{fh}); |
588
|
0
|
0
|
|
|
|
0
|
ref($self->{fh}) eq 'IO::Socket::SSL' |
589
|
|
|
|
|
|
|
or die(qq/SSL connection failed for $host\n/); |
590
|
0
|
0
|
|
|
|
0
|
$self->{fh}->verify_hostname( $host, $ssl_verify_args ) |
591
|
|
|
|
|
|
|
or die(qq/SSL certificate not valid for $host\n/); |
592
|
|
|
|
|
|
|
} |
593
|
|
|
|
|
|
|
|
594
|
0
|
|
|
|
|
0
|
$self->{host} = $host; |
595
|
0
|
|
|
|
|
0
|
$self->{port} = $port; |
596
|
|
|
|
|
|
|
|
597
|
0
|
|
|
|
|
0
|
return $self; |
598
|
|
|
|
|
|
|
} |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
sub close { |
601
|
0
|
0
|
|
0
|
|
0
|
@_ == 1 || die(q/Usage: $handle->close()/ . "\n"); |
602
|
0
|
|
|
|
|
0
|
my ($self) = @_; |
603
|
0
|
0
|
|
|
|
0
|
CORE::close($self->{fh}) |
604
|
|
|
|
|
|
|
or die(qq/Could not close socket: '$!'\n/); |
605
|
|
|
|
|
|
|
} |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
sub write { |
608
|
0
|
0
|
|
0
|
|
0
|
@_ == 2 || die(q/Usage: $handle->write(buf)/ . "\n"); |
609
|
0
|
|
|
|
|
0
|
my ($self, $buf) = @_; |
610
|
|
|
|
|
|
|
|
611
|
0
|
0
|
|
|
|
0
|
if ( $] ge '5.008' ) { |
612
|
0
|
0
|
|
|
|
0
|
utf8::downgrade($buf, 1) |
613
|
|
|
|
|
|
|
or die(qq/Wide character in write()\n/); |
614
|
|
|
|
|
|
|
} |
615
|
|
|
|
|
|
|
|
616
|
0
|
|
|
|
|
0
|
my $len = length $buf; |
617
|
0
|
|
|
|
|
0
|
my $off = 0; |
618
|
|
|
|
|
|
|
|
619
|
0
|
|
|
|
|
0
|
local $SIG{PIPE} = 'IGNORE'; |
620
|
|
|
|
|
|
|
|
621
|
0
|
|
|
|
|
0
|
while () { |
622
|
0
|
0
|
|
|
|
0
|
$self->can_write |
623
|
|
|
|
|
|
|
or die(qq/Timed out while waiting for socket to become ready for writing\n/); |
624
|
0
|
|
|
|
|
0
|
my $r = syswrite($self->{fh}, $buf, $len, $off); |
625
|
0
|
0
|
|
|
|
0
|
if (defined $r) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
626
|
0
|
|
|
|
|
0
|
$len -= $r; |
627
|
0
|
|
|
|
|
0
|
$off += $r; |
628
|
0
|
0
|
|
|
|
0
|
last unless $len > 0; |
629
|
|
|
|
|
|
|
} |
630
|
|
|
|
|
|
|
elsif ($! == EPIPE) { |
631
|
0
|
|
|
|
|
0
|
die(qq/Socket closed by remote server: $!\n/); |
632
|
|
|
|
|
|
|
} |
633
|
|
|
|
|
|
|
elsif ($! != EINTR) { |
634
|
0
|
|
|
|
|
0
|
die(qq/Could not write to socket: '$!'\n/); |
635
|
|
|
|
|
|
|
} |
636
|
|
|
|
|
|
|
} |
637
|
0
|
|
|
|
|
0
|
return $off; |
638
|
|
|
|
|
|
|
} |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
sub read { |
641
|
0
|
0
|
0
|
0
|
|
0
|
@_ == 2 || @_ == 3 || die(q/Usage: $handle->read(len [, allow_partial])/ . "\n"); |
642
|
0
|
|
|
|
|
0
|
my ($self, $len, $allow_partial) = @_; |
643
|
|
|
|
|
|
|
|
644
|
0
|
|
|
|
|
0
|
my $buf = ''; |
645
|
0
|
|
|
|
|
0
|
my $got = length $self->{rbuf}; |
646
|
|
|
|
|
|
|
|
647
|
0
|
0
|
|
|
|
0
|
if ($got) { |
648
|
0
|
0
|
|
|
|
0
|
my $take = ($got < $len) ? $got : $len; |
649
|
0
|
|
|
|
|
0
|
$buf = substr($self->{rbuf}, 0, $take, ''); |
650
|
0
|
|
|
|
|
0
|
$len -= $take; |
651
|
|
|
|
|
|
|
} |
652
|
|
|
|
|
|
|
|
653
|
0
|
|
|
|
|
0
|
while ($len > 0) { |
654
|
0
|
0
|
|
|
|
0
|
$self->can_read |
655
|
|
|
|
|
|
|
or die(q/Timed out while waiting for socket to become ready for reading/ . "\n"); |
656
|
0
|
|
|
|
|
0
|
my $r = sysread($self->{fh}, $buf, $len, length $buf); |
657
|
0
|
0
|
|
|
|
0
|
if (defined $r) { |
|
|
0
|
|
|
|
|
|
658
|
0
|
0
|
|
|
|
0
|
last unless $r; |
659
|
0
|
|
|
|
|
0
|
$len -= $r; |
660
|
|
|
|
|
|
|
} |
661
|
|
|
|
|
|
|
elsif ($! != EINTR) { |
662
|
0
|
|
|
|
|
0
|
die(qq/Could not read from socket: '$!'\n/); |
663
|
|
|
|
|
|
|
} |
664
|
|
|
|
|
|
|
} |
665
|
0
|
0
|
0
|
|
|
0
|
if ($len && !$allow_partial) { |
666
|
0
|
|
|
|
|
0
|
die(qq/Unexpected end of stream\n/); |
667
|
|
|
|
|
|
|
} |
668
|
0
|
|
|
|
|
0
|
return $buf; |
669
|
|
|
|
|
|
|
} |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
sub readline { |
672
|
0
|
0
|
|
0
|
|
0
|
@_ == 1 || die(q/Usage: $handle->readline()/ . "\n"); |
673
|
0
|
|
|
|
|
0
|
my ($self) = @_; |
674
|
|
|
|
|
|
|
|
675
|
0
|
|
|
|
|
0
|
while () { |
676
|
0
|
0
|
|
|
|
0
|
if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) { |
677
|
0
|
|
|
|
|
0
|
return $1; |
678
|
|
|
|
|
|
|
} |
679
|
0
|
0
|
|
|
|
0
|
if (length $self->{rbuf} >= $self->{max_line_size}) { |
680
|
0
|
|
|
|
|
0
|
die(qq/Line size exceeds the maximum allowed size of $self->{max_line_size}\n/); |
681
|
|
|
|
|
|
|
} |
682
|
|
|
|
|
|
|
$self->can_read |
683
|
0
|
0
|
|
|
|
0
|
or die(qq/Timed out while waiting for socket to become ready for reading\n/); |
684
|
0
|
|
|
|
|
0
|
my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf}); |
685
|
0
|
0
|
|
|
|
0
|
if (defined $r) { |
|
|
0
|
|
|
|
|
|
686
|
0
|
0
|
|
|
|
0
|
last unless $r; |
687
|
|
|
|
|
|
|
} |
688
|
|
|
|
|
|
|
elsif ($! != EINTR) { |
689
|
0
|
|
|
|
|
0
|
die(qq/Could not read from socket: '$!'\n/); |
690
|
|
|
|
|
|
|
} |
691
|
|
|
|
|
|
|
} |
692
|
0
|
|
|
|
|
0
|
die(qq/Unexpected end of stream while looking for line\n/); |
693
|
|
|
|
|
|
|
} |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
sub read_header_lines { |
696
|
0
|
0
|
0
|
0
|
|
0
|
@_ == 1 || @_ == 2 || die(q/Usage: $handle->read_header_lines([headers])/ . "\n"); |
697
|
0
|
|
|
|
|
0
|
my ($self, $headers) = @_; |
698
|
0
|
|
0
|
|
|
0
|
$headers ||= {}; |
699
|
0
|
|
|
|
|
0
|
my $lines = 0; |
700
|
0
|
|
|
|
|
0
|
my $val; |
701
|
|
|
|
|
|
|
|
702
|
0
|
|
|
|
|
0
|
while () { |
703
|
0
|
|
|
|
|
0
|
my $line = $self->readline; |
704
|
|
|
|
|
|
|
|
705
|
0
|
0
|
|
|
|
0
|
if (++$lines >= $self->{max_header_lines}) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
706
|
0
|
|
|
|
|
0
|
die(qq/Header lines exceeds maximum number allowed of $self->{max_header_lines}\n/); |
707
|
|
|
|
|
|
|
} |
708
|
|
|
|
|
|
|
elsif ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) { |
709
|
0
|
|
|
|
|
0
|
my ($field_name) = lc $1; |
710
|
0
|
0
|
|
|
|
0
|
if (exists $headers->{$field_name}) { |
711
|
0
|
|
|
|
|
0
|
for ($headers->{$field_name}) { |
712
|
0
|
0
|
|
|
|
0
|
$_ = [$_] unless ref $_ eq "ARRAY"; |
713
|
0
|
|
|
|
|
0
|
push @$_, $2; |
714
|
0
|
|
|
|
|
0
|
$val = \$_->[-1]; |
715
|
|
|
|
|
|
|
} |
716
|
|
|
|
|
|
|
} |
717
|
|
|
|
|
|
|
else { |
718
|
0
|
|
|
|
|
0
|
$val = \($headers->{$field_name} = $2); |
719
|
|
|
|
|
|
|
} |
720
|
|
|
|
|
|
|
} |
721
|
|
|
|
|
|
|
elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) { |
722
|
0
|
0
|
|
|
|
0
|
$val |
723
|
|
|
|
|
|
|
or die(qq/Unexpected header continuation line\n/); |
724
|
0
|
0
|
|
|
|
0
|
next unless length $1; |
725
|
0
|
0
|
|
|
|
0
|
$$val .= ' ' if length $$val; |
726
|
0
|
|
|
|
|
0
|
$$val .= $1; |
727
|
|
|
|
|
|
|
} |
728
|
|
|
|
|
|
|
elsif ($line =~ /\A \x0D?\x0A \z/x) { |
729
|
0
|
|
|
|
|
0
|
last; |
730
|
|
|
|
|
|
|
} |
731
|
|
|
|
|
|
|
else { |
732
|
0
|
|
|
|
|
0
|
die(q/Malformed header line: / . $Printable->($line) . "\n"); |
733
|
|
|
|
|
|
|
} |
734
|
|
|
|
|
|
|
} |
735
|
0
|
|
|
|
|
0
|
return $headers; |
736
|
|
|
|
|
|
|
} |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
sub write_request { |
739
|
0
|
0
|
|
0
|
|
0
|
@_ == 2 || die(q/Usage: $handle->write_request(request)/ . "\n"); |
740
|
0
|
|
|
|
|
0
|
my($self, $request) = @_; |
741
|
0
|
|
|
|
|
0
|
$self->write_request_header(@{$request}{qw/method uri headers/}); |
|
0
|
|
|
|
|
0
|
|
742
|
0
|
0
|
|
|
|
0
|
$self->write_body($request) if $request->{cb}; |
743
|
0
|
|
|
|
|
0
|
return; |
744
|
|
|
|
|
|
|
} |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
my %HeaderCase = ( |
747
|
|
|
|
|
|
|
'content-md5' => 'Content-MD5', |
748
|
|
|
|
|
|
|
'etag' => 'ETag', |
749
|
|
|
|
|
|
|
'te' => 'TE', |
750
|
|
|
|
|
|
|
'www-authenticate' => 'WWW-Authenticate', |
751
|
|
|
|
|
|
|
'x-xss-protection' => 'X-XSS-Protection', |
752
|
|
|
|
|
|
|
); |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
sub write_header_lines { |
755
|
0
|
0
|
0
|
0
|
|
0
|
(@_ == 2 && ref $_[1] eq 'HASH') || die(q/Usage: $handle->write_header_lines(headers)/ . "\n"); |
756
|
0
|
|
|
|
|
0
|
my($self, $headers) = @_; |
757
|
|
|
|
|
|
|
|
758
|
0
|
|
|
|
|
0
|
my $buf = ''; |
759
|
0
|
|
|
|
|
0
|
while (my ($k, $v) = each %$headers) { |
760
|
0
|
|
|
|
|
0
|
my $field_name = lc $k; |
761
|
0
|
0
|
|
|
|
0
|
if (exists $HeaderCase{$field_name}) { |
762
|
0
|
|
|
|
|
0
|
$field_name = $HeaderCase{$field_name}; |
763
|
|
|
|
|
|
|
} |
764
|
|
|
|
|
|
|
else { |
765
|
0
|
0
|
|
|
|
0
|
$field_name =~ /\A $Token+ \z/xo |
766
|
|
|
|
|
|
|
or die(q/Invalid HTTP header field name: / . $Printable->($field_name) . "\n"); |
767
|
0
|
|
|
|
|
0
|
$field_name =~ s/\b(\w)/\u$1/g; |
768
|
0
|
|
|
|
|
0
|
$HeaderCase{lc $field_name} = $field_name; |
769
|
|
|
|
|
|
|
} |
770
|
0
|
0
|
|
|
|
0
|
for (ref $v eq 'ARRAY' ? @$v : $v) { |
771
|
0
|
0
|
|
|
|
0
|
/[^\x0D\x0A]/ |
772
|
|
|
|
|
|
|
or die(qq/Invalid HTTP header field value ($field_name): / . $Printable->($_). "\n"); |
773
|
0
|
|
|
|
|
0
|
$buf .= "$field_name: $_\x0D\x0A"; |
774
|
|
|
|
|
|
|
} |
775
|
|
|
|
|
|
|
} |
776
|
0
|
|
|
|
|
0
|
$buf .= "\x0D\x0A"; |
777
|
0
|
|
|
|
|
0
|
return $self->write($buf); |
778
|
|
|
|
|
|
|
} |
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
sub read_body { |
781
|
0
|
0
|
|
0
|
|
0
|
@_ == 3 || die(q/Usage: $handle->read_body(callback, response)/ . "\n"); |
782
|
0
|
|
|
|
|
0
|
my ($self, $cb, $response) = @_; |
783
|
0
|
|
0
|
|
|
0
|
my $te = $response->{headers}{'transfer-encoding'} || ''; |
784
|
0
|
0
|
|
|
|
0
|
if ( grep { /chunked/i } ( ref $te eq 'ARRAY' ? @$te : $te ) ) { |
|
0
|
0
|
|
|
|
0
|
|
785
|
0
|
|
|
|
|
0
|
$self->read_chunked_body($cb, $response); |
786
|
|
|
|
|
|
|
} |
787
|
|
|
|
|
|
|
else { |
788
|
0
|
|
|
|
|
0
|
$self->read_content_body($cb, $response); |
789
|
|
|
|
|
|
|
} |
790
|
0
|
|
|
|
|
0
|
return; |
791
|
|
|
|
|
|
|
} |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
sub write_body { |
794
|
0
|
0
|
|
0
|
|
0
|
@_ == 2 || die(q/Usage: $handle->write_body(request)/ . "\n"); |
795
|
0
|
|
|
|
|
0
|
my ($self, $request) = @_; |
796
|
0
|
0
|
|
|
|
0
|
if ($request->{headers}{'content-length'}) { |
797
|
0
|
|
|
|
|
0
|
return $self->write_content_body($request); |
798
|
|
|
|
|
|
|
} |
799
|
|
|
|
|
|
|
else { |
800
|
0
|
|
|
|
|
0
|
return $self->write_chunked_body($request); |
801
|
|
|
|
|
|
|
} |
802
|
|
|
|
|
|
|
} |
803
|
|
|
|
|
|
|
|
804
|
|
|
|
|
|
|
sub read_content_body { |
805
|
0
|
0
|
0
|
0
|
|
0
|
@_ == 3 || @_ == 4 || die(q/Usage: $handle->read_content_body(callback, response, [read_length])/ . "\n"); |
806
|
0
|
|
|
|
|
0
|
my ($self, $cb, $response, $content_length) = @_; |
807
|
0
|
|
0
|
|
|
0
|
$content_length ||= $response->{headers}{'content-length'}; |
808
|
|
|
|
|
|
|
|
809
|
0
|
0
|
|
|
|
0
|
if ( $content_length ) { |
810
|
0
|
|
|
|
|
0
|
my $len = $content_length; |
811
|
0
|
|
|
|
|
0
|
while ($len > 0) { |
812
|
0
|
0
|
|
|
|
0
|
my $read = ($len > BUFSIZE) ? BUFSIZE : $len; |
813
|
0
|
|
|
|
|
0
|
$cb->($self->read($read, 0), $response); |
814
|
0
|
|
|
|
|
0
|
$len -= $read; |
815
|
|
|
|
|
|
|
} |
816
|
|
|
|
|
|
|
} |
817
|
|
|
|
|
|
|
else { |
818
|
0
|
|
|
|
|
0
|
my $chunk; |
819
|
0
|
|
|
|
|
0
|
$cb->($chunk, $response) while length( $chunk = $self->read(BUFSIZE, 1) ); |
820
|
|
|
|
|
|
|
} |
821
|
|
|
|
|
|
|
|
822
|
0
|
|
|
|
|
0
|
return; |
823
|
|
|
|
|
|
|
} |
824
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
sub write_content_body { |
826
|
0
|
0
|
|
0
|
|
0
|
@_ == 2 || die(q/Usage: $handle->write_content_body(request)/ . "\n"); |
827
|
0
|
|
|
|
|
0
|
my ($self, $request) = @_; |
828
|
|
|
|
|
|
|
|
829
|
0
|
|
|
|
|
0
|
my ($len, $content_length) = (0, $request->{headers}{'content-length'}); |
830
|
0
|
|
|
|
|
0
|
while () { |
831
|
0
|
|
|
|
|
0
|
my $data = $request->{cb}->(); |
832
|
|
|
|
|
|
|
|
833
|
0
|
0
|
0
|
|
|
0
|
defined $data && length $data |
834
|
|
|
|
|
|
|
or last; |
835
|
|
|
|
|
|
|
|
836
|
0
|
0
|
|
|
|
0
|
if ( $] ge '5.008' ) { |
837
|
0
|
0
|
|
|
|
0
|
utf8::downgrade($data, 1) |
838
|
|
|
|
|
|
|
or die(qq/Wide character in write_content()\n/); |
839
|
|
|
|
|
|
|
} |
840
|
|
|
|
|
|
|
|
841
|
0
|
|
|
|
|
0
|
$len += $self->write($data); |
842
|
|
|
|
|
|
|
} |
843
|
|
|
|
|
|
|
|
844
|
0
|
0
|
|
|
|
0
|
$len == $content_length |
845
|
|
|
|
|
|
|
or die(qq/Content-Length missmatch (got: $len expected: $content_length)\n/); |
846
|
|
|
|
|
|
|
|
847
|
0
|
|
|
|
|
0
|
return $len; |
848
|
|
|
|
|
|
|
} |
849
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
sub read_chunked_body { |
851
|
0
|
0
|
|
0
|
|
0
|
@_ == 3 || die(q/Usage: $handle->read_chunked_body(callback, $response)/ . "\n"); |
852
|
0
|
|
|
|
|
0
|
my ($self, $cb, $response) = @_; |
853
|
|
|
|
|
|
|
|
854
|
0
|
|
|
|
|
0
|
while () { |
855
|
0
|
|
|
|
|
0
|
my $head = $self->readline; |
856
|
|
|
|
|
|
|
|
857
|
0
|
0
|
|
|
|
0
|
$head =~ /\A ([A-Fa-f0-9]+)/x |
858
|
|
|
|
|
|
|
or die(q/Malformed chunk head: / . $Printable->($head) . "\n"); |
859
|
|
|
|
|
|
|
|
860
|
0
|
0
|
|
|
|
0
|
my $len = hex($1) |
861
|
|
|
|
|
|
|
or last; |
862
|
|
|
|
|
|
|
|
863
|
0
|
|
|
|
|
0
|
$self->read_content_body($cb, $response, $len); |
864
|
|
|
|
|
|
|
|
865
|
0
|
0
|
|
|
|
0
|
$self->read(2) eq "\x0D\x0A" |
866
|
|
|
|
|
|
|
or die(qq/Malformed chunk: missing CRLF after chunk data\n/); |
867
|
|
|
|
|
|
|
} |
868
|
0
|
|
|
|
|
0
|
$self->read_header_lines($response->{headers}); |
869
|
0
|
|
|
|
|
0
|
return; |
870
|
|
|
|
|
|
|
} |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
sub write_chunked_body { |
873
|
0
|
0
|
|
0
|
|
0
|
@_ == 2 || die(q/Usage: $handle->write_chunked_body(request)/ . "\n"); |
874
|
0
|
|
|
|
|
0
|
my ($self, $request) = @_; |
875
|
|
|
|
|
|
|
|
876
|
0
|
|
|
|
|
0
|
my $len = 0; |
877
|
0
|
|
|
|
|
0
|
while () { |
878
|
0
|
|
|
|
|
0
|
my $data = $request->{cb}->(); |
879
|
|
|
|
|
|
|
|
880
|
0
|
0
|
0
|
|
|
0
|
defined $data && length $data |
881
|
|
|
|
|
|
|
or last; |
882
|
|
|
|
|
|
|
|
883
|
0
|
0
|
|
|
|
0
|
if ( $] ge '5.008' ) { |
884
|
0
|
0
|
|
|
|
0
|
utf8::downgrade($data, 1) |
885
|
|
|
|
|
|
|
or die(qq/Wide character in write_chunked_body()\n/); |
886
|
|
|
|
|
|
|
} |
887
|
|
|
|
|
|
|
|
888
|
0
|
|
|
|
|
0
|
$len += length $data; |
889
|
|
|
|
|
|
|
|
890
|
0
|
|
|
|
|
0
|
my $chunk = sprintf '%X', length $data; |
891
|
0
|
|
|
|
|
0
|
$chunk .= "\x0D\x0A"; |
892
|
0
|
|
|
|
|
0
|
$chunk .= $data; |
893
|
0
|
|
|
|
|
0
|
$chunk .= "\x0D\x0A"; |
894
|
|
|
|
|
|
|
|
895
|
0
|
|
|
|
|
0
|
$self->write($chunk); |
896
|
|
|
|
|
|
|
} |
897
|
0
|
|
|
|
|
0
|
$self->write("0\x0D\x0A"); |
898
|
0
|
0
|
|
|
|
0
|
$self->write_header_lines($request->{trailer_cb}->()) |
899
|
|
|
|
|
|
|
if ref $request->{trailer_cb} eq 'CODE'; |
900
|
0
|
|
|
|
|
0
|
return $len; |
901
|
|
|
|
|
|
|
} |
902
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
sub read_response_header { |
904
|
0
|
0
|
|
0
|
|
0
|
@_ == 1 || die(q/Usage: $handle->read_response_header()/ . "\n"); |
905
|
0
|
|
|
|
|
0
|
my ($self) = @_; |
906
|
|
|
|
|
|
|
|
907
|
0
|
|
|
|
|
0
|
my $line = $self->readline; |
908
|
|
|
|
|
|
|
|
909
|
0
|
0
|
|
|
|
0
|
$line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x |
910
|
|
|
|
|
|
|
or die(q/Malformed Status-Line: / . $Printable->($line). "\n"); |
911
|
|
|
|
|
|
|
|
912
|
0
|
|
|
|
|
0
|
my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4); |
913
|
|
|
|
|
|
|
|
914
|
0
|
0
|
|
|
|
0
|
die (qq/Unsupported HTTP protocol: $protocol\n/) |
915
|
|
|
|
|
|
|
unless $version =~ /0*1\.0*[01]/; |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
return { |
918
|
0
|
|
|
|
|
0
|
status => $status, |
919
|
|
|
|
|
|
|
reason => $reason, |
920
|
|
|
|
|
|
|
headers => $self->read_header_lines, |
921
|
|
|
|
|
|
|
protocol => $protocol, |
922
|
|
|
|
|
|
|
}; |
923
|
|
|
|
|
|
|
} |
924
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
sub write_request_header { |
926
|
0
|
0
|
|
0
|
|
0
|
@_ == 4 || die(q/Usage: $handle->write_request_header(method, request_uri, headers)/ . "\n"); |
927
|
0
|
|
|
|
|
0
|
my ($self, $method, $request_uri, $headers) = @_; |
928
|
|
|
|
|
|
|
|
929
|
0
|
|
|
|
|
0
|
return $self->write("$method $request_uri HTTP/1.1\x0D\x0A") |
930
|
|
|
|
|
|
|
+ $self->write_header_lines($headers); |
931
|
|
|
|
|
|
|
} |
932
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
sub _do_timeout { |
934
|
0
|
|
|
0
|
|
0
|
my ($self, $type, $timeout) = @_; |
935
|
0
|
0
|
0
|
|
|
0
|
$timeout = $self->{timeout} |
936
|
|
|
|
|
|
|
unless defined $timeout && $timeout >= 0; |
937
|
|
|
|
|
|
|
|
938
|
0
|
|
|
|
|
0
|
my $fd = fileno $self->{fh}; |
939
|
0
|
0
|
0
|
|
|
0
|
defined $fd && $fd >= 0 |
940
|
|
|
|
|
|
|
or die(qq/select(2): 'Bad file descriptor'\n/); |
941
|
|
|
|
|
|
|
|
942
|
0
|
|
|
|
|
0
|
my $initial = time; |
943
|
0
|
|
|
|
|
0
|
my $pending = $timeout; |
944
|
0
|
|
|
|
|
0
|
my $nfound; |
945
|
|
|
|
|
|
|
|
946
|
0
|
|
|
|
|
0
|
vec(my $fdset = '', $fd, 1) = 1; |
947
|
|
|
|
|
|
|
|
948
|
0
|
|
|
|
|
0
|
while () { |
949
|
0
|
0
|
|
|
|
0
|
$nfound = ($type eq 'read') |
950
|
|
|
|
|
|
|
? select($fdset, undef, undef, $pending) |
951
|
|
|
|
|
|
|
: select(undef, $fdset, undef, $pending) ; |
952
|
0
|
0
|
|
|
|
0
|
if ($nfound == -1) { |
953
|
0
|
0
|
|
|
|
0
|
$! == EINTR |
954
|
|
|
|
|
|
|
or die(qq/select(2): '$!'\n/); |
955
|
0
|
0
|
0
|
|
|
0
|
redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0; |
956
|
0
|
|
|
|
|
0
|
$nfound = 0; |
957
|
|
|
|
|
|
|
} |
958
|
0
|
|
|
|
|
0
|
last; |
959
|
|
|
|
|
|
|
} |
960
|
0
|
|
|
|
|
0
|
$! = 0; |
961
|
0
|
|
|
|
|
0
|
return $nfound; |
962
|
|
|
|
|
|
|
} |
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
sub can_read { |
965
|
0
|
0
|
0
|
0
|
|
0
|
@_ == 1 || @_ == 2 || die(q/Usage: $handle->can_read([timeout])/ . "\n"); |
966
|
0
|
|
|
|
|
0
|
my $self = shift; |
967
|
0
|
|
|
|
|
0
|
return $self->_do_timeout('read', @_) |
968
|
|
|
|
|
|
|
} |
969
|
|
|
|
|
|
|
|
970
|
|
|
|
|
|
|
sub can_write { |
971
|
0
|
0
|
0
|
0
|
|
0
|
@_ == 1 || @_ == 2 || die(q/Usage: $handle->can_write([timeout])/ . "\n"); |
972
|
0
|
|
|
|
|
0
|
my $self = shift; |
973
|
0
|
|
|
|
|
0
|
return $self->_do_timeout('write', @_) |
974
|
|
|
|
|
|
|
} |
975
|
|
|
|
|
|
|
|
976
|
1
|
|
|
1
|
|
4553
|
no warnings 'once'; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
65
|
|
977
|
|
|
|
|
|
|
package Test::ModuleVersion::JSON::PP; |
978
|
|
|
|
|
|
|
# JSON-2.0 |
979
|
|
|
|
|
|
|
|
980
|
1
|
|
|
1
|
|
35
|
use 5.005; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
43
|
|
981
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
39
|
|
982
|
1
|
|
|
1
|
|
5
|
use base qw(Exporter); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
138
|
|
983
|
1
|
|
|
1
|
|
1767
|
use overload (); |
|
1
|
|
|
|
|
1270
|
|
|
1
|
|
|
|
|
25
|
|
984
|
|
|
|
|
|
|
|
985
|
1
|
|
|
1
|
|
8
|
use Carp (); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
17
|
|
986
|
1
|
|
|
1
|
|
6
|
use B (); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
50
|
|
987
|
|
|
|
|
|
|
#use Devel::Peek; |
988
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
$Test::ModuleVersion::JSON::PP::VERSION = '2.27200'; |
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
@Test::ModuleVersion::JSON::PP::EXPORT = qw(encode_json decode_json from_json to_json); |
992
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
# instead of hash-access, i tried index-access for speed. |
994
|
|
|
|
|
|
|
# but this method is not faster than what i expected. so it will be changed. |
995
|
|
|
|
|
|
|
|
996
|
1
|
|
|
1
|
|
5
|
use constant P_ASCII => 0; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
84
|
|
997
|
1
|
|
|
1
|
|
6
|
use constant P_LATIN1 => 1; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
57
|
|
998
|
1
|
|
|
1
|
|
6
|
use constant P_UTF8 => 2; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
47
|
|
999
|
1
|
|
|
1
|
|
17
|
use constant P_INDENT => 3; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
59
|
|
1000
|
1
|
|
|
1
|
|
7
|
use constant P_CANONICAL => 4; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
45
|
|
1001
|
1
|
|
|
1
|
|
5
|
use constant P_SPACE_BEFORE => 5; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
53
|
|
1002
|
1
|
|
|
1
|
|
5
|
use constant P_SPACE_AFTER => 6; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
54
|
|
1003
|
1
|
|
|
1
|
|
27
|
use constant P_ALLOW_NONREF => 7; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
44
|
|
1004
|
1
|
|
|
1
|
|
6
|
use constant P_SHRINK => 8; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
45
|
|
1005
|
1
|
|
|
1
|
|
5
|
use constant P_ALLOW_BLESSED => 9; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
1519
|
|
1006
|
1
|
|
|
1
|
|
6
|
use constant P_CONVERT_BLESSED => 10; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
44
|
|
1007
|
1
|
|
|
1
|
|
4
|
use constant P_RELAXED => 11; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
40
|
|
1008
|
|
|
|
|
|
|
|
1009
|
1
|
|
|
1
|
|
5
|
use constant P_LOOSE => 12; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
39
|
|
1010
|
1
|
|
|
1
|
|
5
|
use constant P_ALLOW_BIGNUM => 13; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
39
|
|
1011
|
1
|
|
|
1
|
|
4
|
use constant P_ALLOW_BAREKEY => 14; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
44
|
|
1012
|
1
|
|
|
1
|
|
5
|
use constant P_ALLOW_SINGLEQUOTE => 15; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
45
|
|
1013
|
1
|
|
|
1
|
|
5
|
use constant P_ESCAPE_SLASH => 16; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
38
|
|
1014
|
1
|
|
|
1
|
|
18
|
use constant P_AS_NONBLESSED => 17; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
46
|
|
1015
|
|
|
|
|
|
|
|
1016
|
1
|
|
|
1
|
|
5
|
use constant P_ALLOW_UNKNOWN => 18; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
52
|
|
1017
|
|
|
|
|
|
|
|
1018
|
1
|
50
|
|
1
|
|
5
|
use constant OLD_PERL => $] < 5.008 ? 1 : 0; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
206
|
|
1019
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
BEGIN { |
1021
|
1
|
|
|
1
|
|
4
|
my @xs_compati_bit_properties = qw( |
1022
|
|
|
|
|
|
|
latin1 ascii utf8 indent canonical space_before space_after allow_nonref shrink |
1023
|
|
|
|
|
|
|
allow_blessed convert_blessed relaxed allow_unknown |
1024
|
|
|
|
|
|
|
); |
1025
|
1
|
|
|
|
|
3
|
my @pp_bit_properties = qw( |
1026
|
|
|
|
|
|
|
allow_singlequote allow_bignum loose |
1027
|
|
|
|
|
|
|
allow_barekey escape_slash as_nonblessed |
1028
|
|
|
|
|
|
|
); |
1029
|
|
|
|
|
|
|
|
1030
|
|
|
|
|
|
|
# Perl version check, Unicode handling is enable? |
1031
|
|
|
|
|
|
|
# Helper module sets @Test::ModuleVersion::JSON::PP::_properties. |
1032
|
1
|
50
|
|
|
|
5
|
if ($] < 5.008 ) { |
1033
|
0
|
0
|
|
|
|
0
|
my $helper = $] >= 5.006 ? 'Test::ModuleVersion::JSON::PP::Compat5006' : 'Test::ModuleVersion::JSON::PP::Compat5005'; |
1034
|
0
|
|
|
|
|
0
|
eval qq| require $helper |; |
1035
|
0
|
0
|
|
|
|
0
|
if ($@) { Carp::croak $@; } |
|
0
|
|
|
|
|
0
|
|
1036
|
|
|
|
|
|
|
} |
1037
|
|
|
|
|
|
|
|
1038
|
1
|
|
|
|
|
2
|
for my $name (@xs_compati_bit_properties, @pp_bit_properties) { |
1039
|
19
|
|
|
|
|
40
|
my $flag_name = 'P_' . uc($name); |
1040
|
|
|
|
|
|
|
|
1041
|
19
|
0
|
|
0
|
|
6882
|
eval qq/ |
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
0
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1042
|
|
|
|
|
|
|
sub $name { |
1043
|
|
|
|
|
|
|
my \$enable = defined \$_[1] ? \$_[1] : 1; |
1044
|
|
|
|
|
|
|
|
1045
|
|
|
|
|
|
|
if (\$enable) { |
1046
|
|
|
|
|
|
|
\$_[0]->{PROPS}->[$flag_name] = 1; |
1047
|
|
|
|
|
|
|
} |
1048
|
|
|
|
|
|
|
else { |
1049
|
|
|
|
|
|
|
\$_[0]->{PROPS}->[$flag_name] = 0; |
1050
|
|
|
|
|
|
|
} |
1051
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
\$_[0]; |
1053
|
|
|
|
|
|
|
} |
1054
|
|
|
|
|
|
|
|
1055
|
|
|
|
|
|
|
sub get_$name { |
1056
|
|
|
|
|
|
|
\$_[0]->{PROPS}->[$flag_name] ? 1 : ''; |
1057
|
|
|
|
|
|
|
} |
1058
|
|
|
|
|
|
|
/; |
1059
|
|
|
|
|
|
|
} |
1060
|
|
|
|
|
|
|
|
1061
|
|
|
|
|
|
|
} |
1062
|
|
|
|
|
|
|
|
1063
|
|
|
|
|
|
|
|
1064
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
# Functions |
1066
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
my %encode_allow_method |
1068
|
|
|
|
|
|
|
= map {($_ => 1)} qw/utf8 pretty allow_nonref latin1 self_encode escape_slash |
1069
|
|
|
|
|
|
|
allow_blessed convert_blessed indent indent_length allow_bignum |
1070
|
|
|
|
|
|
|
as_nonblessed |
1071
|
|
|
|
|
|
|
/; |
1072
|
|
|
|
|
|
|
my %decode_allow_method |
1073
|
|
|
|
|
|
|
= map {($_ => 1)} qw/utf8 allow_nonref loose allow_singlequote allow_bignum |
1074
|
|
|
|
|
|
|
allow_barekey max_size relaxed/; |
1075
|
|
|
|
|
|
|
|
1076
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
my $JSON; # cache |
1078
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
sub encode_json ($) { # encode |
1080
|
0
|
|
0
|
0
|
|
0
|
($JSON ||= __PACKAGE__->new->utf8)->encode(@_); |
1081
|
|
|
|
|
|
|
} |
1082
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
sub decode_json { # decode |
1085
|
0
|
|
0
|
0
|
|
0
|
($JSON ||= __PACKAGE__->new->utf8)->decode(@_); |
1086
|
|
|
|
|
|
|
} |
1087
|
|
|
|
|
|
|
|
1088
|
|
|
|
|
|
|
# Obsoleted |
1089
|
|
|
|
|
|
|
|
1090
|
|
|
|
|
|
|
sub to_json($) { |
1091
|
0
|
|
|
0
|
|
0
|
Carp::croak ("Test::ModuleVersion::JSON::PP::to_json has been renamed to encode_json."); |
1092
|
|
|
|
|
|
|
} |
1093
|
|
|
|
|
|
|
|
1094
|
|
|
|
|
|
|
|
1095
|
|
|
|
|
|
|
sub from_json($) { |
1096
|
0
|
|
|
0
|
|
0
|
Carp::croak ("Test::ModuleVersion::JSON::PP::from_json has been renamed to decode_json."); |
1097
|
|
|
|
|
|
|
} |
1098
|
|
|
|
|
|
|
|
1099
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
# Methods |
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
sub new { |
1103
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
1104
|
|
|
|
|
|
|
my $self = { |
1105
|
|
|
|
|
|
|
max_depth => 512, |
1106
|
|
|
|
|
|
|
max_size => 0, |
1107
|
|
|
|
|
|
|
indent => 0, |
1108
|
|
|
|
|
|
|
FLAGS => 0, |
1109
|
0
|
|
|
0
|
|
0
|
fallback => sub { encode_error('Invalid value. JSON can only reference.') }, |
1110
|
0
|
|
|
|
|
0
|
indent_length => 3, |
1111
|
|
|
|
|
|
|
}; |
1112
|
|
|
|
|
|
|
|
1113
|
0
|
|
|
|
|
0
|
bless $self, $class; |
1114
|
|
|
|
|
|
|
} |
1115
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
|
1117
|
|
|
|
|
|
|
sub encode { |
1118
|
0
|
|
|
0
|
|
0
|
return $_[0]->PP_encode_json($_[1]); |
1119
|
|
|
|
|
|
|
} |
1120
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
|
1122
|
|
|
|
|
|
|
sub decode { |
1123
|
0
|
|
|
0
|
|
0
|
return $_[0]->PP_decode_json($_[1], 0x00000000); |
1124
|
|
|
|
|
|
|
} |
1125
|
|
|
|
|
|
|
|
1126
|
|
|
|
|
|
|
|
1127
|
|
|
|
|
|
|
sub decode_prefix { |
1128
|
0
|
|
|
0
|
|
0
|
return $_[0]->PP_decode_json($_[1], 0x00000001); |
1129
|
|
|
|
|
|
|
} |
1130
|
|
|
|
|
|
|
|
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
# accessor |
1133
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
|
1135
|
|
|
|
|
|
|
# pretty printing |
1136
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
sub pretty { |
1138
|
0
|
|
|
0
|
|
0
|
my ($self, $v) = @_; |
1139
|
0
|
0
|
|
|
|
0
|
my $enable = defined $v ? $v : 1; |
1140
|
|
|
|
|
|
|
|
1141
|
0
|
0
|
|
|
|
0
|
if ($enable) { # indent_length(3) for JSON::XS compatibility |
1142
|
0
|
|
|
|
|
0
|
$self->indent(1)->indent_length(3)->space_before(1)->space_after(1); |
1143
|
|
|
|
|
|
|
} |
1144
|
|
|
|
|
|
|
else { |
1145
|
0
|
|
|
|
|
0
|
$self->indent(0)->space_before(0)->space_after(0); |
1146
|
|
|
|
|
|
|
} |
1147
|
|
|
|
|
|
|
|
1148
|
0
|
|
|
|
|
0
|
$self; |
1149
|
|
|
|
|
|
|
} |
1150
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
# etc |
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
sub max_depth { |
1154
|
0
|
0
|
|
0
|
|
0
|
my $max = defined $_[1] ? $_[1] : 0x80000000; |
1155
|
0
|
|
|
|
|
0
|
$_[0]->{max_depth} = $max; |
1156
|
0
|
|
|
|
|
0
|
$_[0]; |
1157
|
|
|
|
|
|
|
} |
1158
|
|
|
|
|
|
|
|
1159
|
|
|
|
|
|
|
|
1160
|
0
|
|
|
0
|
|
0
|
sub get_max_depth { $_[0]->{max_depth}; } |
1161
|
|
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
|
1163
|
|
|
|
|
|
|
sub max_size { |
1164
|
0
|
0
|
|
0
|
|
0
|
my $max = defined $_[1] ? $_[1] : 0; |
1165
|
0
|
|
|
|
|
0
|
$_[0]->{max_size} = $max; |
1166
|
0
|
|
|
|
|
0
|
$_[0]; |
1167
|
|
|
|
|
|
|
} |
1168
|
|
|
|
|
|
|
|
1169
|
|
|
|
|
|
|
|
1170
|
0
|
|
|
0
|
|
0
|
sub get_max_size { $_[0]->{max_size}; } |
1171
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
|
1173
|
|
|
|
|
|
|
sub filter_json_object { |
1174
|
0
|
0
|
|
0
|
|
0
|
$_[0]->{cb_object} = defined $_[1] ? $_[1] : 0; |
1175
|
0
|
0
|
0
|
|
|
0
|
$_[0]->{F_HOOK} = ($_[0]->{cb_object} or $_[0]->{cb_sk_object}) ? 1 : 0; |
1176
|
0
|
|
|
|
|
0
|
$_[0]; |
1177
|
|
|
|
|
|
|
} |
1178
|
|
|
|
|
|
|
|
1179
|
|
|
|
|
|
|
sub filter_json_single_key_object { |
1180
|
0
|
0
|
|
0
|
|
0
|
if (@_ > 1) { |
1181
|
0
|
|
|
|
|
0
|
$_[0]->{cb_sk_object}->{$_[1]} = $_[2]; |
1182
|
|
|
|
|
|
|
} |
1183
|
0
|
0
|
0
|
|
|
0
|
$_[0]->{F_HOOK} = ($_[0]->{cb_object} or $_[0]->{cb_sk_object}) ? 1 : 0; |
1184
|
0
|
|
|
|
|
0
|
$_[0]; |
1185
|
|
|
|
|
|
|
} |
1186
|
|
|
|
|
|
|
|
1187
|
|
|
|
|
|
|
sub indent_length { |
1188
|
0
|
0
|
0
|
0
|
|
0
|
if (!defined $_[1] or $_[1] > 15 or $_[1] < 0) { |
|
|
|
0
|
|
|
|
|
1189
|
0
|
|
|
|
|
0
|
Carp::carp "The acceptable range of indent_length() is 0 to 15."; |
1190
|
|
|
|
|
|
|
} |
1191
|
|
|
|
|
|
|
else { |
1192
|
0
|
|
|
|
|
0
|
$_[0]->{indent_length} = $_[1]; |
1193
|
|
|
|
|
|
|
} |
1194
|
0
|
|
|
|
|
0
|
$_[0]; |
1195
|
|
|
|
|
|
|
} |
1196
|
|
|
|
|
|
|
|
1197
|
|
|
|
|
|
|
sub get_indent_length { |
1198
|
0
|
|
|
0
|
|
0
|
$_[0]->{indent_length}; |
1199
|
|
|
|
|
|
|
} |
1200
|
|
|
|
|
|
|
|
1201
|
|
|
|
|
|
|
sub sort_by { |
1202
|
0
|
0
|
|
0
|
|
0
|
$_[0]->{sort_by} = defined $_[1] ? $_[1] : 1; |
1203
|
0
|
|
|
|
|
0
|
$_[0]; |
1204
|
|
|
|
|
|
|
} |
1205
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
sub allow_bigint { |
1207
|
0
|
|
|
0
|
|
0
|
Carp::carp("allow_bigint() is obsoleted. use allow_bignum() insted."); |
1208
|
|
|
|
|
|
|
} |
1209
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
############################### |
1211
|
|
|
|
|
|
|
|
1212
|
|
|
|
|
|
|
### |
1213
|
|
|
|
|
|
|
### Perl => JSON |
1214
|
|
|
|
|
|
|
### |
1215
|
|
|
|
|
|
|
|
1216
|
|
|
|
|
|
|
|
1217
|
|
|
|
|
|
|
{ # Convert |
1218
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
my $max_depth; |
1220
|
|
|
|
|
|
|
my $indent; |
1221
|
|
|
|
|
|
|
my $ascii; |
1222
|
|
|
|
|
|
|
my $latin1; |
1223
|
|
|
|
|
|
|
my $utf8; |
1224
|
|
|
|
|
|
|
my $space_before; |
1225
|
|
|
|
|
|
|
my $space_after; |
1226
|
|
|
|
|
|
|
my $canonical; |
1227
|
|
|
|
|
|
|
my $allow_blessed; |
1228
|
|
|
|
|
|
|
my $convert_blessed; |
1229
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
my $indent_length; |
1231
|
|
|
|
|
|
|
my $escape_slash; |
1232
|
|
|
|
|
|
|
my $bignum; |
1233
|
|
|
|
|
|
|
my $as_nonblessed; |
1234
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
my $depth; |
1236
|
|
|
|
|
|
|
my $indent_count; |
1237
|
|
|
|
|
|
|
my $keysort; |
1238
|
|
|
|
|
|
|
|
1239
|
|
|
|
|
|
|
|
1240
|
|
|
|
|
|
|
sub PP_encode_json { |
1241
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1242
|
0
|
|
|
|
|
0
|
my $obj = shift; |
1243
|
|
|
|
|
|
|
|
1244
|
0
|
|
|
|
|
0
|
$indent_count = 0; |
1245
|
0
|
|
|
|
|
0
|
$depth = 0; |
1246
|
|
|
|
|
|
|
|
1247
|
0
|
|
|
|
|
0
|
my $idx = $self->{PROPS}; |
1248
|
|
|
|
|
|
|
|
1249
|
0
|
|
|
|
|
0
|
($ascii, $latin1, $utf8, $indent, $canonical, $space_before, $space_after, $allow_blessed, |
1250
|
|
|
|
|
|
|
$convert_blessed, $escape_slash, $bignum, $as_nonblessed) |
1251
|
0
|
|
|
|
|
0
|
= @{$idx}[P_ASCII .. P_SPACE_AFTER, P_ALLOW_BLESSED, P_CONVERT_BLESSED, |
1252
|
|
|
|
|
|
|
P_ESCAPE_SLASH, P_ALLOW_BIGNUM, P_AS_NONBLESSED]; |
1253
|
|
|
|
|
|
|
|
1254
|
0
|
|
|
|
|
0
|
($max_depth, $indent_length) = @{$self}{qw/max_depth indent_length/}; |
|
0
|
|
|
|
|
0
|
|
1255
|
|
|
|
|
|
|
|
1256
|
0
|
0
|
|
0
|
|
0
|
$keysort = $canonical ? sub { $a cmp $b } : undef; |
|
0
|
|
|
|
|
0
|
|
1257
|
|
|
|
|
|
|
|
1258
|
0
|
0
|
|
|
|
0
|
if ($self->{sort_by}) { |
1259
|
|
|
|
|
|
|
$keysort = ref($self->{sort_by}) eq 'CODE' ? $self->{sort_by} |
1260
|
|
|
|
|
|
|
: $self->{sort_by} =~ /\D+/ ? $self->{sort_by} |
1261
|
0
|
0
|
|
0
|
|
0
|
: sub { $a cmp $b }; |
|
0
|
0
|
|
|
|
0
|
|
1262
|
|
|
|
|
|
|
} |
1263
|
|
|
|
|
|
|
|
1264
|
0
|
0
|
0
|
|
|
0
|
encode_error("hash- or arrayref expected (not a simple scalar, use allow_nonref to allow this)") |
1265
|
|
|
|
|
|
|
if(!ref $obj and !$idx->[ P_ALLOW_NONREF ]); |
1266
|
|
|
|
|
|
|
|
1267
|
0
|
|
|
|
|
0
|
my $str = $self->object_to_json($obj); |
1268
|
|
|
|
|
|
|
|
1269
|
0
|
0
|
|
|
|
0
|
$str .= "\n" if ( $indent ); # JSON::XS 2.26 compatible |
1270
|
|
|
|
|
|
|
|
1271
|
0
|
0
|
0
|
|
|
0
|
unless ($ascii or $latin1 or $utf8) { |
|
|
|
0
|
|
|
|
|
1272
|
0
|
|
|
|
|
0
|
utf8::upgrade($str); |
1273
|
|
|
|
|
|
|
} |
1274
|
|
|
|
|
|
|
|
1275
|
0
|
0
|
|
|
|
0
|
if ($idx->[ P_SHRINK ]) { |
1276
|
0
|
|
|
|
|
0
|
utf8::downgrade($str, 1); |
1277
|
|
|
|
|
|
|
} |
1278
|
|
|
|
|
|
|
|
1279
|
0
|
|
|
|
|
0
|
return $str; |
1280
|
|
|
|
|
|
|
} |
1281
|
|
|
|
|
|
|
|
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
sub object_to_json { |
1284
|
0
|
|
|
0
|
|
0
|
my ($self, $obj) = @_; |
1285
|
0
|
|
|
|
|
0
|
my $type = ref($obj); |
1286
|
|
|
|
|
|
|
|
1287
|
0
|
0
|
|
|
|
0
|
if($type eq 'HASH'){ |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1288
|
0
|
|
|
|
|
0
|
return $self->hash_to_json($obj); |
1289
|
|
|
|
|
|
|
} |
1290
|
|
|
|
|
|
|
elsif($type eq 'ARRAY'){ |
1291
|
0
|
|
|
|
|
0
|
return $self->array_to_json($obj); |
1292
|
|
|
|
|
|
|
} |
1293
|
|
|
|
|
|
|
elsif ($type) { # blessed object? |
1294
|
0
|
0
|
|
|
|
0
|
if (blessed($obj)) { |
1295
|
|
|
|
|
|
|
|
1296
|
0
|
0
|
|
|
|
0
|
return $self->value_to_json($obj) if ( $obj->isa('Test::ModuleVersion::JSON::PP::Boolean') ); |
1297
|
|
|
|
|
|
|
|
1298
|
0
|
0
|
0
|
|
|
0
|
if ( $convert_blessed and $obj->can('TO_JSON') ) { |
1299
|
0
|
|
|
|
|
0
|
my $result = $obj->TO_JSON(); |
1300
|
0
|
0
|
0
|
|
|
0
|
if ( defined $result and ref( $result ) ) { |
1301
|
0
|
0
|
|
|
|
0
|
if ( refaddr( $obj ) eq refaddr( $result ) ) { |
1302
|
0
|
|
|
|
|
0
|
encode_error( sprintf( |
1303
|
|
|
|
|
|
|
"%s::TO_JSON method returned same object as was passed instead of a new one", |
1304
|
|
|
|
|
|
|
ref $obj |
1305
|
|
|
|
|
|
|
) ); |
1306
|
|
|
|
|
|
|
} |
1307
|
|
|
|
|
|
|
} |
1308
|
|
|
|
|
|
|
|
1309
|
0
|
|
|
|
|
0
|
return $self->object_to_json( $result ); |
1310
|
|
|
|
|
|
|
} |
1311
|
|
|
|
|
|
|
|
1312
|
0
|
0
|
0
|
|
|
0
|
return "$obj" if ( $bignum and _is_bignum($obj) ); |
1313
|
0
|
0
|
0
|
|
|
0
|
return $self->blessed_to_json($obj) if ($allow_blessed and $as_nonblessed); # will be removed. |
1314
|
|
|
|
|
|
|
|
1315
|
0
|
0
|
|
|
|
0
|
encode_error( sprintf("encountered object '%s', but neither allow_blessed " |
1316
|
|
|
|
|
|
|
. "nor convert_blessed settings are enabled", $obj) |
1317
|
|
|
|
|
|
|
) unless ($allow_blessed); |
1318
|
|
|
|
|
|
|
|
1319
|
0
|
|
|
|
|
0
|
return 'null'; |
1320
|
|
|
|
|
|
|
} |
1321
|
|
|
|
|
|
|
else { |
1322
|
0
|
|
|
|
|
0
|
return $self->value_to_json($obj); |
1323
|
|
|
|
|
|
|
} |
1324
|
|
|
|
|
|
|
} |
1325
|
|
|
|
|
|
|
else{ |
1326
|
0
|
|
|
|
|
0
|
return $self->value_to_json($obj); |
1327
|
|
|
|
|
|
|
} |
1328
|
|
|
|
|
|
|
} |
1329
|
|
|
|
|
|
|
|
1330
|
|
|
|
|
|
|
|
1331
|
|
|
|
|
|
|
sub hash_to_json { |
1332
|
0
|
|
|
0
|
|
0
|
my ($self, $obj) = @_; |
1333
|
0
|
|
|
|
|
0
|
my @res; |
1334
|
|
|
|
|
|
|
|
1335
|
0
|
0
|
|
|
|
0
|
encode_error("json text or perl structure exceeds maximum nesting level (max_depth set too low?)") |
1336
|
|
|
|
|
|
|
if (++$depth > $max_depth); |
1337
|
|
|
|
|
|
|
|
1338
|
0
|
0
|
|
|
|
0
|
my ($pre, $post) = $indent ? $self->_up_indent() : ('', ''); |
1339
|
0
|
0
|
|
|
|
0
|
my $del = ($space_before ? ' ' : '') . ':' . ($space_after ? ' ' : ''); |
|
|
0
|
|
|
|
|
|
1340
|
|
|
|
|
|
|
|
1341
|
0
|
|
|
|
|
0
|
for my $k ( _sort( $obj ) ) { |
1342
|
0
|
|
|
|
|
0
|
if ( OLD_PERL ) { utf8::decode($k) } # key for Perl 5.6 / be optimized |
1343
|
0
|
|
0
|
|
|
0
|
push @res, string_to_json( $self, $k ) |
1344
|
|
|
|
|
|
|
. $del |
1345
|
|
|
|
|
|
|
. ( $self->object_to_json( $obj->{$k} ) || $self->value_to_json( $obj->{$k} ) ); |
1346
|
|
|
|
|
|
|
} |
1347
|
|
|
|
|
|
|
|
1348
|
0
|
|
|
|
|
0
|
--$depth; |
1349
|
0
|
0
|
|
|
|
0
|
$self->_down_indent() if ($indent); |
1350
|
|
|
|
|
|
|
|
1351
|
0
|
0
|
|
|
|
0
|
return '{' . ( @res ? $pre : '' ) . ( @res ? join( ",$pre", @res ) . $post : '' ) . '}'; |
|
|
0
|
|
|
|
|
|
1352
|
|
|
|
|
|
|
} |
1353
|
|
|
|
|
|
|
|
1354
|
|
|
|
|
|
|
|
1355
|
|
|
|
|
|
|
sub array_to_json { |
1356
|
0
|
|
|
0
|
|
0
|
my ($self, $obj) = @_; |
1357
|
0
|
|
|
|
|
0
|
my @res; |
1358
|
|
|
|
|
|
|
|
1359
|
0
|
0
|
|
|
|
0
|
encode_error("json text or perl structure exceeds maximum nesting level (max_depth set too low?)") |
1360
|
|
|
|
|
|
|
if (++$depth > $max_depth); |
1361
|
|
|
|
|
|
|
|
1362
|
0
|
0
|
|
|
|
0
|
my ($pre, $post) = $indent ? $self->_up_indent() : ('', ''); |
1363
|
|
|
|
|
|
|
|
1364
|
0
|
|
|
|
|
0
|
for my $v (@$obj){ |
1365
|
0
|
|
0
|
|
|
0
|
push @res, $self->object_to_json($v) || $self->value_to_json($v); |
1366
|
|
|
|
|
|
|
} |
1367
|
|
|
|
|
|
|
|
1368
|
0
|
|
|
|
|
0
|
--$depth; |
1369
|
0
|
0
|
|
|
|
0
|
$self->_down_indent() if ($indent); |
1370
|
|
|
|
|
|
|
|
1371
|
0
|
0
|
|
|
|
0
|
return '[' . ( @res ? $pre : '' ) . ( @res ? join( ",$pre", @res ) . $post : '' ) . ']'; |
|
|
0
|
|
|
|
|
|
1372
|
|
|
|
|
|
|
} |
1373
|
|
|
|
|
|
|
|
1374
|
|
|
|
|
|
|
|
1375
|
|
|
|
|
|
|
sub value_to_json { |
1376
|
0
|
|
|
0
|
|
0
|
my ($self, $value) = @_; |
1377
|
|
|
|
|
|
|
|
1378
|
0
|
0
|
|
|
|
0
|
return 'null' if(!defined $value); |
1379
|
|
|
|
|
|
|
|
1380
|
0
|
|
|
|
|
0
|
my $b_obj = B::svref_2object(\$value); # for round trip problem |
1381
|
0
|
|
|
|
|
0
|
my $flags = $b_obj->FLAGS; |
1382
|
|
|
|
|
|
|
|
1383
|
0
|
0
|
0
|
|
|
0
|
return $value # as is |
1384
|
|
|
|
|
|
|
if $flags & ( B::SVp_IOK | B::SVp_NOK ) and !( $flags & B::SVp_POK ); # SvTYPE is IV or NV? |
1385
|
|
|
|
|
|
|
|
1386
|
0
|
|
|
|
|
0
|
my $type = ref($value); |
1387
|
|
|
|
|
|
|
|
1388
|
0
|
0
|
0
|
|
|
0
|
if(!$type){ |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1389
|
0
|
|
|
|
|
0
|
return string_to_json($self, $value); |
1390
|
|
|
|
|
|
|
} |
1391
|
|
|
|
|
|
|
elsif( blessed($value) and $value->isa('Test::ModuleVersion::JSON::PP::Boolean') ){ |
1392
|
0
|
0
|
|
|
|
0
|
return $$value == 1 ? 'true' : 'false'; |
1393
|
|
|
|
|
|
|
} |
1394
|
|
|
|
|
|
|
elsif ($type) { |
1395
|
0
|
0
|
|
|
|
0
|
if ((overload::StrVal($value) =~ /=(\w+)/)[0]) { |
1396
|
0
|
|
|
|
|
0
|
return $self->value_to_json("$value"); |
1397
|
|
|
|
|
|
|
} |
1398
|
|
|
|
|
|
|
|
1399
|
0
|
0
|
0
|
|
|
0
|
if ($type eq 'SCALAR' and defined $$value) { |
1400
|
0
|
0
|
|
|
|
0
|
return $$value eq '1' ? 'true' |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1401
|
|
|
|
|
|
|
: $$value eq '0' ? 'false' |
1402
|
|
|
|
|
|
|
: $self->{PROPS}->[ P_ALLOW_UNKNOWN ] ? 'null' |
1403
|
|
|
|
|
|
|
: encode_error("cannot encode reference to scalar"); |
1404
|
|
|
|
|
|
|
} |
1405
|
|
|
|
|
|
|
|
1406
|
0
|
0
|
|
|
|
0
|
if ( $self->{PROPS}->[ P_ALLOW_UNKNOWN ] ) { |
1407
|
0
|
|
|
|
|
0
|
return 'null'; |
1408
|
|
|
|
|
|
|
} |
1409
|
|
|
|
|
|
|
else { |
1410
|
0
|
0
|
0
|
|
|
0
|
if ( $type eq 'SCALAR' or $type eq 'REF' ) { |
1411
|
0
|
|
|
|
|
0
|
encode_error("cannot encode reference to scalar"); |
1412
|
|
|
|
|
|
|
} |
1413
|
|
|
|
|
|
|
else { |
1414
|
0
|
|
|
|
|
0
|
encode_error("encountered $value, but JSON can only represent references to arrays or hashes"); |
1415
|
|
|
|
|
|
|
} |
1416
|
|
|
|
|
|
|
} |
1417
|
|
|
|
|
|
|
|
1418
|
|
|
|
|
|
|
} |
1419
|
|
|
|
|
|
|
else { |
1420
|
0
|
0
|
0
|
|
|
0
|
return $self->{fallback}->($value) |
1421
|
|
|
|
|
|
|
if ($self->{fallback} and ref($self->{fallback}) eq 'CODE'); |
1422
|
0
|
|
|
|
|
0
|
return 'null'; |
1423
|
|
|
|
|
|
|
} |
1424
|
|
|
|
|
|
|
|
1425
|
|
|
|
|
|
|
} |
1426
|
|
|
|
|
|
|
|
1427
|
|
|
|
|
|
|
|
1428
|
|
|
|
|
|
|
my %esc = ( |
1429
|
|
|
|
|
|
|
"\n" => '\n', |
1430
|
|
|
|
|
|
|
"\r" => '\r', |
1431
|
|
|
|
|
|
|
"\t" => '\t', |
1432
|
|
|
|
|
|
|
"\f" => '\f', |
1433
|
|
|
|
|
|
|
"\b" => '\b', |
1434
|
|
|
|
|
|
|
"\"" => '\"', |
1435
|
|
|
|
|
|
|
"\\" => '\\\\', |
1436
|
|
|
|
|
|
|
"\'" => '\\\'', |
1437
|
|
|
|
|
|
|
); |
1438
|
|
|
|
|
|
|
|
1439
|
|
|
|
|
|
|
|
1440
|
|
|
|
|
|
|
sub string_to_json { |
1441
|
0
|
|
|
0
|
|
0
|
my ($self, $arg) = @_; |
1442
|
|
|
|
|
|
|
|
1443
|
0
|
|
|
|
|
0
|
$arg =~ s/([\x22\x5c\n\r\t\f\b])/$esc{$1}/g; |
1444
|
0
|
0
|
|
|
|
0
|
$arg =~ s/\//\\\//g if ($escape_slash); |
1445
|
0
|
|
|
|
|
0
|
$arg =~ s/([\x00-\x08\x0b\x0e-\x1f])/'\\u00' . unpack('H2', $1)/eg; |
|
0
|
|
|
|
|
0
|
|
1446
|
|
|
|
|
|
|
|
1447
|
0
|
0
|
|
|
|
0
|
if ($ascii) { |
1448
|
0
|
|
|
|
|
0
|
$arg = JSON_PP_encode_ascii($arg); |
1449
|
|
|
|
|
|
|
} |
1450
|
|
|
|
|
|
|
|
1451
|
0
|
0
|
|
|
|
0
|
if ($latin1) { |
1452
|
0
|
|
|
|
|
0
|
$arg = JSON_PP_encode_latin1($arg); |
1453
|
|
|
|
|
|
|
} |
1454
|
|
|
|
|
|
|
|
1455
|
0
|
0
|
|
|
|
0
|
if ($utf8) { |
1456
|
0
|
|
|
|
|
0
|
utf8::encode($arg); |
1457
|
|
|
|
|
|
|
} |
1458
|
|
|
|
|
|
|
|
1459
|
0
|
|
|
|
|
0
|
return '"' . $arg . '"'; |
1460
|
|
|
|
|
|
|
} |
1461
|
|
|
|
|
|
|
|
1462
|
|
|
|
|
|
|
|
1463
|
|
|
|
|
|
|
sub blessed_to_json { |
1464
|
0
|
|
0
|
0
|
|
0
|
my $reftype = reftype($_[1]) || ''; |
1465
|
0
|
0
|
|
|
|
0
|
if ($reftype eq 'HASH') { |
|
|
0
|
|
|
|
|
|
1466
|
0
|
|
|
|
|
0
|
return $_[0]->hash_to_json($_[1]); |
1467
|
|
|
|
|
|
|
} |
1468
|
|
|
|
|
|
|
elsif ($reftype eq 'ARRAY') { |
1469
|
0
|
|
|
|
|
0
|
return $_[0]->array_to_json($_[1]); |
1470
|
|
|
|
|
|
|
} |
1471
|
|
|
|
|
|
|
else { |
1472
|
0
|
|
|
|
|
0
|
return 'null'; |
1473
|
|
|
|
|
|
|
} |
1474
|
|
|
|
|
|
|
} |
1475
|
|
|
|
|
|
|
|
1476
|
|
|
|
|
|
|
|
1477
|
|
|
|
|
|
|
sub encode_error { |
1478
|
0
|
|
|
0
|
|
0
|
my $error = shift; |
1479
|
0
|
|
|
|
|
0
|
Carp::croak "$error"; |
1480
|
|
|
|
|
|
|
} |
1481
|
|
|
|
|
|
|
|
1482
|
|
|
|
|
|
|
|
1483
|
|
|
|
|
|
|
sub _sort { |
1484
|
0
|
0
|
|
0
|
|
0
|
defined $keysort ? (sort $keysort (keys %{$_[0]})) : keys %{$_[0]}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1485
|
|
|
|
|
|
|
} |
1486
|
|
|
|
|
|
|
|
1487
|
|
|
|
|
|
|
|
1488
|
|
|
|
|
|
|
sub _up_indent { |
1489
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1490
|
0
|
|
|
|
|
0
|
my $space = ' ' x $indent_length; |
1491
|
|
|
|
|
|
|
|
1492
|
0
|
|
|
|
|
0
|
my ($pre,$post) = ('',''); |
1493
|
|
|
|
|
|
|
|
1494
|
0
|
|
|
|
|
0
|
$post = "\n" . $space x $indent_count; |
1495
|
|
|
|
|
|
|
|
1496
|
0
|
|
|
|
|
0
|
$indent_count++; |
1497
|
|
|
|
|
|
|
|
1498
|
0
|
|
|
|
|
0
|
$pre = "\n" . $space x $indent_count; |
1499
|
|
|
|
|
|
|
|
1500
|
0
|
|
|
|
|
0
|
return ($pre,$post); |
1501
|
|
|
|
|
|
|
} |
1502
|
|
|
|
|
|
|
|
1503
|
|
|
|
|
|
|
|
1504
|
0
|
|
|
0
|
|
0
|
sub _down_indent { $indent_count--; } |
1505
|
|
|
|
|
|
|
|
1506
|
|
|
|
|
|
|
|
1507
|
|
|
|
|
|
|
sub PP_encode_box { |
1508
|
|
|
|
|
|
|
{ |
1509
|
0
|
|
|
0
|
|
0
|
depth => $depth, |
1510
|
|
|
|
|
|
|
indent_count => $indent_count, |
1511
|
|
|
|
|
|
|
}; |
1512
|
|
|
|
|
|
|
} |
1513
|
|
|
|
|
|
|
|
1514
|
|
|
|
|
|
|
} # Convert |
1515
|
|
|
|
|
|
|
|
1516
|
|
|
|
|
|
|
|
1517
|
|
|
|
|
|
|
sub _encode_ascii { |
1518
|
0
|
0
|
|
|
|
0
|
join('', |
|
|
0
|
|
|
|
|
|
1519
|
|
|
|
|
|
|
map { |
1520
|
0
|
|
|
0
|
|
0
|
$_ <= 127 ? |
1521
|
|
|
|
|
|
|
chr($_) : |
1522
|
|
|
|
|
|
|
$_ <= 65535 ? |
1523
|
|
|
|
|
|
|
sprintf('\u%04x', $_) : sprintf('\u%x\u%x', _encode_surrogates($_)); |
1524
|
|
|
|
|
|
|
} unpack('U*', $_[0]) |
1525
|
|
|
|
|
|
|
); |
1526
|
|
|
|
|
|
|
} |
1527
|
|
|
|
|
|
|
|
1528
|
|
|
|
|
|
|
|
1529
|
|
|
|
|
|
|
sub _encode_latin1 { |
1530
|
0
|
0
|
|
|
|
0
|
join('', |
|
|
0
|
|
|
|
|
|
1531
|
|
|
|
|
|
|
map { |
1532
|
0
|
|
|
0
|
|
0
|
$_ <= 255 ? |
1533
|
|
|
|
|
|
|
chr($_) : |
1534
|
|
|
|
|
|
|
$_ <= 65535 ? |
1535
|
|
|
|
|
|
|
sprintf('\u%04x', $_) : sprintf('\u%x\u%x', _encode_surrogates($_)); |
1536
|
|
|
|
|
|
|
} unpack('U*', $_[0]) |
1537
|
|
|
|
|
|
|
); |
1538
|
|
|
|
|
|
|
} |
1539
|
|
|
|
|
|
|
|
1540
|
|
|
|
|
|
|
|
1541
|
|
|
|
|
|
|
sub _encode_surrogates { # from perlunicode |
1542
|
0
|
|
|
0
|
|
0
|
my $uni = $_[0] - 0x10000; |
1543
|
0
|
|
|
|
|
0
|
return ($uni / 0x400 + 0xD800, $uni % 0x400 + 0xDC00); |
1544
|
|
|
|
|
|
|
} |
1545
|
|
|
|
|
|
|
|
1546
|
|
|
|
|
|
|
|
1547
|
|
|
|
|
|
|
sub _is_bignum { |
1548
|
0
|
0
|
|
0
|
|
0
|
$_[0]->isa('Math::BigInt') or $_[0]->isa('Math::BigFloat'); |
1549
|
|
|
|
|
|
|
} |
1550
|
|
|
|
|
|
|
|
1551
|
|
|
|
|
|
|
|
1552
|
|
|
|
|
|
|
|
1553
|
|
|
|
|
|
|
# |
1554
|
|
|
|
|
|
|
# JSON => Perl |
1555
|
|
|
|
|
|
|
# |
1556
|
|
|
|
|
|
|
|
1557
|
|
|
|
|
|
|
my $max_intsize; |
1558
|
|
|
|
|
|
|
|
1559
|
|
|
|
|
|
|
BEGIN { |
1560
|
1
|
|
|
1
|
|
3
|
my $checkint = 1111; |
1561
|
1
|
|
|
|
|
3
|
for my $d (5..64) { |
1562
|
17
|
|
|
|
|
23
|
$checkint .= 1; |
1563
|
17
|
|
|
|
|
691
|
my $int = eval qq| $checkint |; |
1564
|
17
|
100
|
|
|
|
81
|
if ($int =~ /[eE]/) { |
1565
|
1
|
|
|
|
|
3
|
$max_intsize = $d - 1; |
1566
|
1
|
|
|
|
|
489
|
last; |
1567
|
|
|
|
|
|
|
} |
1568
|
|
|
|
|
|
|
} |
1569
|
|
|
|
|
|
|
} |
1570
|
|
|
|
|
|
|
|
1571
|
|
|
|
|
|
|
{ # PARSE |
1572
|
|
|
|
|
|
|
|
1573
|
|
|
|
|
|
|
my %escapes = ( # by Jeremy Muhlich |
1574
|
|
|
|
|
|
|
b => "\x8", |
1575
|
|
|
|
|
|
|
t => "\x9", |
1576
|
|
|
|
|
|
|
n => "\xA", |
1577
|
|
|
|
|
|
|
f => "\xC", |
1578
|
|
|
|
|
|
|
r => "\xD", |
1579
|
|
|
|
|
|
|
'\\' => '\\', |
1580
|
|
|
|
|
|
|
'"' => '"', |
1581
|
|
|
|
|
|
|
'/' => '/', |
1582
|
|
|
|
|
|
|
); |
1583
|
|
|
|
|
|
|
|
1584
|
|
|
|
|
|
|
my $text; # json data |
1585
|
|
|
|
|
|
|
my $at; # offset |
1586
|
|
|
|
|
|
|
my $ch; # 1chracter |
1587
|
|
|
|
|
|
|
my $len; # text length (changed according to UTF8 or NON UTF8) |
1588
|
|
|
|
|
|
|
# INTERNAL |
1589
|
|
|
|
|
|
|
my $depth; # nest counter |
1590
|
|
|
|
|
|
|
my $encoding; # json text encoding |
1591
|
|
|
|
|
|
|
my $is_valid_utf8; # temp variable |
1592
|
|
|
|
|
|
|
my $utf8_len; # utf8 byte length |
1593
|
|
|
|
|
|
|
# FLAGS |
1594
|
|
|
|
|
|
|
my $utf8; # must be utf8 |
1595
|
|
|
|
|
|
|
my $max_depth; # max nest nubmer of objects and arrays |
1596
|
|
|
|
|
|
|
my $max_size; |
1597
|
|
|
|
|
|
|
my $relaxed; |
1598
|
|
|
|
|
|
|
my $cb_object; |
1599
|
|
|
|
|
|
|
my $cb_sk_object; |
1600
|
|
|
|
|
|
|
|
1601
|
|
|
|
|
|
|
my $F_HOOK; |
1602
|
|
|
|
|
|
|
|
1603
|
|
|
|
|
|
|
my $allow_bigint; # using Math::BigInt |
1604
|
|
|
|
|
|
|
my $singlequote; # loosely quoting |
1605
|
|
|
|
|
|
|
my $loose; # |
1606
|
|
|
|
|
|
|
my $allow_barekey; # bareKey |
1607
|
|
|
|
|
|
|
|
1608
|
|
|
|
|
|
|
# $opt flag |
1609
|
|
|
|
|
|
|
# 0x00000001 .... decode_prefix |
1610
|
|
|
|
|
|
|
# 0x10000000 .... incr_parse |
1611
|
|
|
|
|
|
|
|
1612
|
|
|
|
|
|
|
sub PP_decode_json { |
1613
|
0
|
|
|
0
|
|
0
|
my ($self, $opt); # $opt is an effective flag during this decode_json. |
1614
|
|
|
|
|
|
|
|
1615
|
0
|
|
|
|
|
0
|
($self, $text, $opt) = @_; |
1616
|
|
|
|
|
|
|
|
1617
|
0
|
|
|
|
|
0
|
($at, $ch, $depth) = (0, '', 0); |
1618
|
|
|
|
|
|
|
|
1619
|
0
|
0
|
0
|
|
|
0
|
if ( !defined $text or ref $text ) { |
1620
|
0
|
|
|
|
|
0
|
decode_error("malformed JSON string, neither array, object, number, string or atom"); |
1621
|
|
|
|
|
|
|
} |
1622
|
|
|
|
|
|
|
|
1623
|
0
|
|
|
|
|
0
|
my $idx = $self->{PROPS}; |
1624
|
|
|
|
|
|
|
|
1625
|
0
|
|
|
|
|
0
|
($utf8, $relaxed, $loose, $allow_bigint, $allow_barekey, $singlequote) |
1626
|
0
|
|
|
|
|
0
|
= @{$idx}[P_UTF8, P_RELAXED, P_LOOSE .. P_ALLOW_SINGLEQUOTE]; |
1627
|
|
|
|
|
|
|
|
1628
|
0
|
0
|
|
|
|
0
|
if ( $utf8 ) { |
1629
|
0
|
0
|
|
|
|
0
|
utf8::downgrade( $text, 1 ) or Carp::croak("Wide character in subroutine entry"); |
1630
|
|
|
|
|
|
|
} |
1631
|
|
|
|
|
|
|
else { |
1632
|
0
|
|
|
|
|
0
|
utf8::upgrade( $text ); |
1633
|
|
|
|
|
|
|
} |
1634
|
|
|
|
|
|
|
|
1635
|
0
|
|
|
|
|
0
|
$len = length $text; |
1636
|
|
|
|
|
|
|
|
1637
|
0
|
|
|
|
|
0
|
($max_depth, $max_size, $cb_object, $cb_sk_object, $F_HOOK) |
1638
|
0
|
|
|
|
|
0
|
= @{$self}{qw/max_depth max_size cb_object cb_sk_object F_HOOK/}; |
1639
|
|
|
|
|
|
|
|
1640
|
0
|
0
|
|
|
|
0
|
if ($max_size > 1) { |
1641
|
1
|
|
|
1
|
|
10
|
use bytes; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
9
|
|
1642
|
0
|
|
|
|
|
0
|
my $bytes = length $text; |
1643
|
0
|
0
|
|
|
|
0
|
decode_error( |
1644
|
|
|
|
|
|
|
sprintf("attempted decode of JSON text of %s bytes size, but max_size is set to %s" |
1645
|
|
|
|
|
|
|
, $bytes, $max_size), 1 |
1646
|
|
|
|
|
|
|
) if ($bytes > $max_size); |
1647
|
|
|
|
|
|
|
} |
1648
|
|
|
|
|
|
|
|
1649
|
|
|
|
|
|
|
# Currently no effect |
1650
|
|
|
|
|
|
|
# should use regexp |
1651
|
0
|
|
|
|
|
0
|
my @octets = unpack('C4', $text); |
1652
|
0
|
0
|
0
|
|
|
0
|
$encoding = ( $octets[0] and $octets[1]) ? 'UTF-8' |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1653
|
|
|
|
|
|
|
: (!$octets[0] and $octets[1]) ? 'UTF-16BE' |
1654
|
|
|
|
|
|
|
: (!$octets[0] and !$octets[1]) ? 'UTF-32BE' |
1655
|
|
|
|
|
|
|
: ( $octets[2] ) ? 'UTF-16LE' |
1656
|
|
|
|
|
|
|
: (!$octets[2] ) ? 'UTF-32LE' |
1657
|
|
|
|
|
|
|
: 'unknown'; |
1658
|
|
|
|
|
|
|
|
1659
|
0
|
|
|
|
|
0
|
white(); # remove head white space |
1660
|
|
|
|
|
|
|
|
1661
|
0
|
|
|
|
|
0
|
my $valid_start = defined $ch; # Is there a first character for JSON structure? |
1662
|
|
|
|
|
|
|
|
1663
|
0
|
|
|
|
|
0
|
my $result = value(); |
1664
|
|
|
|
|
|
|
|
1665
|
0
|
0
|
0
|
|
|
0
|
return undef if ( !$result && ( $opt & 0x10000000 ) ); # for incr_parse |
1666
|
|
|
|
|
|
|
|
1667
|
0
|
0
|
|
|
|
0
|
decode_error("malformed JSON string, neither array, object, number, string or atom") unless $valid_start; |
1668
|
|
|
|
|
|
|
|
1669
|
0
|
0
|
0
|
|
|
0
|
if ( !$idx->[ P_ALLOW_NONREF ] and !ref $result ) { |
1670
|
0
|
|
|
|
|
0
|
decode_error( |
1671
|
|
|
|
|
|
|
'JSON text must be an object or array (but found number, string, true, false or null,' |
1672
|
|
|
|
|
|
|
. ' use allow_nonref to allow this)', 1); |
1673
|
|
|
|
|
|
|
} |
1674
|
|
|
|
|
|
|
|
1675
|
0
|
0
|
|
|
|
0
|
Carp::croak('something wrong.') if $len < $at; # we won't arrive here. |
1676
|
|
|
|
|
|
|
|
1677
|
0
|
0
|
|
|
|
0
|
my $consumed = defined $ch ? $at - 1 : $at; # consumed JSON text length |
1678
|
|
|
|
|
|
|
|
1679
|
0
|
|
|
|
|
0
|
white(); # remove tail white space |
1680
|
|
|
|
|
|
|
|
1681
|
0
|
0
|
|
|
|
0
|
if ( $ch ) { |
1682
|
0
|
0
|
|
|
|
0
|
return ( $result, $consumed ) if ($opt & 0x00000001); # all right if decode_prefix |
1683
|
0
|
|
|
|
|
0
|
decode_error("garbage after JSON object"); |
1684
|
|
|
|
|
|
|
} |
1685
|
|
|
|
|
|
|
|
1686
|
0
|
0
|
|
|
|
0
|
( $opt & 0x00000001 ) ? ( $result, $consumed ) : $result; |
1687
|
|
|
|
|
|
|
} |
1688
|
|
|
|
|
|
|
|
1689
|
|
|
|
|
|
|
|
1690
|
|
|
|
|
|
|
sub next_chr { |
1691
|
0
|
0
|
|
0
|
|
0
|
return $ch = undef if($at >= $len); |
1692
|
0
|
|
|
|
|
0
|
$ch = substr($text, $at++, 1); |
1693
|
|
|
|
|
|
|
} |
1694
|
|
|
|
|
|
|
|
1695
|
|
|
|
|
|
|
|
1696
|
|
|
|
|
|
|
sub value { |
1697
|
0
|
|
|
0
|
|
0
|
white(); |
1698
|
0
|
0
|
|
|
|
0
|
return if(!defined $ch); |
1699
|
0
|
0
|
|
|
|
0
|
return object() if($ch eq '{'); |
1700
|
0
|
0
|
|
|
|
0
|
return array() if($ch eq '['); |
1701
|
0
|
0
|
0
|
|
|
0
|
return string() if($ch eq '"' or ($singlequote and $ch eq "'")); |
|
|
|
0
|
|
|
|
|
1702
|
0
|
0
|
0
|
|
|
0
|
return number() if($ch =~ /[0-9]/ or $ch eq '-'); |
1703
|
0
|
|
|
|
|
0
|
return word(); |
1704
|
|
|
|
|
|
|
} |
1705
|
|
|
|
|
|
|
|
1706
|
|
|
|
|
|
|
sub string { |
1707
|
0
|
|
|
0
|
|
0
|
my ($i, $s, $t, $u); |
1708
|
0
|
|
|
|
|
0
|
my $utf16; |
1709
|
0
|
|
|
|
|
0
|
my $is_utf8; |
1710
|
|
|
|
|
|
|
|
1711
|
0
|
|
|
|
|
0
|
($is_valid_utf8, $utf8_len) = ('', 0); |
1712
|
|
|
|
|
|
|
|
1713
|
0
|
|
|
|
|
0
|
$s = ''; # basically UTF8 flag on |
1714
|
|
|
|
|
|
|
|
1715
|
0
|
0
|
0
|
|
|
0
|
if($ch eq '"' or ($singlequote and $ch eq "'")){ |
|
|
|
0
|
|
|
|
|
1716
|
0
|
|
|
|
|
0
|
my $boundChar = $ch; |
1717
|
|
|
|
|
|
|
|
1718
|
0
|
|
|
|
|
0
|
OUTER: while( defined(next_chr()) ){ |
1719
|
|
|
|
|
|
|
|
1720
|
0
|
0
|
|
|
|
0
|
if($ch eq $boundChar){ |
|
|
0
|
|
|
|
|
|
1721
|
0
|
|
|
|
|
0
|
next_chr(); |
1722
|
|
|
|
|
|
|
|
1723
|
0
|
0
|
|
|
|
0
|
if ($utf16) { |
1724
|
0
|
|
|
|
|
0
|
decode_error("missing low surrogate character in surrogate pair"); |
1725
|
|
|
|
|
|
|
} |
1726
|
|
|
|
|
|
|
|
1727
|
0
|
0
|
|
|
|
0
|
utf8::decode($s) if($is_utf8); |
1728
|
|
|
|
|
|
|
|
1729
|
0
|
|
|
|
|
0
|
return $s; |
1730
|
|
|
|
|
|
|
} |
1731
|
|
|
|
|
|
|
elsif($ch eq '\\'){ |
1732
|
0
|
|
|
|
|
0
|
next_chr(); |
1733
|
0
|
0
|
|
|
|
0
|
if(exists $escapes{$ch}){ |
|
|
0
|
|
|
|
|
|
1734
|
0
|
|
|
|
|
0
|
$s .= $escapes{$ch}; |
1735
|
|
|
|
|
|
|
} |
1736
|
|
|
|
|
|
|
elsif($ch eq 'u'){ # UNICODE handling |
1737
|
0
|
|
|
|
|
0
|
my $u = ''; |
1738
|
|
|
|
|
|
|
|
1739
|
0
|
|
|
|
|
0
|
for(1..4){ |
1740
|
0
|
|
|
|
|
0
|
$ch = next_chr(); |
1741
|
0
|
0
|
|
|
|
0
|
last OUTER if($ch !~ /[0-9a-fA-F]/); |
1742
|
0
|
|
|
|
|
0
|
$u .= $ch; |
1743
|
|
|
|
|
|
|
} |
1744
|
|
|
|
|
|
|
|
1745
|
|
|
|
|
|
|
# U+D800 - U+DBFF |
1746
|
0
|
0
|
|
|
|
0
|
if ($u =~ /^[dD][89abAB][0-9a-fA-F]{2}/) { # UTF-16 high surrogate? |
|
|
0
|
|
|
|
|
|
1747
|
0
|
|
|
|
|
0
|
$utf16 = $u; |
1748
|
|
|
|
|
|
|
} |
1749
|
|
|
|
|
|
|
# U+DC00 - U+DFFF |
1750
|
|
|
|
|
|
|
elsif ($u =~ /^[dD][c-fC-F][0-9a-fA-F]{2}/) { # UTF-16 low surrogate? |
1751
|
0
|
0
|
|
|
|
0
|
unless (defined $utf16) { |
1752
|
0
|
|
|
|
|
0
|
decode_error("missing high surrogate character in surrogate pair"); |
1753
|
|
|
|
|
|
|
} |
1754
|
0
|
|
|
|
|
0
|
$is_utf8 = 1; |
1755
|
0
|
|
0
|
|
|
0
|
$s .= JSON_PP_decode_surrogates($utf16, $u) || next; |
1756
|
0
|
|
|
|
|
0
|
$utf16 = undef; |
1757
|
|
|
|
|
|
|
} |
1758
|
|
|
|
|
|
|
else { |
1759
|
0
|
0
|
|
|
|
0
|
if (defined $utf16) { |
1760
|
0
|
|
|
|
|
0
|
decode_error("surrogate pair expected"); |
1761
|
|
|
|
|
|
|
} |
1762
|
|
|
|
|
|
|
|
1763
|
0
|
0
|
|
|
|
0
|
if ( ( my $hex = hex( $u ) ) > 127 ) { |
1764
|
0
|
|
|
|
|
0
|
$is_utf8 = 1; |
1765
|
0
|
|
0
|
|
|
0
|
$s .= JSON_PP_decode_unicode($u) || next; |
1766
|
|
|
|
|
|
|
} |
1767
|
|
|
|
|
|
|
else { |
1768
|
0
|
|
|
|
|
0
|
$s .= chr $hex; |
1769
|
|
|
|
|
|
|
} |
1770
|
|
|
|
|
|
|
} |
1771
|
|
|
|
|
|
|
|
1772
|
|
|
|
|
|
|
} |
1773
|
|
|
|
|
|
|
else{ |
1774
|
0
|
0
|
|
|
|
0
|
unless ($loose) { |
1775
|
0
|
|
|
|
|
0
|
$at -= 2; |
1776
|
0
|
|
|
|
|
0
|
decode_error('illegal backslash escape sequence in string'); |
1777
|
|
|
|
|
|
|
} |
1778
|
0
|
|
|
|
|
0
|
$s .= $ch; |
1779
|
|
|
|
|
|
|
} |
1780
|
|
|
|
|
|
|
} |
1781
|
|
|
|
|
|
|
else{ |
1782
|
|
|
|
|
|
|
|
1783
|
0
|
0
|
|
|
|
0
|
if ( ord $ch > 127 ) { |
1784
|
0
|
0
|
|
|
|
0
|
if ( $utf8 ) { |
1785
|
0
|
0
|
|
|
|
0
|
unless( $ch = is_valid_utf8($ch) ) { |
1786
|
0
|
|
|
|
|
0
|
$at -= 1; |
1787
|
0
|
|
|
|
|
0
|
decode_error("malformed UTF-8 character in JSON string"); |
1788
|
|
|
|
|
|
|
} |
1789
|
|
|
|
|
|
|
else { |
1790
|
0
|
|
|
|
|
0
|
$at += $utf8_len - 1; |
1791
|
|
|
|
|
|
|
} |
1792
|
|
|
|
|
|
|
} |
1793
|
|
|
|
|
|
|
else { |
1794
|
0
|
|
|
|
|
0
|
utf8::encode( $ch ); |
1795
|
|
|
|
|
|
|
} |
1796
|
|
|
|
|
|
|
|
1797
|
0
|
|
|
|
|
0
|
$is_utf8 = 1; |
1798
|
|
|
|
|
|
|
} |
1799
|
|
|
|
|
|
|
|
1800
|
0
|
0
|
|
|
|
0
|
if (!$loose) { |
1801
|
0
|
0
|
|
|
|
0
|
if ($ch =~ /[\x00-\x1f\x22\x5c]/) { # '/' ok |
1802
|
0
|
|
|
|
|
0
|
$at--; |
1803
|
0
|
|
|
|
|
0
|
decode_error('invalid character encountered while parsing JSON string'); |
1804
|
|
|
|
|
|
|
} |
1805
|
|
|
|
|
|
|
} |
1806
|
|
|
|
|
|
|
|
1807
|
0
|
|
|
|
|
0
|
$s .= $ch; |
1808
|
|
|
|
|
|
|
} |
1809
|
|
|
|
|
|
|
} |
1810
|
|
|
|
|
|
|
} |
1811
|
|
|
|
|
|
|
|
1812
|
0
|
|
|
|
|
0
|
decode_error("unexpected end of string while parsing JSON string"); |
1813
|
|
|
|
|
|
|
} |
1814
|
|
|
|
|
|
|
|
1815
|
|
|
|
|
|
|
|
1816
|
|
|
|
|
|
|
sub white { |
1817
|
0
|
|
|
0
|
|
0
|
while( defined $ch ){ |
1818
|
0
|
0
|
|
|
|
0
|
if($ch le ' '){ |
|
|
0
|
|
|
|
|
|
1819
|
0
|
|
|
|
|
0
|
next_chr(); |
1820
|
|
|
|
|
|
|
} |
1821
|
|
|
|
|
|
|
elsif($ch eq '/'){ |
1822
|
0
|
|
|
|
|
0
|
next_chr(); |
1823
|
0
|
0
|
0
|
|
|
0
|
if(defined $ch and $ch eq '/'){ |
|
|
0
|
0
|
|
|
|
|
1824
|
0
|
|
0
|
|
|
0
|
1 while(defined(next_chr()) and $ch ne "\n" and $ch ne "\r"); |
|
|
|
0
|
|
|
|
|
1825
|
|
|
|
|
|
|
} |
1826
|
|
|
|
|
|
|
elsif(defined $ch and $ch eq '*'){ |
1827
|
0
|
|
|
|
|
0
|
next_chr(); |
1828
|
0
|
|
|
|
|
0
|
while(1){ |
1829
|
0
|
0
|
|
|
|
0
|
if(defined $ch){ |
1830
|
0
|
0
|
|
|
|
0
|
if($ch eq '*'){ |
1831
|
0
|
0
|
0
|
|
|
0
|
if(defined(next_chr()) and $ch eq '/'){ |
1832
|
0
|
|
|
|
|
0
|
next_chr(); |
1833
|
0
|
|
|
|
|
0
|
last; |
1834
|
|
|
|
|
|
|
} |
1835
|
|
|
|
|
|
|
} |
1836
|
|
|
|
|
|
|
else{ |
1837
|
0
|
|
|
|
|
0
|
next_chr(); |
1838
|
|
|
|
|
|
|
} |
1839
|
|
|
|
|
|
|
} |
1840
|
|
|
|
|
|
|
else{ |
1841
|
0
|
|
|
|
|
0
|
decode_error("Unterminated comment"); |
1842
|
|
|
|
|
|
|
} |
1843
|
|
|
|
|
|
|
} |
1844
|
0
|
|
|
|
|
0
|
next; |
1845
|
|
|
|
|
|
|
} |
1846
|
|
|
|
|
|
|
else{ |
1847
|
0
|
|
|
|
|
0
|
$at--; |
1848
|
0
|
|
|
|
|
0
|
decode_error("malformed JSON string, neither array, object, number, string or atom"); |
1849
|
|
|
|
|
|
|
} |
1850
|
|
|
|
|
|
|
} |
1851
|
|
|
|
|
|
|
else{ |
1852
|
0
|
0
|
0
|
|
|
0
|
if ($relaxed and $ch eq '#') { # correctly? |
1853
|
0
|
|
|
|
|
0
|
pos($text) = $at; |
1854
|
0
|
|
|
|
|
0
|
$text =~ /\G([^\n]*(?:\r\n|\r|\n|$))/g; |
1855
|
0
|
|
|
|
|
0
|
$at = pos($text); |
1856
|
0
|
|
|
|
|
0
|
next_chr; |
1857
|
0
|
|
|
|
|
0
|
next; |
1858
|
|
|
|
|
|
|
} |
1859
|
|
|
|
|
|
|
|
1860
|
0
|
|
|
|
|
0
|
last; |
1861
|
|
|
|
|
|
|
} |
1862
|
|
|
|
|
|
|
} |
1863
|
|
|
|
|
|
|
} |
1864
|
|
|
|
|
|
|
|
1865
|
|
|
|
|
|
|
|
1866
|
|
|
|
|
|
|
sub array { |
1867
|
0
|
|
0
|
0
|
|
0
|
my $a = $_[0] || []; # you can use this code to use another array ref object. |
1868
|
|
|
|
|
|
|
|
1869
|
0
|
0
|
|
|
|
0
|
decode_error('json text or perl structure exceeds maximum nesting level (max_depth set too low?)') |
1870
|
|
|
|
|
|
|
if (++$depth > $max_depth); |
1871
|
|
|
|
|
|
|
|
1872
|
0
|
|
|
|
|
0
|
next_chr(); |
1873
|
0
|
|
|
|
|
0
|
white(); |
1874
|
|
|
|
|
|
|
|
1875
|
0
|
0
|
0
|
|
|
0
|
if(defined $ch and $ch eq ']'){ |
1876
|
0
|
|
|
|
|
0
|
--$depth; |
1877
|
0
|
|
|
|
|
0
|
next_chr(); |
1878
|
0
|
|
|
|
|
0
|
return $a; |
1879
|
|
|
|
|
|
|
} |
1880
|
|
|
|
|
|
|
else { |
1881
|
0
|
|
|
|
|
0
|
while(defined($ch)){ |
1882
|
0
|
|
|
|
|
0
|
push @$a, value(); |
1883
|
|
|
|
|
|
|
|
1884
|
0
|
|
|
|
|
0
|
white(); |
1885
|
|
|
|
|
|
|
|
1886
|
0
|
0
|
|
|
|
0
|
if (!defined $ch) { |
1887
|
0
|
|
|
|
|
0
|
last; |
1888
|
|
|
|
|
|
|
} |
1889
|
|
|
|
|
|
|
|
1890
|
0
|
0
|
|
|
|
0
|
if($ch eq ']'){ |
1891
|
0
|
|
|
|
|
0
|
--$depth; |
1892
|
0
|
|
|
|
|
0
|
next_chr(); |
1893
|
0
|
|
|
|
|
0
|
return $a; |
1894
|
|
|
|
|
|
|
} |
1895
|
|
|
|
|
|
|
|
1896
|
0
|
0
|
|
|
|
0
|
if($ch ne ','){ |
1897
|
0
|
|
|
|
|
0
|
last; |
1898
|
|
|
|
|
|
|
} |
1899
|
|
|
|
|
|
|
|
1900
|
0
|
|
|
|
|
0
|
next_chr(); |
1901
|
0
|
|
|
|
|
0
|
white(); |
1902
|
|
|
|
|
|
|
|
1903
|
0
|
0
|
0
|
|
|
0
|
if ($relaxed and $ch eq ']') { |
1904
|
0
|
|
|
|
|
0
|
--$depth; |
1905
|
0
|
|
|
|
|
0
|
next_chr(); |
1906
|
0
|
|
|
|
|
0
|
return $a; |
1907
|
|
|
|
|
|
|
} |
1908
|
|
|
|
|
|
|
|
1909
|
|
|
|
|
|
|
} |
1910
|
|
|
|
|
|
|
} |
1911
|
|
|
|
|
|
|
|
1912
|
0
|
|
|
|
|
0
|
decode_error(", or ] expected while parsing array"); |
1913
|
|
|
|
|
|
|
} |
1914
|
|
|
|
|
|
|
|
1915
|
|
|
|
|
|
|
|
1916
|
|
|
|
|
|
|
sub object { |
1917
|
0
|
|
0
|
0
|
|
0
|
my $o = $_[0] || {}; # you can use this code to use another hash ref object. |
1918
|
0
|
|
|
|
|
0
|
my $k; |
1919
|
|
|
|
|
|
|
|
1920
|
0
|
0
|
|
|
|
0
|
decode_error('json text or perl structure exceeds maximum nesting level (max_depth set too low?)') |
1921
|
|
|
|
|
|
|
if (++$depth > $max_depth); |
1922
|
0
|
|
|
|
|
0
|
next_chr(); |
1923
|
0
|
|
|
|
|
0
|
white(); |
1924
|
|
|
|
|
|
|
|
1925
|
0
|
0
|
0
|
|
|
0
|
if(defined $ch and $ch eq '}'){ |
1926
|
0
|
|
|
|
|
0
|
--$depth; |
1927
|
0
|
|
|
|
|
0
|
next_chr(); |
1928
|
0
|
0
|
|
|
|
0
|
if ($F_HOOK) { |
1929
|
0
|
|
|
|
|
0
|
return _json_object_hook($o); |
1930
|
|
|
|
|
|
|
} |
1931
|
0
|
|
|
|
|
0
|
return $o; |
1932
|
|
|
|
|
|
|
} |
1933
|
|
|
|
|
|
|
else { |
1934
|
0
|
|
|
|
|
0
|
while (defined $ch) { |
1935
|
0
|
0
|
0
|
|
|
0
|
$k = ($allow_barekey and $ch ne '"' and $ch ne "'") ? bareKey() : string(); |
1936
|
0
|
|
|
|
|
0
|
white(); |
1937
|
|
|
|
|
|
|
|
1938
|
0
|
0
|
0
|
|
|
0
|
if(!defined $ch or $ch ne ':'){ |
1939
|
0
|
|
|
|
|
0
|
$at--; |
1940
|
0
|
|
|
|
|
0
|
decode_error("':' expected"); |
1941
|
|
|
|
|
|
|
} |
1942
|
|
|
|
|
|
|
|
1943
|
0
|
|
|
|
|
0
|
next_chr(); |
1944
|
0
|
|
|
|
|
0
|
$o->{$k} = value(); |
1945
|
0
|
|
|
|
|
0
|
white(); |
1946
|
|
|
|
|
|
|
|
1947
|
0
|
0
|
|
|
|
0
|
last if (!defined $ch); |
1948
|
|
|
|
|
|
|
|
1949
|
0
|
0
|
|
|
|
0
|
if($ch eq '}'){ |
1950
|
0
|
|
|
|
|
0
|
--$depth; |
1951
|
0
|
|
|
|
|
0
|
next_chr(); |
1952
|
0
|
0
|
|
|
|
0
|
if ($F_HOOK) { |
1953
|
0
|
|
|
|
|
0
|
return _json_object_hook($o); |
1954
|
|
|
|
|
|
|
} |
1955
|
0
|
|
|
|
|
0
|
return $o; |
1956
|
|
|
|
|
|
|
} |
1957
|
|
|
|
|
|
|
|
1958
|
0
|
0
|
|
|
|
0
|
if($ch ne ','){ |
1959
|
0
|
|
|
|
|
0
|
last; |
1960
|
|
|
|
|
|
|
} |
1961
|
|
|
|
|
|
|
|
1962
|
0
|
|
|
|
|
0
|
next_chr(); |
1963
|
0
|
|
|
|
|
0
|
white(); |
1964
|
|
|
|
|
|
|
|
1965
|
0
|
0
|
0
|
|
|
0
|
if ($relaxed and $ch eq '}') { |
1966
|
0
|
|
|
|
|
0
|
--$depth; |
1967
|
0
|
|
|
|
|
0
|
next_chr(); |
1968
|
0
|
0
|
|
|
|
0
|
if ($F_HOOK) { |
1969
|
0
|
|
|
|
|
0
|
return _json_object_hook($o); |
1970
|
|
|
|
|
|
|
} |
1971
|
0
|
|
|
|
|
0
|
return $o; |
1972
|
|
|
|
|
|
|
} |
1973
|
|
|
|
|
|
|
|
1974
|
|
|
|
|
|
|
} |
1975
|
|
|
|
|
|
|
|
1976
|
|
|
|
|
|
|
} |
1977
|
|
|
|
|
|
|
|
1978
|
0
|
|
|
|
|
0
|
$at--; |
1979
|
0
|
|
|
|
|
0
|
decode_error(", or } expected while parsing object/hash"); |
1980
|
|
|
|
|
|
|
} |
1981
|
|
|
|
|
|
|
|
1982
|
|
|
|
|
|
|
|
1983
|
|
|
|
|
|
|
sub bareKey { # doesn't strictly follow Standard ECMA-262 3rd Edition |
1984
|
0
|
|
|
0
|
|
0
|
my $key; |
1985
|
0
|
|
|
|
|
0
|
while($ch =~ /[^\x00-\x23\x25-\x2F\x3A-\x40\x5B-\x5E\x60\x7B-\x7F]/){ |
1986
|
0
|
|
|
|
|
0
|
$key .= $ch; |
1987
|
0
|
|
|
|
|
0
|
next_chr(); |
1988
|
|
|
|
|
|
|
} |
1989
|
0
|
|
|
|
|
0
|
return $key; |
1990
|
|
|
|
|
|
|
} |
1991
|
|
|
|
|
|
|
|
1992
|
|
|
|
|
|
|
|
1993
|
|
|
|
|
|
|
sub word { |
1994
|
0
|
|
|
0
|
|
0
|
my $word = substr($text,$at-1,4); |
1995
|
|
|
|
|
|
|
|
1996
|
0
|
0
|
|
|
|
0
|
if($word eq 'true'){ |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1997
|
0
|
|
|
|
|
0
|
$at += 3; |
1998
|
0
|
|
|
|
|
0
|
next_chr; |
1999
|
0
|
|
|
|
|
0
|
return $Test::ModuleVersion::JSON::PP::true; |
2000
|
|
|
|
|
|
|
} |
2001
|
|
|
|
|
|
|
elsif($word eq 'null'){ |
2002
|
0
|
|
|
|
|
0
|
$at += 3; |
2003
|
0
|
|
|
|
|
0
|
next_chr; |
2004
|
0
|
|
|
|
|
0
|
return undef; |
2005
|
|
|
|
|
|
|
} |
2006
|
|
|
|
|
|
|
elsif($word eq 'fals'){ |
2007
|
0
|
|
|
|
|
0
|
$at += 3; |
2008
|
0
|
0
|
|
|
|
0
|
if(substr($text,$at,1) eq 'e'){ |
2009
|
0
|
|
|
|
|
0
|
$at++; |
2010
|
0
|
|
|
|
|
0
|
next_chr; |
2011
|
0
|
|
|
|
|
0
|
return $Test::ModuleVersion::JSON::PP::false; |
2012
|
|
|
|
|
|
|
} |
2013
|
|
|
|
|
|
|
} |
2014
|
|
|
|
|
|
|
|
2015
|
0
|
|
|
|
|
0
|
$at--; # for decode_error report |
2016
|
|
|
|
|
|
|
|
2017
|
0
|
0
|
|
|
|
0
|
decode_error("'null' expected") if ($word =~ /^n/); |
2018
|
0
|
0
|
|
|
|
0
|
decode_error("'true' expected") if ($word =~ /^t/); |
2019
|
0
|
0
|
|
|
|
0
|
decode_error("'false' expected") if ($word =~ /^f/); |
2020
|
0
|
|
|
|
|
0
|
decode_error("malformed JSON string, neither array, object, number, string or atom"); |
2021
|
|
|
|
|
|
|
} |
2022
|
|
|
|
|
|
|
|
2023
|
|
|
|
|
|
|
|
2024
|
|
|
|
|
|
|
sub number { |
2025
|
0
|
|
|
0
|
|
0
|
my $n = ''; |
2026
|
0
|
|
|
|
|
0
|
my $v; |
2027
|
|
|
|
|
|
|
|
2028
|
|
|
|
|
|
|
# According to RFC4627, hex or oct digts are invalid. |
2029
|
0
|
0
|
|
|
|
0
|
if($ch eq '0'){ |
2030
|
0
|
|
|
|
|
0
|
my $peek = substr($text,$at,1); |
2031
|
0
|
|
|
|
|
0
|
my $hex = $peek =~ /[xX]/; # 0 or 1 |
2032
|
|
|
|
|
|
|
|
2033
|
0
|
0
|
|
|
|
0
|
if($hex){ |
2034
|
0
|
|
|
|
|
0
|
decode_error("malformed number (leading zero must not be followed by another digit)"); |
2035
|
0
|
|
|
|
|
0
|
($n) = ( substr($text, $at+1) =~ /^([0-9a-fA-F]+)/); |
2036
|
|
|
|
|
|
|
} |
2037
|
|
|
|
|
|
|
else{ # oct |
2038
|
0
|
|
|
|
|
0
|
($n) = ( substr($text, $at) =~ /^([0-7]+)/); |
2039
|
0
|
0
|
0
|
|
|
0
|
if (defined $n and length $n > 1) { |
2040
|
0
|
|
|
|
|
0
|
decode_error("malformed number (leading zero must not be followed by another digit)"); |
2041
|
|
|
|
|
|
|
} |
2042
|
|
|
|
|
|
|
} |
2043
|
|
|
|
|
|
|
|
2044
|
0
|
0
|
0
|
|
|
0
|
if(defined $n and length($n)){ |
2045
|
0
|
0
|
0
|
|
|
0
|
if (!$hex and length($n) == 1) { |
2046
|
0
|
|
|
|
|
0
|
decode_error("malformed number (leading zero must not be followed by another digit)"); |
2047
|
|
|
|
|
|
|
} |
2048
|
0
|
|
|
|
|
0
|
$at += length($n) + $hex; |
2049
|
0
|
|
|
|
|
0
|
next_chr; |
2050
|
0
|
0
|
|
|
|
0
|
return $hex ? hex($n) : oct($n); |
2051
|
|
|
|
|
|
|
} |
2052
|
|
|
|
|
|
|
} |
2053
|
|
|
|
|
|
|
|
2054
|
0
|
0
|
|
|
|
0
|
if($ch eq '-'){ |
2055
|
0
|
|
|
|
|
0
|
$n = '-'; |
2056
|
0
|
|
|
|
|
0
|
next_chr; |
2057
|
0
|
0
|
0
|
|
|
0
|
if (!defined $ch or $ch !~ /\d/) { |
2058
|
0
|
|
|
|
|
0
|
decode_error("malformed number (no digits after initial minus)"); |
2059
|
|
|
|
|
|
|
} |
2060
|
|
|
|
|
|
|
} |
2061
|
|
|
|
|
|
|
|
2062
|
0
|
|
0
|
|
|
0
|
while(defined $ch and $ch =~ /\d/){ |
2063
|
0
|
|
|
|
|
0
|
$n .= $ch; |
2064
|
0
|
|
|
|
|
0
|
next_chr; |
2065
|
|
|
|
|
|
|
} |
2066
|
|
|
|
|
|
|
|
2067
|
0
|
0
|
0
|
|
|
0
|
if(defined $ch and $ch eq '.'){ |
2068
|
0
|
|
|
|
|
0
|
$n .= '.'; |
2069
|
|
|
|
|
|
|
|
2070
|
0
|
|
|
|
|
0
|
next_chr; |
2071
|
0
|
0
|
0
|
|
|
0
|
if (!defined $ch or $ch !~ /\d/) { |
2072
|
0
|
|
|
|
|
0
|
decode_error("malformed number (no digits after decimal point)"); |
2073
|
|
|
|
|
|
|
} |
2074
|
|
|
|
|
|
|
else { |
2075
|
0
|
|
|
|
|
0
|
$n .= $ch; |
2076
|
|
|
|
|
|
|
} |
2077
|
|
|
|
|
|
|
|
2078
|
0
|
|
0
|
|
|
0
|
while(defined(next_chr) and $ch =~ /\d/){ |
2079
|
0
|
|
|
|
|
0
|
$n .= $ch; |
2080
|
|
|
|
|
|
|
} |
2081
|
|
|
|
|
|
|
} |
2082
|
|
|
|
|
|
|
|
2083
|
0
|
0
|
0
|
|
|
0
|
if(defined $ch and ($ch eq 'e' or $ch eq 'E')){ |
|
|
|
0
|
|
|
|
|
2084
|
0
|
|
|
|
|
0
|
$n .= $ch; |
2085
|
0
|
|
|
|
|
0
|
next_chr; |
2086
|
|
|
|
|
|
|
|
2087
|
0
|
0
|
0
|
|
|
0
|
if(defined($ch) and ($ch eq '+' or $ch eq '-')){ |
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
2088
|
0
|
|
|
|
|
0
|
$n .= $ch; |
2089
|
0
|
|
|
|
|
0
|
next_chr; |
2090
|
0
|
0
|
0
|
|
|
0
|
if (!defined $ch or $ch =~ /\D/) { |
2091
|
0
|
|
|
|
|
0
|
decode_error("malformed number (no digits after exp sign)"); |
2092
|
|
|
|
|
|
|
} |
2093
|
0
|
|
|
|
|
0
|
$n .= $ch; |
2094
|
|
|
|
|
|
|
} |
2095
|
|
|
|
|
|
|
elsif(defined($ch) and $ch =~ /\d/){ |
2096
|
0
|
|
|
|
|
0
|
$n .= $ch; |
2097
|
|
|
|
|
|
|
} |
2098
|
|
|
|
|
|
|
else { |
2099
|
0
|
|
|
|
|
0
|
decode_error("malformed number (no digits after exp sign)"); |
2100
|
|
|
|
|
|
|
} |
2101
|
|
|
|
|
|
|
|
2102
|
0
|
|
0
|
|
|
0
|
while(defined(next_chr) and $ch =~ /\d/){ |
2103
|
0
|
|
|
|
|
0
|
$n .= $ch; |
2104
|
|
|
|
|
|
|
} |
2105
|
|
|
|
|
|
|
|
2106
|
|
|
|
|
|
|
} |
2107
|
|
|
|
|
|
|
|
2108
|
0
|
|
|
|
|
0
|
$v .= $n; |
2109
|
|
|
|
|
|
|
|
2110
|
0
|
0
|
0
|
|
|
0
|
if ($v !~ /[.eE]/ and length $v > $max_intsize) { |
|
|
0
|
|
|
|
|
|
2111
|
0
|
0
|
|
|
|
0
|
if ($allow_bigint) { # from Adam Sussman |
2112
|
0
|
|
|
|
|
0
|
require Math::BigInt; |
2113
|
0
|
|
|
|
|
0
|
return Math::BigInt->new($v); |
2114
|
|
|
|
|
|
|
} |
2115
|
|
|
|
|
|
|
else { |
2116
|
0
|
|
|
|
|
0
|
return "$v"; |
2117
|
|
|
|
|
|
|
} |
2118
|
|
|
|
|
|
|
} |
2119
|
|
|
|
|
|
|
elsif ($allow_bigint) { |
2120
|
0
|
|
|
|
|
0
|
require Math::BigFloat; |
2121
|
0
|
|
|
|
|
0
|
return Math::BigFloat->new($v); |
2122
|
|
|
|
|
|
|
} |
2123
|
|
|
|
|
|
|
|
2124
|
0
|
|
|
|
|
0
|
return 0+$v; |
2125
|
|
|
|
|
|
|
} |
2126
|
|
|
|
|
|
|
|
2127
|
|
|
|
|
|
|
|
2128
|
|
|
|
|
|
|
sub is_valid_utf8 { |
2129
|
|
|
|
|
|
|
|
2130
|
0
|
0
|
|
0
|
|
0
|
$utf8_len = $_[0] =~ /[\x00-\x7F]/ ? 1 |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
2131
|
|
|
|
|
|
|
: $_[0] =~ /[\xC2-\xDF]/ ? 2 |
2132
|
|
|
|
|
|
|
: $_[0] =~ /[\xE0-\xEF]/ ? 3 |
2133
|
|
|
|
|
|
|
: $_[0] =~ /[\xF0-\xF4]/ ? 4 |
2134
|
|
|
|
|
|
|
: 0 |
2135
|
|
|
|
|
|
|
; |
2136
|
|
|
|
|
|
|
|
2137
|
0
|
0
|
|
|
|
0
|
return unless $utf8_len; |
2138
|
|
|
|
|
|
|
|
2139
|
0
|
|
|
|
|
0
|
my $is_valid_utf8 = substr($text, $at - 1, $utf8_len); |
2140
|
|
|
|
|
|
|
|
2141
|
0
|
0
|
|
|
|
0
|
return ( $is_valid_utf8 =~ /^(?: |
2142
|
|
|
|
|
|
|
[\x00-\x7F] |
2143
|
|
|
|
|
|
|
|[\xC2-\xDF][\x80-\xBF] |
2144
|
|
|
|
|
|
|
|[\xE0][\xA0-\xBF][\x80-\xBF] |
2145
|
|
|
|
|
|
|
|[\xE1-\xEC][\x80-\xBF][\x80-\xBF] |
2146
|
|
|
|
|
|
|
|[\xED][\x80-\x9F][\x80-\xBF] |
2147
|
|
|
|
|
|
|
|[\xEE-\xEF][\x80-\xBF][\x80-\xBF] |
2148
|
|
|
|
|
|
|
|[\xF0][\x90-\xBF][\x80-\xBF][\x80-\xBF] |
2149
|
|
|
|
|
|
|
|[\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF] |
2150
|
|
|
|
|
|
|
|[\xF4][\x80-\x8F][\x80-\xBF][\x80-\xBF] |
2151
|
|
|
|
|
|
|
)$/x ) ? $is_valid_utf8 : ''; |
2152
|
|
|
|
|
|
|
} |
2153
|
|
|
|
|
|
|
|
2154
|
|
|
|
|
|
|
|
2155
|
|
|
|
|
|
|
sub decode_error { |
2156
|
0
|
|
|
0
|
|
0
|
my $error = shift; |
2157
|
0
|
|
|
|
|
0
|
my $no_rep = shift; |
2158
|
0
|
0
|
|
|
|
0
|
my $str = defined $text ? substr($text, $at) : ''; |
2159
|
0
|
|
|
|
|
0
|
my $mess = ''; |
2160
|
0
|
0
|
|
|
|
0
|
my $type = $] >= 5.008 ? 'U*' |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
2161
|
|
|
|
|
|
|
: $] < 5.006 ? 'C*' |
2162
|
|
|
|
|
|
|
: utf8::is_utf8( $str ) ? 'U*' # 5.6 |
2163
|
|
|
|
|
|
|
: 'C*' |
2164
|
|
|
|
|
|
|
; |
2165
|
|
|
|
|
|
|
|
2166
|
0
|
|
|
|
|
0
|
for my $c ( unpack( $type, $str ) ) { # emulate pv_uni_display() ? |
2167
|
0
|
0
|
|
|
|
0
|
$mess .= $c == 0x07 ? '\a' |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
2168
|
|
|
|
|
|
|
: $c == 0x09 ? '\t' |
2169
|
|
|
|
|
|
|
: $c == 0x0a ? '\n' |
2170
|
|
|
|
|
|
|
: $c == 0x0d ? '\r' |
2171
|
|
|
|
|
|
|
: $c == 0x0c ? '\f' |
2172
|
|
|
|
|
|
|
: $c < 0x20 ? sprintf('\x{%x}', $c) |
2173
|
|
|
|
|
|
|
: $c == 0x5c ? '\\\\' |
2174
|
|
|
|
|
|
|
: $c < 0x80 ? chr($c) |
2175
|
|
|
|
|
|
|
: sprintf('\x{%x}', $c) |
2176
|
|
|
|
|
|
|
; |
2177
|
0
|
0
|
|
|
|
0
|
if ( length $mess >= 20 ) { |
2178
|
0
|
|
|
|
|
0
|
$mess .= '...'; |
2179
|
0
|
|
|
|
|
0
|
last; |
2180
|
|
|
|
|
|
|
} |
2181
|
|
|
|
|
|
|
} |
2182
|
|
|
|
|
|
|
|
2183
|
0
|
0
|
|
|
|
0
|
unless ( length $mess ) { |
2184
|
0
|
|
|
|
|
0
|
$mess = '(end of string)'; |
2185
|
|
|
|
|
|
|
} |
2186
|
|
|
|
|
|
|
|
2187
|
|
|
|
|
|
|
Carp::croak ( |
2188
|
0
|
0
|
|
|
|
0
|
$no_rep ? "$error" : "$error, at character offset $at (before \"$mess\")" |
2189
|
|
|
|
|
|
|
); |
2190
|
|
|
|
|
|
|
|
2191
|
|
|
|
|
|
|
} |
2192
|
|
|
|
|
|
|
|
2193
|
|
|
|
|
|
|
|
2194
|
|
|
|
|
|
|
sub _json_object_hook { |
2195
|
0
|
|
|
0
|
|
0
|
my $o = $_[0]; |
2196
|
0
|
|
|
|
|
0
|
my @ks = keys %{$o}; |
|
0
|
|
|
|
|
0
|
|
2197
|
|
|
|
|
|
|
|
2198
|
0
|
0
|
0
|
|
|
0
|
if ( $cb_sk_object and @ks == 1 and exists $cb_sk_object->{ $ks[0] } and ref $cb_sk_object->{ $ks[0] } ) { |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
2199
|
0
|
|
|
|
|
0
|
my @val = $cb_sk_object->{ $ks[0] }->( $o->{$ks[0]} ); |
2200
|
0
|
0
|
|
|
|
0
|
if (@val == 1) { |
2201
|
0
|
|
|
|
|
0
|
return $val[0]; |
2202
|
|
|
|
|
|
|
} |
2203
|
|
|
|
|
|
|
} |
2204
|
|
|
|
|
|
|
|
2205
|
0
|
0
|
|
|
|
0
|
my @val = $cb_object->($o) if ($cb_object); |
2206
|
0
|
0
|
0
|
|
|
0
|
if (@val == 0 or @val > 1) { |
2207
|
0
|
|
|
|
|
0
|
return $o; |
2208
|
|
|
|
|
|
|
} |
2209
|
|
|
|
|
|
|
else { |
2210
|
0
|
|
|
|
|
0
|
return $val[0]; |
2211
|
|
|
|
|
|
|
} |
2212
|
|
|
|
|
|
|
} |
2213
|
|
|
|
|
|
|
|
2214
|
|
|
|
|
|
|
|
2215
|
|
|
|
|
|
|
sub PP_decode_box { |
2216
|
|
|
|
|
|
|
{ |
2217
|
0
|
|
|
0
|
|
0
|
text => $text, |
2218
|
|
|
|
|
|
|
at => $at, |
2219
|
|
|
|
|
|
|
ch => $ch, |
2220
|
|
|
|
|
|
|
len => $len, |
2221
|
|
|
|
|
|
|
depth => $depth, |
2222
|
|
|
|
|
|
|
encoding => $encoding, |
2223
|
|
|
|
|
|
|
is_valid_utf8 => $is_valid_utf8, |
2224
|
|
|
|
|
|
|
}; |
2225
|
|
|
|
|
|
|
} |
2226
|
|
|
|
|
|
|
|
2227
|
|
|
|
|
|
|
} # PARSE |
2228
|
|
|
|
|
|
|
|
2229
|
|
|
|
|
|
|
|
2230
|
|
|
|
|
|
|
sub _decode_surrogates { # from perlunicode |
2231
|
0
|
|
|
0
|
|
0
|
my $uni = 0x10000 + (hex($_[0]) - 0xD800) * 0x400 + (hex($_[1]) - 0xDC00); |
2232
|
0
|
|
|
|
|
0
|
my $un = pack('U*', $uni); |
2233
|
0
|
|
|
|
|
0
|
utf8::encode( $un ); |
2234
|
0
|
|
|
|
|
0
|
return $un; |
2235
|
|
|
|
|
|
|
} |
2236
|
|
|
|
|
|
|
|
2237
|
|
|
|
|
|
|
|
2238
|
|
|
|
|
|
|
sub _decode_unicode { |
2239
|
0
|
|
|
0
|
|
0
|
my $un = pack('U', hex shift); |
2240
|
0
|
|
|
|
|
0
|
utf8::encode( $un ); |
2241
|
0
|
|
|
|
|
0
|
return $un; |
2242
|
|
|
|
|
|
|
} |
2243
|
|
|
|
|
|
|
|
2244
|
|
|
|
|
|
|
# |
2245
|
|
|
|
|
|
|
# Setup for various Perl versions (the code from Test::ModuleVersion::JSON::PP58) |
2246
|
|
|
|
|
|
|
# |
2247
|
|
|
|
|
|
|
|
2248
|
|
|
|
|
|
|
BEGIN { |
2249
|
|
|
|
|
|
|
|
2250
|
1
|
50
|
|
1
|
|
5052
|
unless ( defined &utf8::is_utf8 ) { |
2251
|
0
|
|
|
|
|
0
|
require Encode; |
2252
|
0
|
|
|
|
|
0
|
*utf8::is_utf8 = *Encode::is_utf8; |
2253
|
|
|
|
|
|
|
} |
2254
|
|
|
|
|
|
|
|
2255
|
1
|
50
|
|
|
|
5
|
if ( $] >= 5.008 ) { |
2256
|
1
|
|
|
|
|
4
|
*Test::ModuleVersion::JSON::PP::JSON_PP_encode_ascii = \&_encode_ascii; |
2257
|
1
|
|
|
|
|
4
|
*Test::ModuleVersion::JSON::PP::JSON_PP_encode_latin1 = \&_encode_latin1; |
2258
|
1
|
|
|
|
|
2
|
*Test::ModuleVersion::JSON::PP::JSON_PP_decode_surrogates = \&_decode_surrogates; |
2259
|
1
|
|
|
|
|
8
|
*Test::ModuleVersion::JSON::PP::JSON_PP_decode_unicode = \&_decode_unicode; |
2260
|
|
|
|
|
|
|
} |
2261
|
|
|
|
|
|
|
|
2262
|
1
|
50
|
33
|
|
|
16
|
if ($] >= 5.008 and $] < 5.008003) { # join() in 5.8.0 - 5.8.2 is broken. |
2263
|
|
|
|
|
|
|
package Test::ModuleVersion::JSON::PP; |
2264
|
0
|
|
|
|
|
0
|
require subs; |
2265
|
0
|
|
|
|
|
0
|
subs->import('join'); |
2266
|
0
|
|
|
|
|
0
|
eval q| |
2267
|
|
|
|
|
|
|
sub join { |
2268
|
|
|
|
|
|
|
return '' if (@_ < 2); |
2269
|
|
|
|
|
|
|
my $j = shift; |
2270
|
|
|
|
|
|
|
my $str = shift; |
2271
|
|
|
|
|
|
|
for (@_) { $str .= $j . $_; } |
2272
|
|
|
|
|
|
|
return $str; |
2273
|
|
|
|
|
|
|
} |
2274
|
|
|
|
|
|
|
|; |
2275
|
|
|
|
|
|
|
} |
2276
|
|
|
|
|
|
|
|
2277
|
|
|
|
|
|
|
|
2278
|
|
|
|
|
|
|
sub Test::ModuleVersion::JSON::PP::incr_parse { |
2279
|
0
|
|
|
0
|
|
0
|
local $Carp::CarpLevel = 1; |
2280
|
0
|
|
0
|
|
|
0
|
( $_[0]->{_incr_parser} ||= Test::ModuleVersion::JSON::PP::IncrParser->new )->incr_parse( @_ ); |
2281
|
|
|
|
|
|
|
} |
2282
|
|
|
|
|
|
|
|
2283
|
|
|
|
|
|
|
|
2284
|
|
|
|
|
|
|
sub Test::ModuleVersion::JSON::PP::incr_skip { |
2285
|
0
|
|
0
|
0
|
|
0
|
( $_[0]->{_incr_parser} ||= Test::ModuleVersion::JSON::PP::IncrParser->new )->incr_skip; |
2286
|
|
|
|
|
|
|
} |
2287
|
|
|
|
|
|
|
|
2288
|
|
|
|
|
|
|
|
2289
|
|
|
|
|
|
|
sub Test::ModuleVersion::JSON::PP::incr_reset { |
2290
|
0
|
|
0
|
0
|
|
0
|
( $_[0]->{_incr_parser} ||= Test::ModuleVersion::JSON::PP::IncrParser->new )->incr_reset; |
2291
|
|
|
|
|
|
|
} |
2292
|
|
|
|
|
|
|
|
2293
|
1
|
50
|
0
|
0
|
|
626
|
eval q{ |
|
0
|
0
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
2294
|
|
|
|
|
|
|
sub Test::ModuleVersion::JSON::PP::incr_text : lvalue { |
2295
|
|
|
|
|
|
|
$_[0]->{_incr_parser} ||= Test::ModuleVersion::JSON::PP::IncrParser->new; |
2296
|
|
|
|
|
|
|
|
2297
|
|
|
|
|
|
|
if ( $_[0]->{_incr_parser}->{incr_parsing} ) { |
2298
|
|
|
|
|
|
|
Carp::croak("incr_text can not be called when the incremental parser already started parsing"); |
2299
|
|
|
|
|
|
|
} |
2300
|
|
|
|
|
|
|
$_[0]->{_incr_parser}->{incr_text}; |
2301
|
|
|
|
|
|
|
} |
2302
|
|
|
|
|
|
|
} if ( $] >= 5.006 ); |
2303
|
|
|
|
|
|
|
|
2304
|
|
|
|
|
|
|
} # Setup for various Perl versions (the code from Test::ModuleVersion::JSON::PP58) |
2305
|
|
|
|
|
|
|
|
2306
|
|
|
|
|
|
|
|
2307
|
|
|
|
|
|
|
############################### |
2308
|
|
|
|
|
|
|
# Utilities |
2309
|
|
|
|
|
|
|
# |
2310
|
|
|
|
|
|
|
|
2311
|
|
|
|
|
|
|
BEGIN { |
2312
|
1
|
|
|
1
|
|
76
|
eval 'require Scalar::Util'; |
2313
|
1
|
50
|
|
|
|
6
|
unless($@){ |
2314
|
1
|
|
|
|
|
3
|
*Test::ModuleVersion::JSON::PP::blessed = \&Scalar::Util::blessed; |
2315
|
1
|
|
|
|
|
3
|
*Test::ModuleVersion::JSON::PP::reftype = \&Scalar::Util::reftype; |
2316
|
1
|
|
|
|
|
321
|
*Test::ModuleVersion::JSON::PP::refaddr = \&Scalar::Util::refaddr; |
2317
|
|
|
|
|
|
|
} |
2318
|
|
|
|
|
|
|
else{ # This code is from Sclar::Util. |
2319
|
|
|
|
|
|
|
# warn $@; |
2320
|
0
|
|
|
|
|
0
|
eval 'sub UNIVERSAL::a_sub_not_likely_to_be_here { ref($_[0]) }'; |
2321
|
|
|
|
|
|
|
*Test::ModuleVersion::JSON::PP::blessed = sub { |
2322
|
0
|
|
|
|
|
0
|
local($@, $SIG{__DIE__}, $SIG{__WARN__}); |
2323
|
0
|
0
|
|
|
|
0
|
ref($_[0]) ? eval { $_[0]->a_sub_not_likely_to_be_here } : undef; |
|
0
|
|
|
|
|
0
|
|
2324
|
0
|
|
|
|
|
0
|
}; |
2325
|
0
|
|
|
|
|
0
|
my %tmap = qw( |
2326
|
|
|
|
|
|
|
B::NULL SCALAR |
2327
|
|
|
|
|
|
|
B::HV HASH |
2328
|
|
|
|
|
|
|
B::AV ARRAY |
2329
|
|
|
|
|
|
|
B::CV CODE |
2330
|
|
|
|
|
|
|
B::IO IO |
2331
|
|
|
|
|
|
|
B::GV GLOB |
2332
|
|
|
|
|
|
|
B::REGEXP REGEXP |
2333
|
|
|
|
|
|
|
); |
2334
|
|
|
|
|
|
|
*Test::ModuleVersion::JSON::PP::reftype = sub { |
2335
|
0
|
|
|
|
|
0
|
my $r = shift; |
2336
|
|
|
|
|
|
|
|
2337
|
0
|
0
|
|
|
|
0
|
return undef unless length(ref($r)); |
2338
|
|
|
|
|
|
|
|
2339
|
0
|
|
|
|
|
0
|
my $t = ref(B::svref_2object($r)); |
2340
|
|
|
|
|
|
|
|
2341
|
|
|
|
|
|
|
return |
2342
|
0
|
0
|
|
|
|
0
|
exists $tmap{$t} ? $tmap{$t} |
|
|
0
|
|
|
|
|
|
2343
|
|
|
|
|
|
|
: length(ref($$r)) ? 'REF' |
2344
|
|
|
|
|
|
|
: 'SCALAR'; |
2345
|
0
|
|
|
|
|
0
|
}; |
2346
|
|
|
|
|
|
|
*Test::ModuleVersion::JSON::PP::refaddr = sub { |
2347
|
0
|
0
|
|
|
|
0
|
return undef unless length(ref($_[0])); |
2348
|
|
|
|
|
|
|
|
2349
|
0
|
|
|
|
|
0
|
my $addr; |
2350
|
0
|
0
|
|
|
|
0
|
if(defined(my $pkg = blessed($_[0]))) { |
2351
|
0
|
|
|
|
|
0
|
$addr .= bless $_[0], 'Scalar::Util::Fake'; |
2352
|
0
|
|
|
|
|
0
|
bless $_[0], $pkg; |
2353
|
|
|
|
|
|
|
} |
2354
|
|
|
|
|
|
|
else { |
2355
|
0
|
|
|
|
|
0
|
$addr .= $_[0] |
2356
|
|
|
|
|
|
|
} |
2357
|
|
|
|
|
|
|
|
2358
|
0
|
|
|
|
|
0
|
$addr =~ /0x(\w+)/; |
2359
|
0
|
|
|
|
|
0
|
local $^W; |
2360
|
|
|
|
|
|
|
#no warnings 'portable'; |
2361
|
0
|
|
|
|
|
0
|
hex($1); |
2362
|
|
|
|
|
|
|
} |
2363
|
0
|
|
|
|
|
0
|
} |
2364
|
|
|
|
|
|
|
} |
2365
|
|
|
|
|
|
|
|
2366
|
|
|
|
|
|
|
|
2367
|
|
|
|
|
|
|
# shamely copied and modified from JSON::XS code. |
2368
|
|
|
|
|
|
|
|
2369
|
|
|
|
|
|
|
$Test::ModuleVersion::JSON::PP::true = do { bless \(my $dummy = 1), "Test::ModuleVersion::JSON::PP::Boolean" }; |
2370
|
|
|
|
|
|
|
$Test::ModuleVersion::JSON::PP::false = do { bless \(my $dummy = 0), "Test::ModuleVersion::JSON::PP::Boolean" }; |
2371
|
|
|
|
|
|
|
|
2372
|
0
|
0
|
|
0
|
|
0
|
sub is_bool { defined $_[0] and UNIVERSAL::isa($_[0], "Test::ModuleVersion::JSON::PP::Boolean"); } |
2373
|
|
|
|
|
|
|
|
2374
|
0
|
|
|
0
|
|
0
|
sub true { $Test::ModuleVersion::JSON::PP::true } |
2375
|
0
|
|
|
0
|
|
0
|
sub false { $Test::ModuleVersion::JSON::PP::false } |
2376
|
0
|
|
|
0
|
|
0
|
sub null { undef; } |
2377
|
|
|
|
|
|
|
|
2378
|
|
|
|
|
|
|
############################### |
2379
|
|
|
|
|
|
|
|
2380
|
|
|
|
|
|
|
package Test::ModuleVersion::JSON::PP::Boolean; |
2381
|
|
|
|
|
|
|
|
2382
|
|
|
|
|
|
|
use overload ( |
2383
|
0
|
|
|
0
|
|
0
|
"0+" => sub { ${$_[0]} }, |
|
0
|
|
|
|
|
0
|
|
2384
|
0
|
|
|
0
|
|
0
|
"++" => sub { $_[0] = ${$_[0]} + 1 }, |
|
0
|
|
|
|
|
0
|
|
2385
|
0
|
|
|
0
|
|
0
|
"--" => sub { $_[0] = ${$_[0]} - 1 }, |
|
0
|
|
|
|
|
0
|
|
2386
|
1
|
|
|
|
|
18
|
fallback => 1, |
2387
|
1
|
|
|
1
|
|
6
|
); |
|
1
|
|
|
|
|
2
|
|
2388
|
|
|
|
|
|
|
|
2389
|
|
|
|
|
|
|
|
2390
|
|
|
|
|
|
|
############################### |
2391
|
|
|
|
|
|
|
|
2392
|
|
|
|
|
|
|
package Test::ModuleVersion::JSON::PP::IncrParser; |
2393
|
|
|
|
|
|
|
|
2394
|
1
|
|
|
1
|
|
130
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
50
|
|
2395
|
|
|
|
|
|
|
|
2396
|
1
|
|
|
1
|
|
6
|
use constant INCR_M_WS => 0; # initial whitespace skipping |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
78
|
|
2397
|
1
|
|
|
1
|
|
5
|
use constant INCR_M_STR => 1; # inside string |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
58
|
|
2398
|
1
|
|
|
1
|
|
5
|
use constant INCR_M_BS => 2; # inside backslash |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
49
|
|
2399
|
1
|
|
|
1
|
|
5
|
use constant INCR_M_JSON => 3; # outside anything, count nesting |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
52
|
|
2400
|
1
|
|
|
1
|
|
6
|
use constant INCR_M_C0 => 4; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
51
|
|
2401
|
1
|
|
|
1
|
|
5
|
use constant INCR_M_C1 => 5; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1461
|
|
2402
|
|
|
|
|
|
|
|
2403
|
|
|
|
|
|
|
$Test::ModuleVersion::JSON::PP::IncrParser::VERSION = '1.01'; |
2404
|
|
|
|
|
|
|
|
2405
|
|
|
|
|
|
|
my $unpack_format = $] < 5.006 ? 'C*' : 'U*'; |
2406
|
|
|
|
|
|
|
|
2407
|
|
|
|
|
|
|
sub new { |
2408
|
0
|
|
|
0
|
|
0
|
my ( $class ) = @_; |
2409
|
|
|
|
|
|
|
|
2410
|
0
|
|
|
|
|
0
|
bless { |
2411
|
|
|
|
|
|
|
incr_nest => 0, |
2412
|
|
|
|
|
|
|
incr_text => undef, |
2413
|
|
|
|
|
|
|
incr_parsing => 0, |
2414
|
|
|
|
|
|
|
incr_p => 0, |
2415
|
|
|
|
|
|
|
}, $class; |
2416
|
|
|
|
|
|
|
} |
2417
|
|
|
|
|
|
|
|
2418
|
|
|
|
|
|
|
|
2419
|
|
|
|
|
|
|
sub incr_parse { |
2420
|
0
|
|
|
0
|
|
0
|
my ( $self, $coder, $text ) = @_; |
2421
|
|
|
|
|
|
|
|
2422
|
0
|
0
|
|
|
|
0
|
$self->{incr_text} = '' unless ( defined $self->{incr_text} ); |
2423
|
|
|
|
|
|
|
|
2424
|
0
|
0
|
|
|
|
0
|
if ( defined $text ) { |
2425
|
0
|
0
|
0
|
|
|
0
|
if ( utf8::is_utf8( $text ) and !utf8::is_utf8( $self->{incr_text} ) ) { |
2426
|
0
|
|
|
|
|
0
|
utf8::upgrade( $self->{incr_text} ) ; |
2427
|
0
|
|
|
|
|
0
|
utf8::decode( $self->{incr_text} ) ; |
2428
|
|
|
|
|
|
|
} |
2429
|
0
|
|
|
|
|
0
|
$self->{incr_text} .= $text; |
2430
|
|
|
|
|
|
|
} |
2431
|
|
|
|
|
|
|
|
2432
|
|
|
|
|
|
|
|
2433
|
0
|
|
|
|
|
0
|
my $max_size = $coder->get_max_size; |
2434
|
|
|
|
|
|
|
|
2435
|
0
|
0
|
|
|
|
0
|
if ( defined wantarray ) { |
2436
|
|
|
|
|
|
|
|
2437
|
0
|
0
|
|
|
|
0
|
$self->{incr_mode} = INCR_M_WS unless defined $self->{incr_mode}; |
2438
|
|
|
|
|
|
|
|
2439
|
0
|
0
|
|
|
|
0
|
if ( wantarray ) { |
2440
|
0
|
|
|
|
|
0
|
my @ret; |
2441
|
|
|
|
|
|
|
|
2442
|
0
|
|
|
|
|
0
|
$self->{incr_parsing} = 1; |
2443
|
|
|
|
|
|
|
|
2444
|
0
|
|
|
|
|
0
|
do { |
2445
|
0
|
|
|
|
|
0
|
push @ret, $self->_incr_parse( $coder, $self->{incr_text} ); |
2446
|
|
|
|
|
|
|
|
2447
|
0
|
0
|
0
|
|
|
0
|
unless ( !$self->{incr_nest} and $self->{incr_mode} == INCR_M_JSON ) { |
2448
|
0
|
0
|
|
|
|
0
|
$self->{incr_mode} = INCR_M_WS if $self->{incr_mode} != INCR_M_STR; |
2449
|
|
|
|
|
|
|
} |
2450
|
|
|
|
|
|
|
|
2451
|
|
|
|
|
|
|
} until ( length $self->{incr_text} >= $self->{incr_p} ); |
2452
|
|
|
|
|
|
|
|
2453
|
0
|
|
|
|
|
0
|
$self->{incr_parsing} = 0; |
2454
|
|
|
|
|
|
|
|
2455
|
0
|
|
|
|
|
0
|
return @ret; |
2456
|
|
|
|
|
|
|
} |
2457
|
|
|
|
|
|
|
else { # in scalar context |
2458
|
0
|
|
|
|
|
0
|
$self->{incr_parsing} = 1; |
2459
|
0
|
|
|
|
|
0
|
my $obj = $self->_incr_parse( $coder, $self->{incr_text} ); |
2460
|
0
|
0
|
|
|
|
0
|
$self->{incr_parsing} = 0 if defined $obj; # pointed by Martin J. Evans |
2461
|
0
|
0
|
|
|
|
0
|
return $obj ? $obj : undef; # $obj is an empty string, parsing was completed. |
2462
|
|
|
|
|
|
|
} |
2463
|
|
|
|
|
|
|
|
2464
|
|
|
|
|
|
|
} |
2465
|
|
|
|
|
|
|
|
2466
|
|
|
|
|
|
|
} |
2467
|
|
|
|
|
|
|
|
2468
|
|
|
|
|
|
|
|
2469
|
|
|
|
|
|
|
sub _incr_parse { |
2470
|
0
|
|
|
0
|
|
0
|
my ( $self, $coder, $text, $skip ) = @_; |
2471
|
0
|
|
|
|
|
0
|
my $p = $self->{incr_p}; |
2472
|
0
|
|
|
|
|
0
|
my $restore = $p; |
2473
|
|
|
|
|
|
|
|
2474
|
0
|
|
|
|
|
0
|
my @obj; |
2475
|
0
|
|
|
|
|
0
|
my $len = length $text; |
2476
|
|
|
|
|
|
|
|
2477
|
0
|
0
|
|
|
|
0
|
if ( $self->{incr_mode} == INCR_M_WS ) { |
2478
|
0
|
|
|
|
|
0
|
while ( $len > $p ) { |
2479
|
0
|
|
|
|
|
0
|
my $s = substr( $text, $p, 1 ); |
2480
|
0
|
0
|
0
|
|
|
0
|
$p++ and next if ( 0x20 >= unpack($unpack_format, $s) ); |
2481
|
0
|
|
|
|
|
0
|
$self->{incr_mode} = INCR_M_JSON; |
2482
|
0
|
|
|
|
|
0
|
last; |
2483
|
|
|
|
|
|
|
} |
2484
|
|
|
|
|
|
|
} |
2485
|
|
|
|
|
|
|
|
2486
|
0
|
|
|
|
|
0
|
while ( $len > $p ) { |
2487
|
0
|
|
|
|
|
0
|
my $s = substr( $text, $p++, 1 ); |
2488
|
|
|
|
|
|
|
|
2489
|
0
|
0
|
|
|
|
0
|
if ( $s eq '"' ) { |
2490
|
0
|
0
|
|
|
|
0
|
if (substr( $text, $p - 2, 1 ) eq '\\' ) { |
2491
|
0
|
|
|
|
|
0
|
next; |
2492
|
|
|
|
|
|
|
} |
2493
|
|
|
|
|
|
|
|
2494
|
0
|
0
|
|
|
|
0
|
if ( $self->{incr_mode} != INCR_M_STR ) { |
2495
|
0
|
|
|
|
|
0
|
$self->{incr_mode} = INCR_M_STR; |
2496
|
|
|
|
|
|
|
} |
2497
|
|
|
|
|
|
|
else { |
2498
|
0
|
|
|
|
|
0
|
$self->{incr_mode} = INCR_M_JSON; |
2499
|
0
|
0
|
|
|
|
0
|
unless ( $self->{incr_nest} ) { |
2500
|
0
|
|
|
|
|
0
|
last; |
2501
|
|
|
|
|
|
|
} |
2502
|
|
|
|
|
|
|
} |
2503
|
|
|
|
|
|
|
} |
2504
|
|
|
|
|
|
|
|
2505
|
0
|
0
|
|
|
|
0
|
if ( $self->{incr_mode} == INCR_M_JSON ) { |
2506
|
|
|
|
|
|
|
|
2507
|
0
|
0
|
0
|
|
|
0
|
if ( $s eq '[' or $s eq '{' ) { |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
2508
|
0
|
0
|
|
|
|
0
|
if ( ++$self->{incr_nest} > $coder->get_max_depth ) { |
2509
|
0
|
|
|
|
|
0
|
Carp::croak('json text or perl structure exceeds maximum nesting level (max_depth set too low?)'); |
2510
|
|
|
|
|
|
|
} |
2511
|
|
|
|
|
|
|
} |
2512
|
|
|
|
|
|
|
elsif ( $s eq ']' or $s eq '}' ) { |
2513
|
0
|
0
|
|
|
|
0
|
last if ( --$self->{incr_nest} <= 0 ); |
2514
|
|
|
|
|
|
|
} |
2515
|
|
|
|
|
|
|
elsif ( $s eq '#' ) { |
2516
|
0
|
|
|
|
|
0
|
while ( $len > $p ) { |
2517
|
0
|
0
|
|
|
|
0
|
last if substr( $text, $p++, 1 ) eq "\n"; |
2518
|
|
|
|
|
|
|
} |
2519
|
|
|
|
|
|
|
} |
2520
|
|
|
|
|
|
|
|
2521
|
|
|
|
|
|
|
} |
2522
|
|
|
|
|
|
|
|
2523
|
|
|
|
|
|
|
} |
2524
|
|
|
|
|
|
|
|
2525
|
0
|
|
|
|
|
0
|
$self->{incr_p} = $p; |
2526
|
|
|
|
|
|
|
|
2527
|
0
|
0
|
0
|
|
|
0
|
return if ( $self->{incr_mode} == INCR_M_STR and not $self->{incr_nest} ); |
2528
|
0
|
0
|
0
|
|
|
0
|
return if ( $self->{incr_mode} == INCR_M_JSON and $self->{incr_nest} > 0 ); |
2529
|
|
|
|
|
|
|
|
2530
|
0
|
0
|
|
|
|
0
|
return '' unless ( length substr( $self->{incr_text}, 0, $p ) ); |
2531
|
|
|
|
|
|
|
|
2532
|
0
|
|
|
|
|
0
|
local $Carp::CarpLevel = 2; |
2533
|
|
|
|
|
|
|
|
2534
|
0
|
|
|
|
|
0
|
$self->{incr_p} = $restore; |
2535
|
0
|
|
|
|
|
0
|
$self->{incr_c} = $p; |
2536
|
|
|
|
|
|
|
|
2537
|
0
|
|
|
|
|
0
|
my ( $obj, $tail ) = $coder->PP_decode_json( substr( $self->{incr_text}, 0, $p ), 0x10000001 ); |
2538
|
|
|
|
|
|
|
|
2539
|
0
|
|
|
|
|
0
|
$self->{incr_text} = substr( $self->{incr_text}, $p ); |
2540
|
0
|
|
|
|
|
0
|
$self->{incr_p} = 0; |
2541
|
|
|
|
|
|
|
|
2542
|
0
|
0
|
|
|
|
0
|
return $obj or ''; |
2543
|
|
|
|
|
|
|
} |
2544
|
|
|
|
|
|
|
|
2545
|
|
|
|
|
|
|
|
2546
|
|
|
|
|
|
|
sub incr_text { |
2547
|
0
|
0
|
|
0
|
|
0
|
if ( $_[0]->{incr_parsing} ) { |
2548
|
0
|
|
|
|
|
0
|
Carp::croak("incr_text can not be called when the incremental parser already started parsing"); |
2549
|
|
|
|
|
|
|
} |
2550
|
0
|
|
|
|
|
0
|
$_[0]->{incr_text}; |
2551
|
|
|
|
|
|
|
} |
2552
|
|
|
|
|
|
|
|
2553
|
|
|
|
|
|
|
|
2554
|
|
|
|
|
|
|
sub incr_skip { |
2555
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
2556
|
0
|
|
|
|
|
0
|
$self->{incr_text} = substr( $self->{incr_text}, $self->{incr_c} ); |
2557
|
0
|
|
|
|
|
0
|
$self->{incr_p} = 0; |
2558
|
|
|
|
|
|
|
} |
2559
|
|
|
|
|
|
|
|
2560
|
|
|
|
|
|
|
|
2561
|
|
|
|
|
|
|
sub incr_reset { |
2562
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
2563
|
0
|
|
|
|
|
0
|
$self->{incr_text} = undef; |
2564
|
0
|
|
|
|
|
0
|
$self->{incr_p} = 0; |
2565
|
0
|
|
|
|
|
0
|
$self->{incr_mode} = 0; |
2566
|
0
|
|
|
|
|
0
|
$self->{incr_nest} = 0; |
2567
|
0
|
|
|
|
|
0
|
$self->{incr_parsing} = 0; |
2568
|
|
|
|
|
|
|
} |
2569
|
|
|
|
|
|
|
|
2570
|
|
|
|
|
|
|
package |
2571
|
|
|
|
|
|
|
Test::ModuleVersion::ModuleURL; |
2572
|
|
|
|
|
|
|
our @ISA = ('Test::ModuleVersion::Object::Simple'); |
2573
|
1
|
|
|
1
|
|
7
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
41
|
|
2574
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
765
|
|
2575
|
4
|
|
|
4
|
|
18
|
sub has { __PACKAGE__->Test::ModuleVersion::Object::Simple::attr(@_) } |
2576
|
|
|
|
|
|
|
|
2577
|
|
|
|
|
|
|
has distnames => sub { {} }; |
2578
|
|
|
|
|
|
|
has privates => sub { {} }; |
2579
|
|
|
|
|
|
|
has 'error'; |
2580
|
|
|
|
|
|
|
has lwp => 'auto'; |
2581
|
|
|
|
|
|
|
|
2582
|
|
|
|
|
|
|
sub get { |
2583
|
0
|
|
|
0
|
|
0
|
my ($self, $module, $version, $opts) = @_; |
2584
|
|
|
|
|
|
|
|
2585
|
0
|
|
0
|
|
|
0
|
$opts ||= {}; |
2586
|
0
|
|
|
|
|
0
|
my $distnames = $self->distnames; |
2587
|
0
|
|
|
|
|
0
|
my $privates = $self->privates; |
2588
|
0
|
|
|
|
|
0
|
my $lwp = $self->lwp; |
2589
|
|
|
|
|
|
|
|
2590
|
|
|
|
|
|
|
# Module |
2591
|
0
|
|
|
|
|
0
|
my $module_dist = $module; |
2592
|
0
|
0
|
|
|
|
0
|
$module_dist = $distnames->{$module} if defined $distnames->{$module}; |
2593
|
0
|
|
|
|
|
0
|
$module_dist =~ s/::/-/g; |
2594
|
|
|
|
|
|
|
|
2595
|
0
|
|
|
|
|
0
|
my $url; |
2596
|
0
|
0
|
|
|
|
0
|
if ($url = $privates->{$module}) { |
2597
|
0
|
|
|
|
|
0
|
$url =~ s/%M/"$module_dist-$version"/e; |
|
0
|
|
|
|
|
0
|
|
2598
|
|
|
|
|
|
|
} |
2599
|
|
|
|
|
|
|
else { |
2600
|
|
|
|
|
|
|
|
2601
|
|
|
|
|
|
|
# Get dounload URL using metaCPAN api |
2602
|
0
|
|
|
|
|
0
|
my $metacpan_api = 'http://api.metacpan.org/v0'; |
2603
|
0
|
|
|
|
|
0
|
my $search = "release/_search?q=name:$module_dist-$version" |
2604
|
|
|
|
|
|
|
. "&fields=download_url,name"; |
2605
|
0
|
|
|
|
|
0
|
my $module_info = "$metacpan_api/$search"; |
2606
|
0
|
|
|
|
|
0
|
my $res = {}; |
2607
|
0
|
|
|
|
|
0
|
my $agent; |
2608
|
0
|
0
|
0
|
|
|
0
|
if ($lwp eq 'use' || $lwp eq 'auto' && eval { require LWP::UserAgent; LWP::UserAgent->VERSION(5.802) }) |
|
0
|
|
0
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
2609
|
|
|
|
|
|
|
{ |
2610
|
0
|
|
|
|
|
0
|
require LWP::UserAgent; |
2611
|
0
|
|
|
|
|
0
|
$agent = 'LWP::UserAgent'; |
2612
|
0
|
|
|
|
|
0
|
my $ua = LWP::UserAgent->new( |
2613
|
|
|
|
|
|
|
parse_head => 0, |
2614
|
|
|
|
|
|
|
env_proxy => 1, |
2615
|
|
|
|
|
|
|
agent => "Test::ModuleVersion/$VERSION", |
2616
|
|
|
|
|
|
|
timeout => 30 |
2617
|
|
|
|
|
|
|
); |
2618
|
0
|
|
|
|
|
0
|
my $r = $ua->get($module_info); |
2619
|
0
|
|
|
|
|
0
|
$agent = 'LWP::UserAgent'; |
2620
|
0
|
|
|
|
|
0
|
$res->{success} = $r->is_success; |
2621
|
0
|
|
|
|
|
0
|
$res->{status_line} = $r->status_line; |
2622
|
0
|
|
|
|
|
0
|
$res->{content} = $r->content; |
2623
|
|
|
|
|
|
|
} |
2624
|
|
|
|
|
|
|
else { |
2625
|
0
|
|
|
|
|
0
|
$agent = 'HTTP::Tiny'; |
2626
|
0
|
|
|
|
|
0
|
my $ua = Test::ModuleVersion::HTTP::Tiny->new; |
2627
|
0
|
|
|
|
|
0
|
my $r = $ua->get($module_info); |
2628
|
0
|
|
|
|
|
0
|
$res->{success} = $r->{success}; |
2629
|
0
|
|
|
|
|
0
|
$res->{status_line} = "$r->{status} $r->{reason}"; |
2630
|
0
|
|
|
|
|
0
|
$res->{content} = $r->{content}; |
2631
|
|
|
|
|
|
|
} |
2632
|
|
|
|
|
|
|
|
2633
|
0
|
|
|
|
|
0
|
my $error; |
2634
|
0
|
0
|
0
|
|
|
0
|
if ($res->{success} && !$ENV{TEST_MODULEVERSION_REQUEST_FAIL}) { |
2635
|
0
|
|
|
|
|
0
|
my $release = Test::ModuleVersion::JSON::PP::decode_json $res->{content}; |
2636
|
0
|
|
|
|
|
0
|
$url = $release->{hits}{hits}[0]{fields}{download_url}; |
2637
|
0
|
0
|
|
|
|
0
|
$error = "$module_dist-$version is unknown" unless defined $url; |
2638
|
|
|
|
|
|
|
} |
2639
|
|
|
|
|
|
|
else { |
2640
|
0
|
|
|
|
|
0
|
$error = "Request to metaCPAN fail($res->{status_line}):$agent:$module_info"; |
2641
|
|
|
|
|
|
|
} |
2642
|
0
|
|
|
|
|
0
|
$self->error($error); |
2643
|
|
|
|
|
|
|
} |
2644
|
|
|
|
|
|
|
|
2645
|
0
|
|
|
|
|
0
|
return $url; |
2646
|
|
|
|
|
|
|
} |
2647
|
|
|
|
|
|
|
|
2648
|
|
|
|
|
|
|
|
2649
|
|
|
|
|
|
|
package Test::ModuleVersion; |
2650
|
|
|
|
|
|
|
our @ISA = ('Test::ModuleVersion::Object::Simple'); |
2651
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
29
|
|
2652
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
33
|
|
2653
|
1
|
|
|
1
|
|
1080
|
use ExtUtils::Installed; |
|
1
|
|
|
|
|
151403
|
|
|
1
|
|
|
|
|
50
|
|
2654
|
1
|
|
|
1
|
|
10
|
use Carp 'croak'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
69
|
|
2655
|
1
|
|
|
1
|
|
1219
|
use Data::Dumper; |
|
1
|
|
|
|
|
6370
|
|
|
1
|
|
|
|
|
1143
|
|
2656
|
|
|
|
|
|
|
|
2657
|
6
|
|
|
6
|
0
|
22
|
sub has { __PACKAGE__->Test::ModuleVersion::Object::Simple::attr(@_) } |
2658
|
|
|
|
|
|
|
has before => ''; |
2659
|
|
|
|
|
|
|
has distnames => sub { {} }; |
2660
|
|
|
|
|
|
|
has default_ignore => sub { ['Perl', 'Test::ModuleVersion'] }; |
2661
|
|
|
|
|
|
|
has lib => sub { [] }; |
2662
|
|
|
|
|
|
|
has modules => sub { [] }; |
2663
|
|
|
|
|
|
|
has privates => sub { {} }; |
2664
|
|
|
|
|
|
|
|
2665
|
|
|
|
|
|
|
sub detect { |
2666
|
0
|
|
|
0
|
1
|
0
|
my ($self, %opts) = @_; |
2667
|
0
|
|
0
|
|
|
0
|
my $ignore = $opts{ignore} || []; |
2668
|
|
|
|
|
|
|
|
2669
|
|
|
|
|
|
|
# Detect installed modules |
2670
|
0
|
|
|
|
|
0
|
my $ei = ExtUtils::Installed->new; |
2671
|
0
|
|
|
|
|
0
|
my @modules; |
2672
|
0
|
|
|
|
|
0
|
for my $module (sort $ei->modules) { |
2673
|
0
|
0
|
|
|
|
0
|
next if grep { $module eq $_ } @$ignore; |
|
0
|
|
|
|
|
0
|
|
2674
|
0
|
|
|
|
|
0
|
my $version = $ei->version($module); |
2675
|
0
|
0
|
|
|
|
0
|
push @modules, [$module => $version] if length $version; |
2676
|
|
|
|
|
|
|
} |
2677
|
|
|
|
|
|
|
|
2678
|
0
|
|
|
|
|
0
|
return \@modules; |
2679
|
|
|
|
|
|
|
} |
2680
|
|
|
|
|
|
|
|
2681
|
|
|
|
|
|
|
sub test_script { |
2682
|
4
|
|
|
4
|
1
|
18
|
my ($self, %opts) = @_; |
2683
|
|
|
|
|
|
|
|
2684
|
|
|
|
|
|
|
# Code |
2685
|
4
|
|
|
|
|
7
|
my $code; |
2686
|
|
|
|
|
|
|
|
2687
|
|
|
|
|
|
|
# Library path |
2688
|
4
|
50
|
|
|
|
15
|
my $libs = ref $self->lib ? $self->lib : [$self->lib]; |
2689
|
4
|
|
|
|
|
23
|
$code .= "use FindBin;\n"; |
2690
|
4
|
|
|
|
|
18
|
$code .= qq|use lib "\$FindBin::Bin/$_";\n| for @$libs; |
2691
|
|
|
|
|
|
|
|
2692
|
|
|
|
|
|
|
# Before |
2693
|
4
|
|
|
|
|
19
|
$code .= $self->before . "\n"; |
2694
|
|
|
|
|
|
|
|
2695
|
|
|
|
|
|
|
# Reffer this module |
2696
|
4
|
|
|
|
|
13
|
$code .= "# Created by Test::ModuleVersion $Test::ModuleVersion::VERSION\n"; |
2697
|
|
|
|
|
|
|
|
2698
|
|
|
|
|
|
|
# Test code |
2699
|
4
|
|
|
|
|
7
|
$code .= <<'EOS'; |
2700
|
|
|
|
|
|
|
use Test::More; |
2701
|
|
|
|
|
|
|
use strict; |
2702
|
|
|
|
|
|
|
use warnings; |
2703
|
|
|
|
|
|
|
use ExtUtils::Installed; |
2704
|
|
|
|
|
|
|
EOS |
2705
|
|
|
|
|
|
|
|
2706
|
|
|
|
|
|
|
# Main |
2707
|
4
|
|
|
|
|
8
|
$code .= <<'EOS'; |
2708
|
|
|
|
|
|
|
|
2709
|
|
|
|
|
|
|
sub main { |
2710
|
|
|
|
|
|
|
my $command = shift; |
2711
|
|
|
|
|
|
|
my @options = @_; |
2712
|
|
|
|
|
|
|
|
2713
|
|
|
|
|
|
|
die qq/command "$command" is unkonwn command/ |
2714
|
|
|
|
|
|
|
if defined $command && $command ne 'list'; |
2715
|
|
|
|
|
|
|
|
2716
|
|
|
|
|
|
|
my $list_failed; |
2717
|
|
|
|
|
|
|
my $lwp = 'auto'; |
2718
|
|
|
|
|
|
|
for my $option (@options) { |
2719
|
|
|
|
|
|
|
if ($option eq '--fail') { $list_failed = 1 } |
2720
|
|
|
|
|
|
|
elsif ($option eq '--lwp') { $lwp = 'use' } |
2721
|
|
|
|
|
|
|
elsif ($option eq '--no-lwp') { $lwp = 'no' } |
2722
|
|
|
|
|
|
|
else { die qq/list $option is unknown option/ } |
2723
|
|
|
|
|
|
|
} |
2724
|
|
|
|
|
|
|
|
2725
|
|
|
|
|
|
|
if (defined $command) { |
2726
|
|
|
|
|
|
|
my $builder = Test::More->builder; |
2727
|
|
|
|
|
|
|
open my $out_fh, '>', undef; |
2728
|
|
|
|
|
|
|
$builder->output($out_fh); |
2729
|
|
|
|
|
|
|
$builder->failure_output($out_fh); |
2730
|
|
|
|
|
|
|
$builder->todo_output($out_fh); |
2731
|
|
|
|
|
|
|
} |
2732
|
|
|
|
|
|
|
|
2733
|
|
|
|
|
|
|
my $modules = []; |
2734
|
|
|
|
|
|
|
my $failed = []; |
2735
|
|
|
|
|
|
|
my $require_ok; |
2736
|
|
|
|
|
|
|
my $version_ok; |
2737
|
|
|
|
|
|
|
my $version; |
2738
|
|
|
|
|
|
|
|
2739
|
|
|
|
|
|
|
plan tests => <%%%%%% test_count %%%%%%>; |
2740
|
|
|
|
|
|
|
|
2741
|
|
|
|
|
|
|
EOS |
2742
|
|
|
|
|
|
|
|
2743
|
|
|
|
|
|
|
# Module and version check |
2744
|
4
|
|
|
|
|
8
|
my $test_count = 0; |
2745
|
4
|
|
|
|
|
7
|
for my $m (@{$self->modules}) { |
|
4
|
|
|
|
|
12
|
|
2746
|
10
|
|
|
|
|
19
|
my ($module, $version) = @$m; |
2747
|
10
|
|
|
|
|
65
|
$code .= " # $module\n" |
2748
|
|
|
|
|
|
|
. " \$require_ok = require_ok('$module');\n" |
2749
|
|
|
|
|
|
|
. " \$version_ok = is(\$${module}::VERSION, '$version', '$module version: $version');\n" |
2750
|
|
|
|
|
|
|
. " push \@\$modules, ['$module' => '$version'];\n" |
2751
|
|
|
|
|
|
|
. " push \@\$failed, ['$module' => '$version'] unless \$require_ok && \$version_ok;\n\n"; |
2752
|
10
|
|
|
|
|
24
|
$test_count += 2; |
2753
|
|
|
|
|
|
|
} |
2754
|
|
|
|
|
|
|
|
2755
|
|
|
|
|
|
|
# Print module URLs |
2756
|
4
|
|
|
|
|
9
|
$code .= <<'EOS'; |
2757
|
|
|
|
|
|
|
# Print module URLs |
2758
|
|
|
|
|
|
|
if (defined $command) { |
2759
|
|
|
|
|
|
|
my $distnames = <%%%%%% distnames %%%%%%> |
2760
|
|
|
|
|
|
|
; |
2761
|
|
|
|
|
|
|
my $privates = <%%%%%% privates %%%%%%> |
2762
|
|
|
|
|
|
|
; |
2763
|
|
|
|
|
|
|
my $tm = Test::ModuleVersion->new; |
2764
|
|
|
|
|
|
|
my @ms = $command eq 'list' && $list_failed ? @$failed |
2765
|
|
|
|
|
|
|
: $command eq 'list' ? @$modules |
2766
|
|
|
|
|
|
|
: []; |
2767
|
|
|
|
|
|
|
for my $m (@ms) { |
2768
|
|
|
|
|
|
|
my ($module, $version) = @$m; |
2769
|
|
|
|
|
|
|
my $mu = Test::ModuleVersion::ModuleURL->new; |
2770
|
|
|
|
|
|
|
$mu->distnames($distnames); |
2771
|
|
|
|
|
|
|
$mu->privates($privates); |
2772
|
|
|
|
|
|
|
$mu->lwp($lwp); |
2773
|
|
|
|
|
|
|
my $url = $mu->get($module, $version); |
2774
|
|
|
|
|
|
|
if (defined $url) { print "$url\n" } |
2775
|
|
|
|
|
|
|
else { print STDERR $mu->error . "\n" } |
2776
|
|
|
|
|
|
|
} |
2777
|
|
|
|
|
|
|
} |
2778
|
|
|
|
|
|
|
} |
2779
|
|
|
|
|
|
|
|
2780
|
|
|
|
|
|
|
EOS |
2781
|
|
|
|
|
|
|
|
2782
|
|
|
|
|
|
|
# Embbed Test::ModuleVersion |
2783
|
4
|
|
|
|
|
14
|
$code .= $self->_source . "\n"; |
2784
|
|
|
|
|
|
|
|
2785
|
|
|
|
|
|
|
# Run |
2786
|
4
|
|
|
|
|
35
|
$code .= "package main;\n" |
2787
|
|
|
|
|
|
|
. "main(\@ARGV);\n"; |
2788
|
|
|
|
|
|
|
|
2789
|
|
|
|
|
|
|
# Test count |
2790
|
4
|
|
|
|
|
41
|
$code =~ s/<%%%%%% test_count %%%%%%>/$test_count/e; |
|
4
|
|
|
|
|
200
|
|
2791
|
|
|
|
|
|
|
|
2792
|
|
|
|
|
|
|
# Distribution names |
2793
|
4
|
|
|
|
|
24
|
my $distnames_code = Data::Dumper->new([$self->distnames])->Terse(1)->Indent(2)->Dump; |
2794
|
4
|
|
|
|
|
404
|
$code =~ s/<%%%%%% distnames %%%%%%>/$distnames_code/e; |
|
4
|
|
|
|
|
247
|
|
2795
|
|
|
|
|
|
|
|
2796
|
|
|
|
|
|
|
# Private repositories |
2797
|
4
|
|
|
|
|
17
|
my $privates_code = Data::Dumper->new([$self->privates])->Terse(1)->Indent(2)->Dump; |
2798
|
4
|
|
|
|
|
220
|
$code =~ s/<%%%%%% privates %%%%%%>/$privates_code/e; |
|
4
|
|
|
|
|
237
|
|
2799
|
|
|
|
|
|
|
|
2800
|
4
|
50
|
|
|
|
17
|
if (my $file = $opts{output}) { |
2801
|
0
|
0
|
|
|
|
0
|
open my $fh, '>', $file |
2802
|
|
|
|
|
|
|
or die qq/Can't open file "$file": $!/; |
2803
|
0
|
|
|
|
|
0
|
print $fh $code; |
2804
|
|
|
|
|
|
|
} |
2805
|
4
|
|
|
|
|
58
|
return $code; |
2806
|
|
|
|
|
|
|
} |
2807
|
|
|
|
|
|
|
|
2808
|
|
|
|
|
|
|
sub _source { |
2809
|
4
|
|
|
4
|
|
6
|
my $self = shift; |
2810
|
|
|
|
|
|
|
|
2811
|
|
|
|
|
|
|
# Source |
2812
|
4
|
|
|
|
|
7
|
my $class = __PACKAGE__; |
2813
|
4
|
|
|
|
|
16
|
$class =~ s/::/\//g; |
2814
|
4
|
|
|
|
|
6
|
$class .= '.pm'; |
2815
|
4
|
|
|
|
|
8
|
my $path = $INC{$class}; |
2816
|
4
|
50
|
|
|
|
259
|
open my $fh, '<', $path |
2817
|
|
|
|
|
|
|
or croak qq/Can't open "$path": $!/; |
2818
|
4
|
|
|
|
|
6
|
my $source; |
2819
|
4
|
|
|
|
|
106
|
while (my $line = <$fh>) { |
2820
|
11312
|
100
|
|
|
|
17501
|
last if $line =~ /^=head1/; |
2821
|
11308
|
|
|
|
|
23293
|
$source .= $line; |
2822
|
|
|
|
|
|
|
} |
2823
|
4
|
|
|
|
|
814
|
return $source; |
2824
|
|
|
|
|
|
|
} |
2825
|
|
|
|
|
|
|
|
2826
|
|
|
|
|
|
|
1; |
2827
|
|
|
|
|
|
|
|
2828
|
|
|
|
|
|
|
=head1 NAME |
2829
|
|
|
|
|
|
|
|
2830
|
|
|
|
|
|
|
Test::ModuleVersion - Module version test generator |
2831
|
|
|
|
|
|
|
|
2832
|
|
|
|
|
|
|
=head1 CAUTION |
2833
|
|
|
|
|
|
|
|
2834
|
|
|
|
|
|
|
(2013/3/20) |
2835
|
|
|
|
|
|
|
|
2836
|
|
|
|
|
|
|
Sorry. This module is DEPRECATED because L and L is much better. |
2837
|
|
|
|
|
|
|
|
2838
|
|
|
|
|
|
|
If you want to install moudles, use L and L instead. |
2839
|
|
|
|
|
|
|
|
2840
|
|
|
|
|
|
|
See L |
2841
|
|
|
|
|
|
|
|
2842
|
|
|
|
|
|
|
If you want to test module version, you write test by yourself. |
2843
|
|
|
|
|
|
|
|
2844
|
|
|
|
|
|
|
is($DBIx::Custom::VERSION, '0.2108'); |
2845
|
|
|
|
|
|
|
|
2846
|
|
|
|
|
|
|
This module will be removed from CPAN on 2018/3/1 |
2847
|
|
|
|
|
|
|
|
2848
|
|
|
|
|
|
|
=head1 SYNOPSIS |
2849
|
|
|
|
|
|
|
|
2850
|
|
|
|
|
|
|
use Test::ModuleVersion; |
2851
|
|
|
|
|
|
|
my $tm = Test::ModuleVersion->new; |
2852
|
|
|
|
|
|
|
$tm->modules([ |
2853
|
|
|
|
|
|
|
['DBIx::Custom' => '0.2108'], |
2854
|
|
|
|
|
|
|
['Validator::Custom' => '0.1426'] |
2855
|
|
|
|
|
|
|
]); |
2856
|
|
|
|
|
|
|
$tm->test_script(output => 't/module.t'); |
2857
|
|
|
|
|
|
|
|
2858
|
|
|
|
|
|
|
=head1 DESCRIPTION |
2859
|
|
|
|
|
|
|
|
2860
|
|
|
|
|
|
|
L is test generator for module version check. |
2861
|
|
|
|
|
|
|
If you run the test generated by L, |
2862
|
|
|
|
|
|
|
you can check the module version. |
2863
|
|
|
|
|
|
|
|
2864
|
|
|
|
|
|
|
If module version test is failed, you can list module URLs. |
2865
|
|
|
|
|
|
|
|
2866
|
|
|
|
|
|
|
=head2 Create version test |
2867
|
|
|
|
|
|
|
|
2868
|
|
|
|
|
|
|
Let's create version test. |
2869
|
|
|
|
|
|
|
|
2870
|
|
|
|
|
|
|
# mvt.pl |
2871
|
|
|
|
|
|
|
my $tm = Test::ModuleVersion->new; |
2872
|
|
|
|
|
|
|
$tm->modules([ |
2873
|
|
|
|
|
|
|
['DBIx::Custom' => '0.2108'], |
2874
|
|
|
|
|
|
|
['Validator::Custom' => '0.1426'] |
2875
|
|
|
|
|
|
|
]); |
2876
|
|
|
|
|
|
|
$tm->test_script(output => 't/module.t'); |
2877
|
|
|
|
|
|
|
|
2878
|
|
|
|
|
|
|
C attribute is set to the pairs of module and version. |
2879
|
|
|
|
|
|
|
C method print version test into C file. |
2880
|
|
|
|
|
|
|
|
2881
|
|
|
|
|
|
|
Run C |
2882
|
|
|
|
|
|
|
|
2883
|
|
|
|
|
|
|
$ perl mvt.pl |
2884
|
|
|
|
|
|
|
|
2885
|
|
|
|
|
|
|
Test script C is created. |
2886
|
|
|
|
|
|
|
|
2887
|
|
|
|
|
|
|
... |
2888
|
|
|
|
|
|
|
$require_ok = require_ok('DBIx::Custom'); |
2889
|
|
|
|
|
|
|
$version_ok = is($DBIx::Custom::VERSION, '0.2108', 'DBIx::Custom version: 0.2108'); |
2890
|
|
|
|
|
|
|
|
2891
|
|
|
|
|
|
|
$require_ok = require_ok('Validator::Custom'); |
2892
|
|
|
|
|
|
|
$version_ok = is($Validator::Custom::VERSION, '0.1426', 'DBIx::Custom version: 0.1426'); |
2893
|
|
|
|
|
|
|
... |
2894
|
|
|
|
|
|
|
|
2895
|
|
|
|
|
|
|
=head2 Run version test |
2896
|
|
|
|
|
|
|
|
2897
|
|
|
|
|
|
|
Run version test. |
2898
|
|
|
|
|
|
|
|
2899
|
|
|
|
|
|
|
$ perl t/module.t |
2900
|
|
|
|
|
|
|
|
2901
|
|
|
|
|
|
|
If module is not installed or version is different, |
2902
|
|
|
|
|
|
|
test fail. |
2903
|
|
|
|
|
|
|
|
2904
|
|
|
|
|
|
|
ok 1 - require DBIx::Custom; |
2905
|
|
|
|
|
|
|
not ok 2 - DBIx::Custom version: 0.2108 |
2906
|
|
|
|
|
|
|
# Failed test 'DBIx::Custom version: 0.2108' |
2907
|
|
|
|
|
|
|
# at t/module.t.pl line 13. |
2908
|
|
|
|
|
|
|
# got: '0.2106' |
2909
|
|
|
|
|
|
|
# expected: '0.2108' |
2910
|
|
|
|
|
|
|
|
2911
|
|
|
|
|
|
|
ok 2 - require Validator::Custom; |
2912
|
|
|
|
|
|
|
ok 3 - Validator::Custom version: 0.1426 |
2913
|
|
|
|
|
|
|
|
2914
|
|
|
|
|
|
|
=head2 List module URLs |
2915
|
|
|
|
|
|
|
|
2916
|
|
|
|
|
|
|
You can list moudle URLs by C command |
2917
|
|
|
|
|
|
|
|
2918
|
|
|
|
|
|
|
$ perl t/module.t list |
2919
|
|
|
|
|
|
|
|
2920
|
|
|
|
|
|
|
All module URLs in version test is output to C. |
2921
|
|
|
|
|
|
|
|
2922
|
|
|
|
|
|
|
http://cpan.metacpan.org/authors/id/K/KI/KIMOTO/DBIx-Custom-0.2108.tar.gz |
2923
|
|
|
|
|
|
|
... |
2924
|
|
|
|
|
|
|
|
2925
|
|
|
|
|
|
|
You can list only test failed module URLs by C<--fail> option |
2926
|
|
|
|
|
|
|
|
2927
|
|
|
|
|
|
|
$ perl t/module.t list --fail |
2928
|
|
|
|
|
|
|
|
2929
|
|
|
|
|
|
|
=head1 Advanced |
2930
|
|
|
|
|
|
|
|
2931
|
|
|
|
|
|
|
=head2 Module installation by L |
2932
|
|
|
|
|
|
|
|
2933
|
|
|
|
|
|
|
$ perl t/module.t list --fail | perl cpanm -L extlib |
2934
|
|
|
|
|
|
|
|
2935
|
|
|
|
|
|
|
Module installation is very easy. Test failed module |
2936
|
|
|
|
|
|
|
is installed into C directory by L. |
2937
|
|
|
|
|
|
|
|
2938
|
|
|
|
|
|
|
=head2 HTTP client |
2939
|
|
|
|
|
|
|
|
2940
|
|
|
|
|
|
|
L version switch two HTTP client as necessary. |
2941
|
|
|
|
|
|
|
|
2942
|
|
|
|
|
|
|
=over 2 |
2943
|
|
|
|
|
|
|
|
2944
|
|
|
|
|
|
|
=item 1. LWP::UserAgent |
2945
|
|
|
|
|
|
|
|
2946
|
|
|
|
|
|
|
=item 2. HTTP::Tiny |
2947
|
|
|
|
|
|
|
|
2948
|
|
|
|
|
|
|
=back |
2949
|
|
|
|
|
|
|
|
2950
|
|
|
|
|
|
|
These module is used to get module URLs from metaCPAN. |
2951
|
|
|
|
|
|
|
|
2952
|
|
|
|
|
|
|
If L 5.802+ is installed, L |
2953
|
|
|
|
|
|
|
is seleced. If not, L is selected. |
2954
|
|
|
|
|
|
|
|
2955
|
|
|
|
|
|
|
C<--lwp> option force L. |
2956
|
|
|
|
|
|
|
|
2957
|
|
|
|
|
|
|
$ perl t/module.t list --lwp |
2958
|
|
|
|
|
|
|
|
2959
|
|
|
|
|
|
|
C<--no-lwp> option force L. |
2960
|
|
|
|
|
|
|
|
2961
|
|
|
|
|
|
|
$ perl t/module.t list --no-lwp |
2962
|
|
|
|
|
|
|
|
2963
|
|
|
|
|
|
|
=head2 HTTP proxy |
2964
|
|
|
|
|
|
|
|
2965
|
|
|
|
|
|
|
export http_proxy=http://hostname:3001 |
2966
|
|
|
|
|
|
|
|
2967
|
|
|
|
|
|
|
C environment variable enable you to use proxy server. |
2968
|
|
|
|
|
|
|
|
2969
|
|
|
|
|
|
|
=head2 HTTP proxy authentication |
2970
|
|
|
|
|
|
|
|
2971
|
|
|
|
|
|
|
export http_proxy=http://username:password@hostname:3001 |
2972
|
|
|
|
|
|
|
|
2973
|
|
|
|
|
|
|
If L 5.802+ is installed, |
2974
|
|
|
|
|
|
|
proxy authentication is available. |
2975
|
|
|
|
|
|
|
L don't support proxy authentication. |
2976
|
|
|
|
|
|
|
|
2977
|
|
|
|
|
|
|
=head1 EXAMPELS |
2978
|
|
|
|
|
|
|
|
2979
|
|
|
|
|
|
|
=head2 Basic1 |
2980
|
|
|
|
|
|
|
|
2981
|
|
|
|
|
|
|
# Directory |
2982
|
|
|
|
|
|
|
t / mvt.pl |
2983
|
|
|
|
|
|
|
/ module.t |
2984
|
|
|
|
|
|
|
|
2985
|
|
|
|
|
|
|
extlib / lib / perl5 / Object / Simple.pm |
2986
|
|
|
|
|
|
|
/ Validator / Custom.pm |
2987
|
|
|
|
|
|
|
|
2988
|
|
|
|
|
|
|
features: |
2989
|
|
|
|
|
|
|
|
2990
|
|
|
|
|
|
|
=over 2 |
2991
|
|
|
|
|
|
|
|
2992
|
|
|
|
|
|
|
=item 1. Module is installed in C |
2993
|
|
|
|
|
|
|
|
2994
|
|
|
|
|
|
|
=item 2. Perl 5.008007+ is required |
2995
|
|
|
|
|
|
|
|
2996
|
|
|
|
|
|
|
=item 3. Object::Simple 3.625, Validator::Custom 0.1401 |
2997
|
|
|
|
|
|
|
|
2998
|
|
|
|
|
|
|
=back |
2999
|
|
|
|
|
|
|
|
3000
|
|
|
|
|
|
|
use Test::ModuleVersion; |
3001
|
|
|
|
|
|
|
use FindBin; |
3002
|
|
|
|
|
|
|
my $tm = Test::ModuleVersion->new; |
3003
|
|
|
|
|
|
|
$tm->lib('../extlib/lib/perl5'); |
3004
|
|
|
|
|
|
|
$tm->before(<<'EOS'); |
3005
|
|
|
|
|
|
|
use 5.008007; |
3006
|
|
|
|
|
|
|
|
3007
|
|
|
|
|
|
|
=pod |
3008
|
|
|
|
|
|
|
|
3009
|
|
|
|
|
|
|
run mvt.pl to create this module version test(t/module.t). |
3010
|
|
|
|
|
|
|
|
3011
|
|
|
|
|
|
|
perl mvt.pl |
3012
|
|
|
|
|
|
|
|
3013
|
|
|
|
|
|
|
=cut |
3014
|
|
|
|
|
|
|
EOS |
3015
|
|
|
|
|
|
|
$tm->modules([ |
3016
|
|
|
|
|
|
|
['Object::Simple' => '3.0625'], |
3017
|
|
|
|
|
|
|
['Validator::Custom' => '0.1401'] |
3018
|
|
|
|
|
|
|
]); |
3019
|
|
|
|
|
|
|
$tm->test_script(output => "$FindBin::Bin/t/module.t"); |
3020
|
|
|
|
|
|
|
|
3021
|
|
|
|
|
|
|
=head2 Basic2 |
3022
|
|
|
|
|
|
|
|
3023
|
|
|
|
|
|
|
# Directory |
3024
|
|
|
|
|
|
|
t / mvt.pl |
3025
|
|
|
|
|
|
|
/ module.t |
3026
|
|
|
|
|
|
|
|
3027
|
|
|
|
|
|
|
extlib / lib / perl5 / LWP.pm |
3028
|
|
|
|
|
|
|
|
3029
|
|
|
|
|
|
|
features: |
3030
|
|
|
|
|
|
|
|
3031
|
|
|
|
|
|
|
=over 2 |
3032
|
|
|
|
|
|
|
|
3033
|
|
|
|
|
|
|
=item 1. LWP 6.03 |
3034
|
|
|
|
|
|
|
|
3035
|
|
|
|
|
|
|
LWP module distribution name is C. |
3036
|
|
|
|
|
|
|
If module name is different from distribution name, |
3037
|
|
|
|
|
|
|
you can use C attribute. |
3038
|
|
|
|
|
|
|
|
3039
|
|
|
|
|
|
|
=back |
3040
|
|
|
|
|
|
|
|
3041
|
|
|
|
|
|
|
use Test::ModuleVersion; |
3042
|
|
|
|
|
|
|
use FindBin; |
3043
|
|
|
|
|
|
|
my $tm = Test::ModuleVersion->new; |
3044
|
|
|
|
|
|
|
$tm->lib('../extlib/lib/perl5'); |
3045
|
|
|
|
|
|
|
$tm->distnames({ |
3046
|
|
|
|
|
|
|
'LWP' => 'libwww-perl', |
3047
|
|
|
|
|
|
|
}); |
3048
|
|
|
|
|
|
|
$tm->modules([ |
3049
|
|
|
|
|
|
|
['LWP' => '6.03'], |
3050
|
|
|
|
|
|
|
]); |
3051
|
|
|
|
|
|
|
$tm->test_script(output => "$FindBin::Bin/t/module.t"); |
3052
|
|
|
|
|
|
|
|
3053
|
|
|
|
|
|
|
=head2 Basic3 |
3054
|
|
|
|
|
|
|
|
3055
|
|
|
|
|
|
|
# Directory |
3056
|
|
|
|
|
|
|
t / mvt.pl |
3057
|
|
|
|
|
|
|
/ module.t |
3058
|
|
|
|
|
|
|
|
3059
|
|
|
|
|
|
|
extlib / lib / perl5 / SomeModule.pm |
3060
|
|
|
|
|
|
|
|
3061
|
|
|
|
|
|
|
features: |
3062
|
|
|
|
|
|
|
|
3063
|
|
|
|
|
|
|
=over 2 |
3064
|
|
|
|
|
|
|
|
3065
|
|
|
|
|
|
|
=item 1. SomeModule 0.03 don't exist in CPAN |
3066
|
|
|
|
|
|
|
|
3067
|
|
|
|
|
|
|
=item 2. SomeModule exist in http://myhost/SomeModule-0.03.tar.gz |
3068
|
|
|
|
|
|
|
|
3069
|
|
|
|
|
|
|
SomeModule is private module. |
3070
|
|
|
|
|
|
|
If module exist in some URL, |
3071
|
|
|
|
|
|
|
you can use C attribute. |
3072
|
|
|
|
|
|
|
|
3073
|
|
|
|
|
|
|
=back |
3074
|
|
|
|
|
|
|
|
3075
|
|
|
|
|
|
|
use Test::ModuleVersion; |
3076
|
|
|
|
|
|
|
use FindBin; |
3077
|
|
|
|
|
|
|
my $tm = Test::ModuleVersion->new; |
3078
|
|
|
|
|
|
|
$tm->lib('../extlib/lib/perl5'); |
3079
|
|
|
|
|
|
|
$tm->privates({ |
3080
|
|
|
|
|
|
|
'SomeModule' => 'http://myhost/%M.tar.gz', |
3081
|
|
|
|
|
|
|
}); |
3082
|
|
|
|
|
|
|
$tm->modules([ |
3083
|
|
|
|
|
|
|
['SomeModule' => '0.03'], |
3084
|
|
|
|
|
|
|
]); |
3085
|
|
|
|
|
|
|
$tm->test_script(output => "$FindBin::Bin/t/module.t"); |
3086
|
|
|
|
|
|
|
|
3087
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
3088
|
|
|
|
|
|
|
|
3089
|
|
|
|
|
|
|
=head2 C |
3090
|
|
|
|
|
|
|
|
3091
|
|
|
|
|
|
|
my $code = $self->before; |
3092
|
|
|
|
|
|
|
$tm = $tm->before($code); |
3093
|
|
|
|
|
|
|
|
3094
|
|
|
|
|
|
|
You can add some code before version test. |
3095
|
|
|
|
|
|
|
|
3096
|
|
|
|
|
|
|
$tm->before(<<'EOS'); |
3097
|
|
|
|
|
|
|
use 5.008007; |
3098
|
|
|
|
|
|
|
|
3099
|
|
|
|
|
|
|
=pod |
3100
|
|
|
|
|
|
|
|
3101
|
|
|
|
|
|
|
You can create this script(t/module.t) by the following command. |
3102
|
|
|
|
|
|
|
|
3103
|
|
|
|
|
|
|
perl mvt.pl |
3104
|
|
|
|
|
|
|
|
3105
|
|
|
|
|
|
|
=cut |
3106
|
|
|
|
|
|
|
|
3107
|
|
|
|
|
|
|
EOS |
3108
|
|
|
|
|
|
|
|
3109
|
|
|
|
|
|
|
=head2 C |
3110
|
|
|
|
|
|
|
|
3111
|
|
|
|
|
|
|
my $distnames = $self->distnames; |
3112
|
|
|
|
|
|
|
$tm = $tm->distnames({ |
3113
|
|
|
|
|
|
|
'LWP' => 'libwww-perl', |
3114
|
|
|
|
|
|
|
'IO::Compress::Base' => 'IO-Compress', |
3115
|
|
|
|
|
|
|
'Cwd' => 'PathTools', |
3116
|
|
|
|
|
|
|
'File::Spec' => 'PathTools', |
3117
|
|
|
|
|
|
|
'List::Util' => 'Scalar-List-Utils', |
3118
|
|
|
|
|
|
|
'Scalar::Util' => 'Scalar-List-Utils' |
3119
|
|
|
|
|
|
|
... |
3120
|
|
|
|
|
|
|
}); |
3121
|
|
|
|
|
|
|
|
3122
|
|
|
|
|
|
|
Module distribution name corresponding to module name. |
3123
|
|
|
|
|
|
|
Some module have different distribution name. |
3124
|
|
|
|
|
|
|
For example, L module distribution name is C. |
3125
|
|
|
|
|
|
|
|
3126
|
|
|
|
|
|
|
you must set C attribute to get module URL. |
3127
|
|
|
|
|
|
|
|
3128
|
|
|
|
|
|
|
=head2 C |
3129
|
|
|
|
|
|
|
|
3130
|
|
|
|
|
|
|
my $lib = $self->lib; |
3131
|
|
|
|
|
|
|
$tm = $tm->lib('../extlib/lib/perl5'); |
3132
|
|
|
|
|
|
|
$tm = $tm->lib(['../extlib/lib/perl5', ...]); |
3133
|
|
|
|
|
|
|
|
3134
|
|
|
|
|
|
|
Module including pass from version test directory. |
3135
|
|
|
|
|
|
|
C |
3136
|
|
|
|
|
|
|
|
3137
|
|
|
|
|
|
|
use lib "$FindBin::Bin/../extlib/lib/perl5"; |
3138
|
|
|
|
|
|
|
|
3139
|
|
|
|
|
|
|
=head2 C |
3140
|
|
|
|
|
|
|
|
3141
|
|
|
|
|
|
|
my $modules = $tm->modules; |
3142
|
|
|
|
|
|
|
$tm = $tm->modules($modules); |
3143
|
|
|
|
|
|
|
|
3144
|
|
|
|
|
|
|
Pairs of module and version. |
3145
|
|
|
|
|
|
|
|
3146
|
|
|
|
|
|
|
$tm->modules([ |
3147
|
|
|
|
|
|
|
['DBIx::Custom' => '0.2108'], |
3148
|
|
|
|
|
|
|
['Validator::Custom' => '0.1426'] |
3149
|
|
|
|
|
|
|
]); |
3150
|
|
|
|
|
|
|
|
3151
|
|
|
|
|
|
|
Note that version must be string(C<'0.1426'>), not number(C<0.1426>). |
3152
|
|
|
|
|
|
|
|
3153
|
|
|
|
|
|
|
=head2 C |
3154
|
|
|
|
|
|
|
|
3155
|
|
|
|
|
|
|
my $privates = $tm->privates; |
3156
|
|
|
|
|
|
|
$tm = $tm->privates({ |
3157
|
|
|
|
|
|
|
'SomeModule' => 'http://localhost/~kimoto/%M.tar.gz' |
3158
|
|
|
|
|
|
|
}); |
3159
|
|
|
|
|
|
|
|
3160
|
|
|
|
|
|
|
Private module URLs. |
3161
|
|
|
|
|
|
|
you can get module URL if the module don't exist in CPAN. |
3162
|
|
|
|
|
|
|
C<%M> is replaced by C like C. |
3163
|
|
|
|
|
|
|
|
3164
|
|
|
|
|
|
|
=head1 METHODS |
3165
|
|
|
|
|
|
|
|
3166
|
|
|
|
|
|
|
=head2 C |
3167
|
|
|
|
|
|
|
|
3168
|
|
|
|
|
|
|
my $modules = $tm->detect; |
3169
|
|
|
|
|
|
|
my $modules = $tm->detect(ignore => ['Perl', 'Test::ModuleVersion']); |
3170
|
|
|
|
|
|
|
|
3171
|
|
|
|
|
|
|
Get all installed module. |
3172
|
|
|
|
|
|
|
If you set C option, the module is ignored. |
3173
|
|
|
|
|
|
|
|
3174
|
|
|
|
|
|
|
Note that L is used internally. |
3175
|
|
|
|
|
|
|
This information will be not accurate in some cases. |
3176
|
|
|
|
|
|
|
|
3177
|
|
|
|
|
|
|
=head2 C |
3178
|
|
|
|
|
|
|
|
3179
|
|
|
|
|
|
|
my $test_script = $tm->test_script; |
3180
|
|
|
|
|
|
|
$tm->test_script(output => 't/module.t'); |
3181
|
|
|
|
|
|
|
|
3182
|
|
|
|
|
|
|
Return version test as string. |
3183
|
|
|
|
|
|
|
If C |
3184
|
|
|
|
|
|
|
|
3185
|
|
|
|
|
|
|
=head1 BACKWARDS COMPATIBILITY POLICY |
3186
|
|
|
|
|
|
|
|
3187
|
|
|
|
|
|
|
If a functionality is DEPRECATED, you can know it by DEPRECATED warnings |
3188
|
|
|
|
|
|
|
except for attribute method. |
3189
|
|
|
|
|
|
|
You can check all DEPRECATED functionalities by document. |
3190
|
|
|
|
|
|
|
DEPRECATED functionality is removed after five years, |
3191
|
|
|
|
|
|
|
but if at least one person use the functionality and tell me that thing |
3192
|
|
|
|
|
|
|
I extend one year each time he tell me it. |
3193
|
|
|
|
|
|
|
|
3194
|
|
|
|
|
|
|
EXPERIMENTAL functionality will be changed without warnings. |
3195
|
|
|
|
|
|
|
|
3196
|
|
|
|
|
|
|
=head1 AUTHOR |
3197
|
|
|
|
|
|
|
|
3198
|
|
|
|
|
|
|
Yuki Kimoto, C<< >> |
3199
|
|
|
|
|
|
|
|
3200
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
3201
|
|
|
|
|
|
|
|
3202
|
|
|
|
|
|
|
Copyright 2012 Yuki Kimoto. |
3203
|
|
|
|
|
|
|
|
3204
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
3205
|
|
|
|
|
|
|
under the terms of either: the GNU General Public License as published |
3206
|
|
|
|
|
|
|
by the Free Software Foundation; or the Artistic License. |
3207
|
|
|
|
|
|
|
|
3208
|
|
|
|
|
|
|
See http://dev.perl.org/licenses/ for more information. |
3209
|
|
|
|
|
|
|
|
3210
|
|
|
|
|
|
|
=cut |