File Coverage

blib/lib/HTTP/MessageParser.pm
Criterion Covered Total %
statement 50 50 100.0
branch 20 28 71.4
condition 2 2 100.0
subroutine 9 9 100.0
pod 6 6 100.0
total 87 95 91.5


line stmt bran cond sub pod time code
1             package HTTP::MessageParser;
2              
3 4     4   5435 use strict;
  4         8  
  4         159  
4 4     4   21 use warnings;
  4         8  
  4         239  
5              
6             our $VERSION = 0.3;
7              
8 4     4   30 use Carp qw[];
  4         7  
  4         4099  
9              
10             {
11             require Sub::Exporter;
12              
13             my $exporter = sub {
14             my ( $class, $method ) = @_;
15             return sub { return $class->$method(@_) };
16             };
17              
18             my %exports = map { $_ => $exporter } qw(
19             parse_headers
20             parse_request
21             parse_request_line
22             parse_response
23             parse_response_line
24             parse_version
25             );
26              
27             Sub::Exporter->import( -setup => { exports => \%exports } );
28             }
29              
30             {
31             # http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
32             # http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
33             # http://lists.w3.org/Archives/Public/ietf-http-wg/2004JanMar/thread.html#50
34             # http://lists.w3.org/Archives/Public/ietf-http-wg/2005AprJun/0016.html
35              
36             my $CRLF = qr/\x0D?\x0A/;
37             my $LWS = qr/$CRLF[\x09\x20]|[\x09\x20]/;
38             my $TEXT = qr/[\x20-\xFF]/;
39             my $Token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
40             my $Header = qr/($Token+)$LWS*:$LWS*((?:$TEXT|$LWS)*)$CRLF/;
41             my $Version = qr/HTTP\/[0-9]+\.[0-9]+/;
42             my $Request = qr/(?:$CRLF)*($Token+)[\x09\x20]+([\x21-\xFF]+)(?:[\x09\x20]+($Version))?$CRLF/;
43             my $Response = qr/($Version)[\x09\x20]+([0-9]{3})[\x09\x20]+($TEXT*)$CRLF/;
44              
45             sub parse_request ($$) {
46 19     19 1 14705 my $class = shift;
47 19 50       50 my $string = ref $_[0] ? shift : \( my $copy = shift );
48              
49 19         41 my @request = $class->parse_request_line($string);
50 16         46 my $version = $class->parse_version( $request[2] );
51 16         29 my $headers = [];
52              
53 16 100       37 if ( $version >= 1000 ) {
54              
55 14         30 $headers = $class->parse_headers($string);
56              
57 14 100       475 $$string =~ s/^$CRLF//o
58             or Carp::croak('Bad Request');
59             }
60             else {
61              
62 2 100       109 $$string eq ''
63             or Carp::croak('Bad Request');
64             }
65              
66 12         64 return ( @request, $headers, $string );
67             }
68              
69             sub parse_request_line ($$) {
70 19     19 1 26 my $class = shift;
71 19 50       34 my $string = ref $_[0] ? shift : \( my $copy = shift );
72              
73 19 100       545 $$string =~ s/^$Request//o
74             or Carp::croak('Bad Request-Line');
75              
76 16   100     122 return ( $1, $2, $3 || 'HTTP/0.9' );
77             }
78              
79             sub parse_response ($$) {
80 13     13 1 11817 my $class = shift;
81 13 50       36 my $string = ref $_[0] ? shift : \( my $copy = shift );
82              
83 13         31 my @response = $class->parse_response_line($string);
84 12         34 my $headers = $class->parse_headers($string);
85              
86 12 100       538 $$string =~ s/^$CRLF//o
87             or Carp::croak('Bad Response');
88              
89 9         55 return ( @response, $headers, $string );
90             }
91              
92             # Yes, I know it's status_line, but response_line fits better with API ;)
93             sub parse_response_line ($$) {
94 13     13 1 15 my $class = shift;
95 13 50       27 my $string = ref $_[0] ? shift : \( my $copy = shift );
96              
97 13 100       228 $$string =~ s/^$Response//o
98             or Carp::croak('Bad Status-Line');
99              
100 12         63 return ( $1, $2, $3 );
101             }
102              
103             sub parse_headers ($$) {
104 27     27 1 1015 my $class = shift;
105 27 50       55 my $string = ref $_[0] ? shift : \( my $copy = shift );
106              
107 27         43 my @headers = ();
108              
109 27         360 while ( $$string =~ s/^$Header//o ) {
110 24         249 push @headers, lc $1 => $2;
111             }
112              
113 27         55 foreach ( @headers ) {
114 48         661 s/$LWS+/\x20/og;
115 48         161 s/^$LWS//o;
116 48         181 s/$LWS$//o;
117             }
118              
119 27 50       90 return wantarray ? @headers : \@headers;
120             }
121              
122             sub parse_version ($$) {
123 16     16 1 22 my $class = shift;
124 16         18 my $string = shift;
125              
126 16 50       70 $string =~ m/^HTTP\/([0-9]+)\.([0-9]+)$/
127             or Carp::croak('Bad HTTP-Version');
128              
129 16         28 my $major = $1;
130 16         22 my $minor = $2;
131 16         49 my $number = $major * 1000 + $minor;
132              
133 16 50       44 return wantarray ? ( $major, $minor ) : $number;
134             }
135             }
136              
137             1;
138              
139             __END__