File Coverage

blib/lib/HTTP/XSHeaders.pm
Criterion Covered Total %
statement 115 126 91.2
branch 48 62 77.4
condition 12 18 66.6
subroutine 24 28 85.7
pod 13 15 86.6
total 212 249 85.1


line stmt bran cond sub pod time code
1             package HTTP::XSHeaders;
2 9     9   444138 use strict;
  9         67  
  9         214  
3 9     9   41 use warnings;
  9         14  
  9         183  
4 9     9   45 use XSLoader;
  9         13  
  9         2883  
5              
6             our $VERSION = '0.400005';
7              
8             eval {
9             require HTTP::Headers::Fast;
10              
11             # HTTP::Headers::Fast
12             *HTTP::Headers::Fast::new = *HTTP::XSHeaders::new;
13             *HTTP::Headers::Fast::DESTROY = *HTTP::XSHeaders::DESTROY;
14             *HTTP::Headers::Fast::clone = *HTTP::XSHeaders::clone;
15             *HTTP::Headers::Fast::header = *HTTP::XSHeaders::header;
16             *HTTP::Headers::Fast::_header = *HTTP::XSHeaders::_header;
17             *HTTP::Headers::Fast::clear = *HTTP::XSHeaders::clear;
18             *HTTP::Headers::Fast::push_header = *HTTP::XSHeaders::push_header;
19             *HTTP::Headers::Fast::init_header = *HTTP::XSHeaders::init_header;
20             *HTTP::Headers::Fast::remove_header = *HTTP::XSHeaders::remove_header;
21             *HTTP::Headers::Fast::remove_content_headers = *HTTP::XSHeaders::remove_content_headers;
22             *HTTP::Headers::Fast::as_string = *HTTP::XSHeaders::as_string;
23             *HTTP::Headers::Fast::as_string_without_sort = *HTTP::XSHeaders::as_string_without_sort;
24             *HTTP::Headers::Fast::header_field_names = *HTTP::XSHeaders::header_field_names;
25             *HTTP::Headers::Fast::scan = *HTTP::XSHeaders::scan;
26              
27             # Implemented in Pure-Perl
28             # (candidates to move to XS)
29             *HTTP::Headers::Fast::_date_header = *HTTP::XSHeaders::_date_header;
30             *HTTP::Headers::Fast::content_type = *HTTP::XSHeaders::content_type;
31             *HTTP::Headers::Fast::content_type_charset = *HTTP::XSHeaders::content_type_charset;
32             *HTTP::Headers::Fast::referer = *HTTP::XSHeaders::referer;
33             *HTTP::Headers::Fast::referrer = *HTTP::XSHeaders::referer;
34             *HTTP::Headers::Fast::_basic_auth = *HTTP::XSHeaders::_basic_auth;
35             };
36              
37             eval {
38             require HTTP::Headers;
39              
40             # HTTP::Headers
41             *HTTP::Headers::new = *HTTP::XSHeaders::new;
42             *HTTP::Headers::clone = *HTTP::XSHeaders::clone;
43             *HTTP::Headers::header = *HTTP::XSHeaders::header;
44             *HTTP::Headers::_header = *HTTP::XSHeaders::_header;
45             *HTTP::Headers::clear = *HTTP::XSHeaders::clear;
46             *HTTP::Headers::push_header = *HTTP::XSHeaders::push_header;
47             *HTTP::Headers::init_header = *HTTP::XSHeaders::init_header;
48             *HTTP::Headers::remove_header = *HTTP::XSHeaders::remove_header;
49             *HTTP::Headers::remove_content_headers = *HTTP::XSHeaders::remove_content_headers;
50             *HTTP::Headers::as_string = *HTTP::XSHeaders::as_string;
51             *HTTP::Headers::header_field_names = *HTTP::XSHeaders::header_field_names;
52             *HTTP::Headers::scan = *HTTP::XSHeaders::scan;
53              
54             # Implemented in Pure-Perl
55             *HTTP::Headers::_date_header = *HTTP::XSHeaders::_date_header;
56             *HTTP::Headers::content_type = *HTTP::XSHeaders::content_type;
57             *HTTP::Headers::content_type_charset = *HTTP::XSHeaders::content_type_charset;
58             *HTTP::Headers::referer = *HTTP::XSHeaders::referer;
59             *HTTP::Headers::referrer = *HTTP::XSHeaders::referer;
60             *HTTP::Headers::_basic_auth = *HTTP::XSHeaders::_basic_auth;
61             };
62              
63             XSLoader::load( 'HTTP::XSHeaders', $VERSION );
64              
65             {
66 9     9   62 no warnings qw;
  9         20  
  9         664  
67             for my $key (qw/content-length content-language content-encoding title user-agent server from warnings www-authenticate authorization proxy-authenticate proxy-authorization/) {
68             (my $meth = $key) =~ s/-/_/g;
69 9     9   51 no strict 'refs'; ## no critic
  9         14  
  9         1135  
70 24     24   350 *{$meth} = sub { (shift->header($key, @_))[0] };
71              
72             *{ "HTTP::Headers::$meth" } = sub {
73 0     0   0 (shift->header($key, @_))[0];
74             };
75              
76             *{ "HTTP::Headers::Fast::$meth" } = sub {
77 0     0   0 (shift->header($key, @_))[0];
78             };
79             }
80             }
81              
82 9     9   201 use 5.00800;
  9         26  
