File Coverage

blib/lib/HTTP/XSHeaders.pm
Criterion Covered Total %
statement 115 124 92.7
branch 48 62 77.4
condition 12 18 66.6
subroutine 24 27 88.8
pod 13 14 92.8
total 212 245 86.5


line stmt bran cond sub pod time code
1             package HTTP::XSHeaders;
2 9     9   446546 use strict;
  9         78  
  9         252  
3 9     9   42 use warnings;
  9         12  
  9         196  
4 9     9   39 use XSLoader;
  9         13  
  9         2911  
5              
6             our $VERSION = '0.400004';
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   60 no warnings qw;
  9         17  
  9         628  
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         20  
  9         1148  
70 24     24   477 *{$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   187 use 5.00800;
  9         28  
83 9     9   51 use Carp ();
  9         16  
  9         12529  
84              
85             sub _date_header {
86 16     16   669 require HTTP::Date;
87 16         4629 my ( $self, $header, $time ) = @_;
88 16         25 my $old;
89 16 100       33 if ( defined $time ) {
90 5         17 ($old) = $self->header($header, HTTP::Date::time2str($time));
91             } else {
92 11         56 ($old) = $self->header($header);
93             }
94 16 100       267 $old =~ s/;.*// if defined($old);
95 16         42 HTTP::Date::str2time($old);
96             }
97              
98             sub content_type {
99 20     20 1 610 my $self = shift;
100 20         79 my $ct = $self->header('content-type');
101 20 100       106 $self->header('content-type', shift) if @_;
102 20 50       42 $ct = $ct->[0] if ref($ct) eq 'ARRAY';
103 20 100 66     79 return '' unless defined($ct) && length($ct);
104 16         58 my @ct = split( /;\s*/, $ct, 2 );
105 16         31 for ( $ct[0] ) {
106 16         45 s/\s+//g;
107 16         36 $_ = lc($_);
108             }
109 16 100       72 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     19 if (s/^\s*(=*[^\s=;,]+)//) { # 'token' or parameter 'attribute'
    50          
    50          
121 2         6 push(@cur, $1);
122             # a quoted value
123 2 100       10 if (s/^\s*=\s*\"([^\"\\]*(?:\\.[^\"\\]*)*)\"//) {
    50          
124 1         2 my $val = $1;
125 1         3 $val =~ s/\\(.)/$1/g;
126 1         2 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         3 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       6 push(@res, \@cur) if @cur;
151             }
152              
153 2         3 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         5 return @res;
159             }
160              
161             sub content_type_charset {
162 2     2 1 681 my $self = shift;
163 2         10 my $h = $self->header('content-type');
164 2 50       5 $h = $h->[0] if ref($h);
165 2 100       5 $h = "" unless defined $h;
166 2         7 my @v = _split_header_words($h);
167 2 100       4 if (@v) {
168 1         1 my($ct, undef, %ct_param) = @{$v[0]};
  1         4  
169 1         2 my $charset = $ct_param{charset};
170 1 50       2 if ($ct) {
171 1         2 $ct = lc($ct);
172 1         3 $ct =~ s/\s+//;
173             }
174 1 50       12 if ($charset) {
175 1         2 $charset = uc($charset);
176 1         3 $charset =~ s/^\s+//; $charset =~ s/\s+\z//;
  1         2  
177 1 50       9 undef($charset) if $charset eq "";
178             }
179 1 50       2 return $ct, $charset if wantarray;
180 1         6 return $charset;
181             }
182 1 50       2 return undef, undef if wantarray; ## no critic
183 1         4 return undef; ## no critic
184             }
185              
186             sub referer {
187 8     8 1 7614 my $self = shift;
188 8 100 100     94 if ( @_ && $_[0] =~ /#/ ) {
189              
190             # Strip fragment per RFC 2616, section 14.36.
191 2         14 my $uri = shift;
192 2 100       8 if ( ref($uri) ) {
193 1         6 require URI;
194 1         7 $uri = $uri->clone;
195 1         12 $uri->fragment(undef);
196             }
197             else {
198 1         6 $uri =~ s/\#.*//;
199             }
200 2         23 unshift @_, $uri;
201             }
202 8         108 ( $self->header( 'Referer', @_ ) )[0];
203             }
204              
205             *referrer = \&referer;
206              
207 7     7 1 215 sub authorization_basic { shift->_basic_auth( "Authorization", @_ ) }
208             sub proxy_authorization_basic {
209 2     2 1 66 shift->_basic_auth( "Proxy-Authorization", @_ );
210             }
211              
212             sub _basic_auth {
213 9     9   543 require MIME::Base64;
214 9         705 my ( $self, $h, $user, $passwd ) = @_;
215 9         45 my ($old) = $self->header($h);
216 9 100       20 if ( defined $user ) {
217 4 100       233 Carp::croak("Basic authorization user name can't contain ':'")
218             if $user =~ /:/;
219 3 100       8 $passwd = '' unless defined $passwd;
220 3         32 $self->header(
221             $h => 'Basic ' . MIME::Base64::encode( "$user:$passwd", '' ) );
222             }
223 8 100 66     52 if ( defined $old && $old =~ s/^\s*Basic\s+// ) {
224 5         17 my $val = MIME::Base64::decode($old);
225 5 100       20 return $val unless wantarray;
226 3         16 return split( /:/, $val, 2 );
227             }
228 3         15 return;
229             }
230              
231 3     3 1 10258 sub date { shift->_date_header( 'date', @_ ); }
232 3     3 1 481 sub expires { shift->_date_header( 'expires', @_ ); }
233 4     4 1 640 sub if_modified_since { shift->_date_header( 'if-modified-since', @_ ); }
234 3     3 1 431 sub if_unmodified_since { shift->_date_header( 'if-unmodified-since', @_ ); }
235 3     3 1 412 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_html {
242 2     2 1 110 my $self = shift;
243 2   66     6 return $self->content_type eq 'text/html' || $self->content_is_xhtml;
244             }
245              
246             sub content_is_xhtml {
247 3     3 1 9 my $ct = shift->content_type;
248 3   66     21 return $ct eq "application/xhtml+xml"
249             || $ct eq "application/vnd.wap.xhtml+xml";
250             }
251              
252             sub content_is_xml {
253 2     2 1 6 my $ct = shift->content_type;
254 2 50       7 return 1 if $ct eq "text/xml";
255 2 50       6 return 1 if $ct eq "application/xml";
256 2 100       11 return 1 if $ct =~ /\+xml$/;
257 1         21 return 0;
258             }
259              
260             1;
261              
262             __END__