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