File Coverage

blib/lib/HTTP/Headers/Util.pm
Criterion Covered Total %
statement 49 50 98.0
branch 21 22 95.4
condition 11 12 91.6
subroutine 6 6 100.0
pod 2 2 100.0
total 89 92 96.7


line stmt bran cond sub pod time code
1             package HTTP::Headers::Util;
2              
3 13     13   68693 use strict;
  13         39  
  13         507  
4 13     13   72 use warnings;
  13         24  
  13         786  
5              
6             our $VERSION = '6.45';
7              
8 13     13   82 use Exporter 5.57 'import';
  13         279  
  13         10749  
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 7393 my @res = &_split_header_words;
15 136         244 for my $arr (@res) {
16 142         388 for (my $i = @$arr - 2; $i >= 0; $i -= 2) {
17 183         527 $arr->[$i] = lc($arr->[$i]);
18             }
19             }
20 136         342 return @res;
21             }
22              
23             sub _split_header_words
24             {
25 136     136   286 my(@val) = @_;
26 136         216 my @res;
27 136         253 for (@val) {
28 137         183 my @cur;
29 137         313 while (length) {
30 235 100 100     1419 if (s/^\s*(=*[^\s=;,]+)//) { # 'token' or parameter 'attribute'
    100 66        
    50          
31 183         560 push(@cur, $1);
32             # a quoted value
33 183 100       633 if (s/^\s*=\s*\"([^\"\\]*(?:\\.[^\"\\]*)*)\"//) {
    100          
34 10         21 my $val = $1;
35 10         27 $val =~ s/\\(.)/$1/g;
36 10         29 push(@cur, $val);
37             # some unquoted value
38             }
39             elsif (s/^\s*=\s*([^;,\s]*)//) {
40 40         85 my $val = $1;
41 40         80 $val =~ s/\s+$//;
42 40         114 push(@cur, $val);
43             # no value, a lone token
44             }
45             else {
46 133         335 push(@cur, undef);
47             }
48             }
49             elsif (s/^\s*,//) {
50 9 100       30 push(@res, [@cur]) if @cur;
51 9         25 @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       444 push(@res, \@cur) if @cur;
61             }
62 136         332 @res;
63             }
64              
65              
66             sub join_header_words
67             {
68 31 100 100 31 1 1000 @_ = ([@_]) if @_ && !ref($_[0]);
69 31         47 my @res;
70 31         56 for (@_) {
71 33         84 my @cur = @$_;
72 33         47 my @attr;
73 33         64 while (@cur) {
74 57         112 my $k = shift @cur;
75 57         80 my $v = shift @cur;
76 57 100       107 if (defined $v) {
77 31 100 100     124 if ($v =~ /[\x00-\x20()<>@,;:\\\"\/\[\]?={}\x7F-\xFF]/ || !length($v)) {
78 5         27 $v =~ s/([\"\\])/\\$1/g; # escape " and \
79 5         14 $k .= qq(="$v");
80             }
81             else {
82             # token
83 26         65 $k .= "=$v";
84             }
85             }
86 57         147 push(@attr, $k);
87             }
88 33 100       135 push(@res, join("; ", @attr)) if @attr;
89             }
90 31         111 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.45
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__