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   179629 use strict;
  9         20  
  9         240  
3 9     9   85 use warnings;
  9         18  
  9         255  
4 9     9   44 use XSLoader;
  9         18  
  9         4225  
5              
6             our $VERSION = '0.400003';
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   45 no warnings qw;
  9         18  
  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   45 no strict 'refs'; ## no critic
  9         16  
  9         1333  
70 24     24   468 *{$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   248 use 5.00800;
  9         31  
83 9     9   46 use Carp ();
  9         18  
  9         14866  
84              
85             sub _date_header {
86 16     16   143668 require HTTP::Date;
87 16         183527 my ( $self, $header, $time ) = @_;
88 16         30 my $old;
89 16 100       54 if ( defined $time ) {
90 5         26 ($old) = $self->header($header, HTTP::Date::time2str($time));
91             } else {
92 11         109 ($old) = $self->header($header);
93             }
94 16 100       399 $old =~ s/;.*// if defined($old);
95 16         70 HTTP::Date::str2time($old);
96             }
97              
98             sub content_type {
99 20     20 1 631 my $self = shift;
100 20         93 my $ct = $self->header('content-type');
101 20 100       220 $self->header('content-type', shift) if @_;
102 20 50       67 $ct = $ct->[0] if ref($ct) eq 'ARRAY';
103 20 100 66     141 return '' unless defined($ct) && length($ct);
104 16         83 my @ct = split( /;\s*/, $ct, 2 );
105 16         46 for ( $ct[0] ) {
106 16         62 s/\s+//g;
107 16         52 $_ = lc($_);
108             }
109 16 100       121 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         5 for (@val) {
118 2         2 my @cur;
119 2         7 while (length) {
120 3 100 33     32 if (s/^\s*(=*[^\s=;,]+)//) { # 'token' or parameter 'attribute'
    50          
    50          
121 2         5 push(@cur, $1);
122             # a quoted value
123 2 100       14 if (s/^\s*=\s*\"([^\"\\]*(?:\\.[^\"\\]*)*)\"//) {
    50          
124 1         2 my $val = $1;
125 1         2 $val =~ s/\\(.)/$1/g;
126 1         4 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         4 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       9 push(@res, \@cur) if @cur;
151             }
152              
153 2         5 for my $arr (@res) {
154 1         4 for (my $i = @$arr - 2; $i >= 0; $i -= 2) {
155 2         8 $arr->[$i] = lc($arr->[$i]);
156             }
157             }
158 2         6 return @res;
159             }
160              
161             sub content_type_charset {
162 2     2 1 204 my $self = shift;
163 2         15 my $h = $self->header('content-type');
164 2 50       24 $h = $h->[0] if ref($h);
165 2 100       26 $h = "" unless defined $h;
166 2         6 my @v = _split_header_words($h);
167 2 100       5 if (@v) {
168 1         2 my($ct, undef, %ct_param) = @{$v[0]};
  1         4  
169 1         3 my $charset = $ct_param{charset};
170 1 50       3 if ($ct) {
171 1         3 $ct = lc($ct);
172 1         3 $ct =~ s/\s+//;
173             }
174 1 50       3 if ($charset) {
175 1         2 $charset = uc($charset);
176 1         3 $charset =~ s/^\s+//; $charset =~ s/\s+\z//;
  1         2  
177 1 50       3 undef($charset) if $charset eq "";
178             }
179 1 50       3 return $ct, $charset if wantarray;
180 1         6 return $charset;
181             }
182 1 50       3 return undef, undef if wantarray; ## no critic
183 1         6 return undef; ## no critic
184             }
185              
186             sub referer {
187 8     8 1 300222 my $self = shift;
188 8 100 100     116 if ( @_ && $_[0] =~ /#/ ) {
189              
190             # Strip fragment per RFC 2616, section 14.36.
191 2         17 my $uri = shift;
192 2 100       8 if ( ref($uri) ) {
193 1         13 require URI;
194 1         11 $uri = $uri->clone;
195 1         13 $uri->fragment(undef);
196             }
197             else {
198 1         5 $uri =~ s/\#.*//;
199             }
200 2         20 unshift @_, $uri;
201             }
202 8         77 ( $self->header( 'Referer', @_ ) )[0];
203             }
204              
205             *referrer = \&referer;
206              
207 7     7 1 167 sub authorization_basic { shift->_basic_auth( "Authorization", @_ ) }
208             sub proxy_authorization_basic {
209 2     2 1 50 shift->_basic_auth( "Proxy-Authorization", @_ );
210             }
211              
212             sub _basic_auth {
213 9     9   1081 require MIME::Base64;
214 9         159281 my ( $self, $h, $user, $passwd ) = @_;
215 9         43 my ($old) = $self->header($h);
216 9 100       25 if ( defined $user ) {
217 4 100       439 Carp::croak("Basic authorization user name can't contain ':'")
218             if $user =~ /:/;
219 3 100       9 $passwd = '' unless defined $passwd;
220 3         64 $self->header(
221             $h => 'Basic ' . MIME::Base64::encode( "$user:$passwd", '' ) );
222             }
223 8 100 66     63 if ( defined $old && $old =~ s/^\s*Basic\s+// ) {
224 5         14 my $val = MIME::Base64::decode($old);
225 5 100       21 return $val unless wantarray;
226 3         17 return split( /:/, $val, 2 );
227             }
228 3         18 return;
229             }
230              
231 3     3 1 236206 sub date { shift->_date_header( 'date', @_ ); }
232 3     3 1 585 sub expires { shift->_date_header( 'expires', @_ ); }
233 4     4 1 766 sub if_modified_since { shift->_date_header( 'if-modified-since', @_ ); }
234 3     3 1 662 sub if_unmodified_since { shift->_date_header( 'if-unmodified-since', @_ ); }
235 3     3 1 830 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 133 my $self = shift;
243 2   66     8 return $self->content_type eq 'text/html' || $self->content_is_xhtml;
244             }
245              
246             sub content_is_xhtml {
247 3     3 1 10 my $ct = shift->content_type;
248 3   66     31 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 8 my $ct = shift->content_type;
254 2 50       9 return 1 if $ct eq "text/xml";
255 2 50       9 return 1 if $ct eq "application/xml";
256 2 100       15 return 1 if $ct =~ /\+xml$/;
257 1         6 return 0;
258             }
259              
260             1;
261              
262             __END__