| 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 |