line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package HTTP::Headers::Util; |
2
|
|
|
|
|
|
|
|
3
|
13
|
|
|
13
|
|
68812
|
use strict; |
|
13
|
|
|
|
|
34
|
|
|
13
|
|
|
|
|
501
|
|
4
|
13
|
|
|
13
|
|
72
|
use warnings; |
|
13
|
|
|
|
|
29
|
|
|
13
|
|
|
|
|
792
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
our $VERSION = '6.44'; |
7
|
|
|
|
|
|
|
|
8
|
13
|
|
|
13
|
|
84
|
use Exporter 5.57 'import'; |
|
13
|
|
|
|
|
291
|
|
|
13
|
|
|
|
|
10743
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
our @EXPORT_OK=qw(split_header_words _split_header_words join_header_words); |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
sub split_header_words { |
14
|
136
|
|
|
136
|
1
|
9506
|
my @res = &_split_header_words; |
15
|
136
|
|
|
|
|
257
|
for my $arr (@res) { |
16
|
142
|
|
|
|
|
388
|
for (my $i = @$arr - 2; $i >= 0; $i -= 2) { |
17
|
183
|
|
|
|
|
573
|
$arr->[$i] = lc($arr->[$i]); |
18
|
|
|
|
|
|
|
} |
19
|
|
|
|
|
|
|
} |
20
|
136
|
|
|
|
|
407
|
return @res; |
21
|
|
|
|
|
|
|
} |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
sub _split_header_words |
24
|
|
|
|
|
|
|
{ |
25
|
136
|
|
|
136
|
|
296
|
my(@val) = @_; |
26
|
136
|
|
|
|
|
209
|
my @res; |
27
|
136
|
|
|
|
|
330
|
for (@val) { |
28
|
137
|
|
|
|
|
192
|
my @cur; |
29
|
137
|
|
|
|
|
312
|
while (length) { |
30
|
235
|
100
|
100
|
|
|
1412
|
if (s/^\s*(=*[^\s=;,]+)//) { # 'token' or parameter 'attribute' |
|
|
100
|
66
|
|
|
|
|
|
|
50
|
|
|
|
|
|
31
|
183
|
|
|
|
|
579
|
push(@cur, $1); |
32
|
|
|
|
|
|
|
# a quoted value |
33
|
183
|
100
|
|
|
|
684
|
if (s/^\s*=\s*\"([^\"\\]*(?:\\.[^\"\\]*)*)\"//) { |
|
|
100
|
|
|
|
|
|
34
|
10
|
|
|
|
|
24
|
my $val = $1; |
35
|
10
|
|
|
|
|
25
|
$val =~ s/\\(.)/$1/g; |
36
|
10
|
|
|
|
|
31
|
push(@cur, $val); |
37
|
|
|
|
|
|
|
# some unquoted value |
38
|
|
|
|
|
|
|
} |
39
|
|
|
|
|
|
|
elsif (s/^\s*=\s*([^;,\s]*)//) { |
40
|
40
|
|
|
|
|
90
|
my $val = $1; |
41
|
40
|
|
|
|
|
75
|
$val =~ s/\s+$//; |
42
|
40
|
|
|
|
|
116
|
push(@cur, $val); |
43
|
|
|
|
|
|
|
# no value, a lone token |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
else { |
46
|
133
|
|
|
|
|
387
|
push(@cur, undef); |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
elsif (s/^\s*,//) { |
50
|
9
|
100
|
|
|
|
28
|
push(@res, [@cur]) if @cur; |
51
|
9
|
|
|
|
|
23
|
@cur = (); |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
elsif (s/^\s*;// || s/^\s+// || s/^=//) { |
54
|
|
|
|
|
|
|
# continue |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
else { |
57
|
0
|
|
|
|
|
0
|
die "This should not happen: '$_'"; |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
} |
60
|
137
|
100
|
|
|
|
452
|
push(@res, \@cur) if @cur; |
61
|
|
|
|
|
|
|
} |
62
|
136
|
|
|
|
|
331
|
@res; |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
sub join_header_words |
67
|
|
|
|
|
|
|
{ |
68
|
31
|
100
|
100
|
31
|
1
|
1262
|
@_ = ([@_]) if @_ && !ref($_[0]); |
69
|
31
|
|
|
|
|
54
|
my @res; |
70
|
31
|
|
|
|
|
59
|
for (@_) { |
71
|
33
|
|
|
|
|
83
|
my @cur = @$_; |
72
|
33
|
|
|
|
|
48
|
my @attr; |
73
|
33
|
|
|
|
|
73
|
while (@cur) { |
74
|
57
|
|
|
|
|
87
|
my $k = shift @cur; |
75
|
57
|
|
|
|
|
88
|
my $v = shift @cur; |
76
|
57
|
100
|
|
|
|
112
|
if (defined $v) { |
77
|
31
|
100
|
100
|
|
|
130
|
if ($v =~ /[\x00-\x20()<>@,;:\\\"\/\[\]?={}\x7F-\xFF]/ || !length($v)) { |
78
|
5
|
|
|
|
|
27
|
$v =~ s/([\"\\])/\\$1/g; # escape " and \ |
79
|
5
|
|
|
|
|
13
|
$k .= qq(="$v"); |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
else { |
82
|
|
|
|
|
|
|
# token |
83
|
26
|
|
|
|
|
67
|
$k .= "=$v"; |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
} |
86
|
57
|
|
|
|
|
158
|
push(@attr, $k); |
87
|
|
|
|
|
|
|
} |
88
|
33
|
100
|
|
|
|
121
|
push(@res, join("; ", @attr)) if @attr; |
89
|
|
|
|
|
|
|
} |
90
|
31
|
|
|
|
|
135
|
join(", ", @res); |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
1; |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=pod |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=encoding UTF-8 |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=head1 NAME |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
HTTP::Headers::Util - Header value parsing utility functions |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=head1 VERSION |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
version 6.44 |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=head1 SYNOPSIS |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
use HTTP::Headers::Util qw(split_header_words); |
111
|
|
|
|
|
|
|
@values = split_header_words($h->header("Content-Type")); |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
=head1 DESCRIPTION |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
This module provides a few functions that helps parsing and |
116
|
|
|
|
|
|
|
construction of valid HTTP header values. None of the functions are |
117
|
|
|
|
|
|
|
exported by default. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
The following functions are available: |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=over 4 |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
=item split_header_words( @header_values ) |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
This function will parse the header values given as argument into a |
126
|
|
|
|
|
|
|
list of anonymous arrays containing key/value pairs. The function |
127
|
|
|
|
|
|
|
knows how to deal with ",", ";" and "=" as well as quoted values after |
128
|
|
|
|
|
|
|
"=". A list of space separated tokens are parsed as if they were |
129
|
|
|
|
|
|
|
separated by ";". |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
If the @header_values passed as argument contains multiple values, |
132
|
|
|
|
|
|
|
then they are treated as if they were a single value separated by |
133
|
|
|
|
|
|
|
comma ",". |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
This means that this function is useful for parsing header fields that |
136
|
|
|
|
|
|
|
follow this syntax (BNF as from the HTTP/1.1 specification, but we relax |
137
|
|
|
|
|
|
|
the requirement for tokens). |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
headers = #header |
140
|
|
|
|
|
|
|
header = (token | parameter) *( [";"] (token | parameter)) |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
token = 1* |
143
|
|
|
|
|
|
|
separators = "(" | ")" | "<" | ">" | "@" |
144
|
|
|
|
|
|
|
| "," | ";" | ":" | "\" | <"> |
145
|
|
|
|
|
|
|
| "/" | "[" | "]" | "?" | "=" |
146
|
|
|
|
|
|
|
| "{" | "}" | SP | HT |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) |
149
|
|
|
|
|
|
|
qdtext = > |
150
|
|
|
|
|
|
|
quoted-pair = "\" CHAR |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
parameter = attribute "=" value |
153
|
|
|
|
|
|
|
attribute = token |
154
|
|
|
|
|
|
|
value = token | quoted-string |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
Each I is represented by an anonymous array of key/value |
157
|
|
|
|
|
|
|
pairs. The keys will be all be forced to lower case. |
158
|
|
|
|
|
|
|
The value for a simple token (not part of a parameter) is C. |
159
|
|
|
|
|
|
|
Syntactically incorrect headers will not necessarily be parsed as you |
160
|
|
|
|
|
|
|
would want. |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
This is easier to describe with some examples: |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
split_header_words('foo="bar"; port="80,81"; DISCARD, BAR=baz'); |
165
|
|
|
|
|
|
|
split_header_words('text/html; charset="iso-8859-1"'); |
166
|
|
|
|
|
|
|
split_header_words('Basic realm="\\"foo\\\\bar\\""'); |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
will return |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
[foo=>'bar', port=>'80,81', discard=> undef], [bar=>'baz' ] |
171
|
|
|
|
|
|
|
['text/html' => undef, charset => 'iso-8859-1'] |
172
|
|
|
|
|
|
|
[basic => undef, realm => "\"foo\\bar\""] |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
If you don't want the function to convert tokens and attribute keys to |
175
|
|
|
|
|
|
|
lower case you can call it as C<_split_header_words> instead (with a |
176
|
|
|
|
|
|
|
leading underscore). |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=item join_header_words( @arrays ) |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
This will do the opposite of the conversion done by split_header_words(). |
181
|
|
|
|
|
|
|
It takes a list of anonymous arrays as arguments (or a list of |
182
|
|
|
|
|
|
|
key/value pairs) and produces a single header value. Attribute values |
183
|
|
|
|
|
|
|
are quoted if needed. |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
Example: |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
join_header_words(["text/plain" => undef, charset => "iso-8859/1"]); |
188
|
|
|
|
|
|
|
join_header_words("text/plain" => undef, charset => "iso-8859/1"); |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
will both return the string: |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
text/plain; charset="iso-8859/1" |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=back |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=head1 AUTHOR |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
Gisle Aas |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
This software is copyright (c) 1994 by Gisle Aas. |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
205
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=cut |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
__END__ |