line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Web::Dispatch::ParamParser; |
2
|
|
|
|
|
|
|
|
3
|
6
|
|
|
6
|
|
20564
|
use strict; |
|
6
|
|
|
|
|
11
|
|
|
6
|
|
|
|
|
271
|
|
4
|
6
|
|
|
6
|
|
27
|
use warnings FATAL => 'all'; |
|
6
|
|
|
|
|
12
|
|
|
6
|
|
|
|
|
311
|
|
5
|
|
|
|
|
|
|
|
6
|
6
|
|
|
6
|
|
3829
|
use Encode 'decode_utf8'; |
|
6
|
|
|
|
|
62255
|
|
|
6
|
|
|
|
|
7590
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
sub UNPACKED_QUERY () { __PACKAGE__.'.unpacked_query' } |
9
|
|
|
|
|
|
|
sub UNPACKED_BODY () { __PACKAGE__.'.unpacked_body' } |
10
|
|
|
|
|
|
|
sub UNPACKED_BODY_OBJECT () { __PACKAGE__.'.unpacked_body_object' } |
11
|
|
|
|
|
|
|
sub UNPACKED_UPLOADS () { __PACKAGE__.'.unpacked_uploads' } |
12
|
|
|
|
|
|
|
sub ORIG_ENV () { 'Web::Dispatch.original_env' } |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
sub get_unpacked_query_from { |
15
|
101
|
|
66
|
101
|
0
|
516
|
return ($_[0]->{+ORIG_ENV}||$_[0])->{+UNPACKED_QUERY} ||= do { |
|
|
|
33
|
|
|
|
|
16
|
|
|
|
|
|
|
_unpack_params($_[0]->{QUERY_STRING}) |
17
|
101
|
|
|
|
|
226
|
}; |
18
|
|
|
|
|
|
|
} |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
sub get_unpacked_body_from { |
21
|
26
|
|
66
|
26
|
0
|
102
|
return ($_[0]->{+ORIG_ENV}||$_[0])->{+UNPACKED_BODY} ||= do { |
|
|
|
66
|
|
|
|
|
22
|
13
|
|
100
|
|
|
52
|
my $ct = lc($_[0]->{CONTENT_TYPE}||''); |
23
|
13
|
100
|
|
|
|
61
|
if (!$_[0]->{CONTENT_LENGTH}) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
24
|
|
|
|
|
|
|
{} |
25
|
2
|
|
|
|
|
9
|
} elsif (index($ct, 'application/x-www-form-urlencoded') >= 0) { |
26
|
6
|
|
|
|
|
54
|
$_[0]->{'psgi.input'}->read(my $buf, $_[0]->{CONTENT_LENGTH}); |
27
|
6
|
|
|
|
|
57
|
_unpack_params($buf); |
28
|
|
|
|
|
|
|
} elsif (index($ct, 'multipart/form-data') >= 0) { |
29
|
5
|
|
|
|
|
15
|
my $p = get_unpacked_body_object_from($_[0])->param; |
30
|
|
|
|
|
|
|
# forcible arrayification (functional, $p does not belong to us, |
31
|
|
|
|
|
|
|
# do NOT replace this with a side-effect ridden "simpler" version) |
32
|
|
|
|
|
|
|
+{ |
33
|
|
|
|
|
|
|
map +(ref($p->{$_}) eq 'ARRAY' |
34
|
|
|
|
|
|
|
? ($_ => $p->{$_}) |
35
|
5
|
100
|
|
|
|
73
|
: ($_ => [ $p->{$_} ]) |
36
|
|
|
|
|
|
|
), keys %$p |
37
|
|
|
|
|
|
|
}; |
38
|
|
|
|
|
|
|
} else { |
39
|
|
|
|
|
|
|
{} |
40
|
0
|
|
|
|
|
0
|
} |
41
|
|
|
|
|
|
|
}; |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub get_unpacked_body_object_from { |
45
|
|
|
|
|
|
|
# we may have no object at all - so use a single element arrayref for ||= |
46
|
12
|
|
33
|
12
|
0
|
49
|
return (($_[0]->{+ORIG_ENV}||$_[0])->{+UNPACKED_BODY_OBJECT} ||= do { |
|
|
|
66
|
|
|
|
|
47
|
8
|
100
|
50
|
|
|
34
|
if (!$_[0]->{CONTENT_LENGTH}) { |
|
|
100
|
|
|
|
|
|
48
|
2
|
|
|
|
|
13
|
[ undef ] |
49
|
|
|
|
|
|
|
} elsif (index(lc($_[0]->{CONTENT_TYPE}||''),'multipart/form-data')==-1) { |
50
|
2
|
|
|
|
|
7
|
[ undef ] |
51
|
|
|
|
|
|
|
} else { |
52
|
4
|
|
|
|
|
7
|
[ _make_http_body($_[0]) ] |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
})->[0]; |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
sub get_unpacked_uploads_from { |
58
|
7
|
|
33
|
7
|
0
|
19
|
$_[0]->{+UNPACKED_UPLOADS} ||= do { |
59
|
7
|
|
|
|
|
447
|
require Web::Dispatch::Upload; require HTTP::Headers; |
|
7
|
|
|
|
|
18
|
|
60
|
7
|
|
|
|
|
12
|
my ($final, $reason) = ( |
61
|
|
|
|
|
|
|
{}, "field %s exists with value %s but body was not multipart/form-data" |
62
|
|
|
|
|
|
|
); |
63
|
7
|
100
|
|
|
|
12
|
if (my $body = get_unpacked_body_object_from($_[0])) { |
64
|
3
|
|
|
|
|
11
|
my $u = $body->upload; |
65
|
3
|
|
|
|
|
13
|
$reason = "field %s exists with value %s but was not an upload"; |
66
|
3
|
|
|
|
|
8
|
foreach my $k (keys %$u) { |
67
|
1
|
50
|
|
|
|
4
|
foreach my $v (ref($u->{$k}) eq 'ARRAY' ? @{$u->{$k}} : $u->{$k}) { |
|
0
|
|
|
|
|
0
|
|
68
|
1
|
|
50
|
|
|
7
|
push(@{$final->{$k}||=[]}, Web::Dispatch::Upload->new( |
69
|
1
|
|
|
|
|
7
|
%{$v}, |
70
|
|
|
|
|
|
|
headers => HTTP::Headers->new($v->{headers}) |
71
|
1
|
|
|
|
|
2
|
)); |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
} |
75
|
7
|
|
|
|
|
64
|
my $b = get_unpacked_body_from($_[0]); |
76
|
7
|
|
|
|
|
16
|
foreach my $k (keys %$b) { |
77
|
5
|
50
|
|
|
|
12
|
next if $final->{$k}; |
78
|
5
|
|
|
|
|
6
|
foreach my $v (@{$b->{$k}}) { |
|
5
|
|
|
|
|
9
|
|
79
|
5
|
50
|
|
|
|
9
|
next unless $v; |
80
|
5
|
|
50
|
|
|
2
|
push(@{$final->{$k}||=[]}, Web::Dispatch::NotAnUpload->new( |
|
5
|
|
|
|
|
55
|
|
81
|
|
|
|
|
|
|
filename => $v, |
82
|
|
|
|
|
|
|
reason => sprintf($reason, $k, $v) |
83
|
|
|
|
|
|
|
)); |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
} |
86
|
7
|
|
|
|
|
25
|
$final; |
87
|
|
|
|
|
|
|
}; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
{ |
91
|
|
|
|
|
|
|
# shamelessly stolen from HTTP::Body::UrlEncoded by Christian Hansen |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
my $DECODE = qr/%([0-9a-fA-F]{2})/; |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
my %hex_chr; |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
foreach my $num ( 0 .. 255 ) { |
98
|
|
|
|
|
|
|
my $h = sprintf "%02X", $num; |
99
|
|
|
|
|
|
|
$hex_chr{ lc $h } = $hex_chr{ uc $h } = chr $num; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
sub _unpack_params { |
103
|
108
|
|
|
108
|
|
128
|
my %unpack; |
104
|
108
|
|
|
|
|
501
|
(my $params = $_[0]) =~ s/\+/ /g; |
105
|
108
|
|
|
|
|
134
|
my ($name, $value); |
106
|
108
|
|
|
|
|
1106
|
foreach my $pair (split(/[&;](?:\s+)?/, $params)) { |
107
|
853
|
100
|
|
|
|
2775
|
$value = 1 unless (($name, $value) = split(/=/, $pair, 2)) == 2; |
108
|
|
|
|
|
|
|
|
109
|
853
|
|
|
|
|
17122
|
s/$DECODE/$hex_chr{$1}/gs for ($name, $value); |
110
|
853
|
|
|
|
|
1957
|
$_ = decode_utf8 $_ for ($name, $value); |
111
|
|
|
|
|
|
|
|
112
|
853
|
|
100
|
|
|
44684
|
push(@{$unpack{$name}||=[]}, $value); |
|
853
|
|
|
|
|
4488
|
|
113
|
|
|
|
|
|
|
} |
114
|
108
|
|
|
|
|
747
|
\%unpack; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
{ |
119
|
|
|
|
|
|
|
# shamelessly stolen from Plack::Request by miyagawa |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
sub _make_http_body { |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
# Can't actually do this yet, since Plack::Request deletes the |
124
|
|
|
|
|
|
|
# header structure out of the uploads in its copy of the body. |
125
|
|
|
|
|
|
|
# I suspect I need to supply miyagawa with a failing test. |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
#if (my $plack_body = $_[0]->{'plack.request.http.body'}) { |
128
|
|
|
|
|
|
|
# # Plack already constructed one; probably wasteful to do it again |
129
|
|
|
|
|
|
|
# return $plack_body; |
130
|
|
|
|
|
|
|
#} |
131
|
|
|
|
|
|
|
|
132
|
4
|
|
|
4
|
|
433
|
require HTTP::Body; |
133
|
4
|
|
|
|
|
14838
|
my $body = HTTP::Body->new(@{$_[0]}{qw(CONTENT_TYPE CONTENT_LENGTH)}); |
|
4
|
|
|
|
|
21
|
|
134
|
4
|
|
|
|
|
295
|
$body->cleanup(1); |
135
|
4
|
|
|
|
|
15
|
my $spin = 0; |
136
|
4
|
|
|
|
|
7
|
my $input = $_[0]->{'psgi.input'}; |
137
|
4
|
|
|
|
|
6
|
my $cl = $_[0]->{CONTENT_LENGTH}; |
138
|
4
|
|
|
|
|
8
|
while ($cl) { |
139
|
4
|
50
|
|
|
|
22
|
$input->read(my $chunk, $cl < 8192 ? $cl : 8192); |
140
|
4
|
|
|
|
|
24
|
my $read = length $chunk; |
141
|
4
|
|
|
|
|
4
|
$cl -= $read; |
142
|
4
|
|
|
|
|
10
|
$body->add($chunk); |
143
|
|
|
|
|
|
|
|
144
|
4
|
50
|
33
|
|
|
1447
|
if ($read == 0 && $spin++ > 2000) { |
145
|
0
|
|
|
|
|
0
|
require Carp; |
146
|
0
|
|
|
|
|
0
|
Carp::croak("Bad Content-Length: maybe client disconnect? ($cl bytes remaining)"); |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
} |
149
|
4
|
|
|
|
|
26
|
return $body; |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
1; |