83 9     9   39 use Carp ();
  9         13  
  9         13000  
84              
85             sub _date_header {
86 16     16   465 require HTTP::Date;
87 16         3808 my ( $self, $header, $time ) = @_;
88 16         17 my $old;
89 16 100       32 if ( defined $time ) {
90 5         12 ($old) = $self->header($header, HTTP::Date::time2str($time));
91             } else {
92 11         39 ($old) = $self->header($header);
93             }
94 16 100       126 $old =~ s/;.*// if defined($old);
95 16         31 HTTP::Date::str2time($old);
96             }
97              
98             sub content_type {
99 20     20 1 450 my $self = shift;
100 20         56 my $ct = $self->header('content-type');
101 20 100       94 $self->header('content-type', shift) if @_;
102 20 50       35 $ct = $ct->[0] if ref($ct) eq 'ARRAY';
103 20 100 66     60 return '' unless defined($ct) && length($ct);
104 16         42 my @ct = split( /;\s*/, $ct, 2 );
105 16         25 for ( $ct[0] ) {
106 16         34 s/\s+//g;
107 16         26 $_ = lc($_);
108             }
109 16 100       49 wantarray ? @ct : $ct[0];
110             }
111              
112             # This is copied here because it is not a method
113             sub _split_header_words
114             {
115 2     2   4 my(@val) = @_;
116 2         4 my @res;
117 2         3 for (@val) {
118 2         3 my @cur;
119 2         5 while (length) {
120 3 100 33     38 if (s/^\s*(=*[^\s=;,]+)//) { # 'token' or parameter 'attribute'
    50          
    50          
121 2         5 push(@cur, $1);
122             # a quoted value
123 2 100       13 if (s/^\s*=\s*\"([^\"\\]*(?:\\.[^\"\\]*)*)\"//) {
    50          
124 1         2 my $val = $1;
125 1         3 $val =~ s/\\(.)/$1/g;
126 1         3 push(@cur, $val);
127             # some unquoted value
128             }
129             elsif (s/^\s*=\s*([^;,\s]*)//) {
130 0         0 my $val = $1;
131 0         0 $val =~ s/\s+$//;
132 0         0 push(@cur, $val);
133             # no value, a lone token
134             }
135             else {
136 1         2 push(@cur, undef);
137             }
138             }
139             elsif (s/^\s*,//) {
140 0 0       0 push(@res, [@cur]) if @cur;
141 0         0 @cur = ();
142             }
143             elsif (s/^\s*;// || s/^\s+//) {
144             # continue
145             }
146             else {
147 0         0 die "This should not happen: '$_'";
148             }
149             }
150 2 100       5 push(@res, \@cur) if @cur;
151             }
152              
153 2         5 for my $arr (@res) {
154 1         3 for (my $i = @$arr - 2; $i >= 0; $i -= 2) {
155 2         6 $arr->[$i] = lc($arr->[$i]);
156             }
157             }
158 2         6 return @res;
159             }
160              
161             sub content_type_charset {
162 2     2 1 741 my $self = shift;
163 2         10 my $h = $self->header('content-type');
164 2 50       6 $h = $h->[0] if ref($h);
165 2 100       4 $h = "" unless defined $h;
166 2         7 my @v = _split_header_words($h);
167 2 100       4 if (@v) {
168 1         2 my($ct, undef, %ct_param) = @{$v[0]};
  1         4  
169 1         2 my $charset = $ct_param{charset};
170 1 50       4 if ($ct) {
171 1         1 $ct = lc($ct);
172 1         3 $ct =~ s/\s+//;
173             }
174 1 50       2 if ($charset) {
175 1         2 $charset = uc($charset);
176 1         3 $charset =~ s/^\s+//; $charset =~ s/\s+\z//;
  1         2  
177 1 50       2 undef($charset) if $charset eq "";
178             }
179 1 50       3 return $ct, $charset if wantarray;
180 1         5 return $charset;
181             }
182 1 50       3 return undef, undef if wantarray; ## no critic
183 1         15 return undef; ## no critic
184             }
185              
186             sub referer {
187 8     8 1 6093 my $self = shift;
188 8 100 100     72 if ( @_ && $_[0] =~ /#/ ) {
189              
190             # Strip fragment per RFC 2616, section 14.36.
191 2         9 my $uri = shift;
192 2 100       12 if ( ref($uri) ) {
193 1         5 require URI;
194 1         5 $uri = $uri->clone;
195 1         9 $uri->fragment(undef);
196             }
197             else {
198 1         5 $uri =~ s/\#.*//;
199             }
200 2         16 unshift @_, $uri;
201             }
202 8         82 ( $self->header( 'Referer', @_ ) )[0];
203             }
204              
205             *referrer = \&referer;
206              
207 7     7 1 187 sub authorization_basic { shift->_basic_auth( "Authorization", @_ ) }
208             sub proxy_authorization_basic {
209 2     2 1 53 shift->_basic_auth( "Proxy-Authorization", @_ );
210             }
211              
212             sub _basic_auth {
213 9     9   435 require MIME::Base64;
214 9         550 my ( $self, $h, $user, $passwd ) = @_;
215 9         34 my ($old) = $self->header($h);
216 9 100       18 if ( defined $user ) {
217 4 100       150 Carp::croak("Basic authorization user name can't contain ':'")
218             if $user =~ /:/;
219 3 100       6 $passwd = '' unless defined $passwd;
220 3         23 $self->header(
221             $h => 'Basic ' . MIME::Base64::encode( "$user:$passwd", '' ) );
222             }
223 8 100 66     42 if ( defined $old && $old =~ s/^\s*Basic\s+// ) {
224 5         14 my $val = MIME::Base64::decode($old);
225 5 100       15 return $val unless wantarray;
226 3         13 return split( /:/, $val, 2 );
227             }
228 3         11 return;
229             }
230              
231 3     3 1 7962 sub date { shift->_date_header( 'date', @_ ); }
232 3     3 1 371 sub expires { shift->_date_header( 'expires', @_ ); }
233 4     4 1 439 sub if_modified_since { shift->_date_header( 'if-modified-since', @_ ); }
234 3     3 1 292 sub if_unmodified_since { shift->_date_header( 'if-unmodified-since', @_ ); }
235 3     3 1 263 sub last_modified { shift->_date_header( 'last-modified', @_ ); }
236              
237             # This is used as a private LWP extension. The Client-Date header is
238             # added as a timestamp to a response when it has been received.
239 0     0 0 0 sub client_date { shift->_date_header( 'client-date', @_ ); }
240              
241             sub content_is_text {
242 0     0 0 0 my $self = shift;
243 0         0 return $self->content_type =~ m{^text/};
244             }
245              
246             sub content_is_html {
247 2     2 1 87 my $self = shift;
248 2   66     10 return $self->content_type eq 'text/html' || $self->content_is_xhtml;
249             }
250              
251             sub content_is_xhtml {
252 3     3 1 7 my $ct = shift->content_type;
253 3   66     15 return $ct eq "application/xhtml+xml"
254             || $ct eq "application/vnd.wap.xhtml+xml";
255             }
256              
257             sub content_is_xml {
258 2     2 1 5 my $ct = shift->content_type;
259 2 50       4 return 1 if $ct eq "text/xml";
260 2 50       7 return 1 if $ct eq "application/xml";
261 2 100       7 return 1 if $ct =~ /\+xml$/;
262 1         6 return 0;
263             }
264              
265             1;
266              
267             __END__