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 = '0.999_007';
4             }
5 3     3   11 use strict;
  3         4  
  3         74  
6 3     3   10 use warnings;
  3         3  
  3         74  
7              
8             =head1 NAME
9              
10             Protocol::SPDY::Frame - support for SPDY frames
11              
12             =head1 VERSION
13              
14             version 0.999_007
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   1345 use Encode;
  3         20633  
  3         180  
27 3     3   16 use Protocol::SPDY::Constants ':all';
  3         5  
  3         483  
28              
29             use overload
30             '""' => 'to_string',
31 26     26   153 bool => sub { 1 },
32 3     3   15 fallback => 1;
  3         4  
  3         26  
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 10 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 26 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 191 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 159 my ($class, %args) = @_;
74 46         66 my $self = bless \%args, $class;
75 46   50     815 $self->{packet} //= "\0" x 8;
76 46   100     236 $self->{data} //= '';
77 46         235 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 44 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 1564 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 1966 shift;
123 26         34 my $pkt = shift;
124             # 2.2 Frames always have a common header which is 8 bytes in length
125 26 50       71 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         113 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         47 my $flags = ($len >> 24) & 0xFF;
135 26         29 $len &= 0x00FFFFFF;
136 26 50       62 return undef unless length $$pkt >= 8 + $len;
137              
138 26 100       55 my $control = $ver & 0x8000 ? 1 : 0;
139 26 100       55 return Protocol::SPDY::Frame::Data->from_data(
140             data => $$pkt
141             ) unless $control;
142              
143 24         31 $ver &= ~0x8000;
144              
145 24         51 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       48 my $target_class = $control ? 'Protocol::SPDY::Frame::Control' : 'Protocol::SPDY::Frame::Data';
149             my $obj = $target_class->from_data(
150             zlib => $args{zlib},
151 24 100       229 type => $type,
    50          
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         70 substr $$pkt, 0, 8 + $len, '';
159 24         101 $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 24 my $self = shift;
191 20         24 my $data = shift;
192 20         25 my $start_len = length $data;
193 20         66 my ($count) = unpack 'N1', substr $data, 0, 4, '';
194 20         21 my @headers;
195 20         45 for (1..$count) {
196 28         66 my ($k, $v) = unpack 'N/A* N/A*', $data;
197 28         55 my @v = split /\0/, $v;
198             # Don't allow non-ASCII characters
199 28         93 push @headers, [ Encode::encode(ascii => (my $key = $k), Encode::FB_CROAK) => @v ];
200 28         556 substr $data, 0, 8 + length($k) + length($v), '';
201             }
202 20         70 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       7 'SPDY:' . $self->type_string . ($self->fin ? ' (FIN)' : '')
214             }
215              
216             1;
217              
218             __END__