File Coverage

blib/lib/Protocol/SPDY/Frame.pm
Criterion Covered Total %
statement 54 59 91.5
branch 12 18 66.6
condition 3 4 75.0
subroutine 15 19 78.9
pod 13 13 100.0
total 97 113 85.8


line stmt bran cond sub pod time code
1             package Protocol::SPDY::Frame;
2             {
3             $Protocol::SPDY::Frame::VERSION = '1.000';
4             }
5 3     3   20 use strict;
  3         5  
  3         112  
6 3     3   19 use warnings;
  3         4  
  3         102  
7              
8             =head1 NAME
9              
10             Protocol::SPDY::Frame - support for SPDY frames
11              
12             =head1 VERSION
13              
14             version 1.000
15              
16             =head1 DESCRIPTION
17              
18             Support for SPDY frames. Typically you'd interact with these through the top-level
19             L object.
20              
21             See the L and L subclasses
22             for the two currently-defined frame types.
23              
24             =cut
25              
26 3     3   3019 use Encode;
  3         38078  
  3         303  
27 3     3   25 use Protocol::SPDY::Constants ':all';
  3         8  
  3         770  
28              
29             use overload
30             '""' => 'to_string',
31 26     26   222 bool => sub { 1 },
32 3     3   18 fallback => 1;
  3         7  
  3         40  
33              
34             =head1 METHODS
35              
36             =cut
37              
38             =head2 is_control
39              
40             Returns true if this is a control frame. Recommended over
41             checking ->isa(L) directly.
42              
43             =cut
44              
45 2     2 1 13 sub is_control { !shift->is_data }
46              
47             =head2 is_data
48              
49             Returns true if this is a data frame. Recommended over
50             checking ->isa(L) directly.
51              
52             =cut
53              
54 2 50   2 1 28 sub is_data { shift->isa('Protocol::SPDY::Frame::Data') ? 1 : 0 }
55              
56             =head2 fin
57              
58             Returns true if the FIN flag is set for this frame.
59              
60             =cut
61              
62 47     47 1 298 sub fin { shift->{fin} }
63              
64             =head2 new
65              
66             Instantiate a new frame. Typically called as a super method
67             from the L or L
68             subclass implementation.
69              
70             =cut
71              
72             sub new {
73 46     46 1 203 my ($class, %args) = @_;
74 46         159 my $self = bless \%args, $class;
75 46   50     2101 $self->{packet} //= "\0" x 8;
76 46   100     249 $self->{data} //= '';
77 46         271 return $self;
78             }
79              
80             =head2 length
81              
82             Returns the length of the current packet in bytes.
83              
84             =cut
85              
86 8     8 1 46 sub length : method { shift->{length} }
87              
88             =head2 type
89              
90             Returns the numerical type of this frame, such as 1 for SYN_STREAM, 3 for RST_STREAM etc.
91              
92             =cut
93              
94 0     0 1 0 sub type { die 'abstract class, no type defined' }
95              
96             =head2 type_string
97              
98             Returns the type of this frame as a string.
99              
100             =cut
101              
102 10     10 1 2231 sub type_string { FRAME_TYPE_BY_ID->{shift->type} }
103              
104             =head2 as_packet
105              
106             Abstract method for returning the byte data comprising the SPDY packet that
107             would hold this frame.
108              
109             =cut
110              
111 0     0 1 0 sub as_packet { die 'abstract method' }
112              
113             =head2 parse
114              
115             Extract a frame from the given packet if possible. Takes a
116             scalar reference to byte data, and returns a L
117             subclass, or undef on failure.
118              
119             =cut
120              
121             sub parse {
122 26     26 1 2832 shift;
123 26         37 my $pkt = shift;
124             # 2.2 Frames always have a common header which is 8 bytes in length
125 26 50       88 return undef unless length $$pkt >= 8;
126              
127             # Data frames technically have a different header structure, but the
128             # length and control-bit values are the same.
129 26         110 my ($ver, $type, $len) = unpack "n1n1N1", $$pkt;
130              
131             # 2.2.2 Length: An unsigned 24-bit value representing the number of
132             # bytes after the length field... It is valid to have a zero-length data
133             # frame.
134 26         59 my $flags = ($len >> 24) & 0xFF;
135 26         33 $len &= 0x00FFFFFF;
136 26 50       79 return undef unless length $$pkt >= 8 + $len;
137              
138 26 100       64 my $control = $ver & 0x8000 ? 1 : 0;
139 26 100       74 return Protocol::SPDY::Frame::Data->from_data(
140             data => $$pkt
141             ) unless $control;
142              
143 24         33 $ver &= ~0x8000;
144              
145 24         63 my %args = @_;
146             # Now we know what type we have, delegate to a subclass which knows more than
147             # we do about constructing the object.
148 24 50       98 my $target_class = $control ? 'Protocol::SPDY::Frame::Control' : 'Protocol::SPDY::Frame::Data';
149 24 100       242 my $obj = $target_class->from_data(
    50          
150             zlib => $args{zlib},
151             type => $type,
152             version => $ver,
153             length => $len,
154             fin => $flags & FLAG_FIN ? 1 : 0,
155             uni => $flags & FLAG_UNI ? 1 : 0,
156             data => substr $$pkt, 8, $len
157             );
158 24         105 substr $$pkt, 0, 8 + $len, '';
159 24         138 $obj
160             }
161              
162             =head2 version
163              
164             Returns the version for this frame, probably 3.
165              
166             =cut
167              
168 0     0 1 0 sub version { shift->{version} }
169              
170             =head2 extract_frame
171              
172             Extracts a frame from the given data.
173              
174             =cut
175              
176             sub extract_frame {
177 0     0 1 0 my $class = shift;
178 0         0 $class->parse(@_)
179             }
180              
181             =head2 extract_headers
182              
183             Given a scalar containing bytes, constructs an arrayref of headers
184             and returns a 2-element list containing this arrayref and the length
185             of processed data.
186              
187             =cut
188              
189             sub extract_headers {
190 20     20 1 34 my $self = shift;
191 20         35 my $data = shift;
192 20         31 my $start_len = length $data;
193 20         66 my ($count) = unpack 'N1', substr $data, 0, 4, '';
194 20         28 my @headers;
195 20         50 for (1..$count) {
196 28         100 my ($k, $v) = unpack 'N/A* N/A*', $data;
197 28         79 my @v = split /\0/, $v;
198             # Don't allow non-ASCII characters
199 28         131 push @headers, [ Encode::encode(ascii => (my $key = $k), Encode::FB_CROAK) => @v ];
200 28         763 substr $data, 0, 8 + length($k) + length($v), '';
201             }
202 20         80 return \@headers, $start_len - length($data);
203             }
204              
205             =head2 to_string
206              
207             String representation of this frame, for debugging.
208              
209             =cut
210              
211             sub to_string {
212 2     2 1 4 my $self = shift;
213 2 50       9 'SPDY:' . $self->type_string . ($self->fin ? ' (FIN)' : '')
214             }
215              
216             1;
217              
218             __END__