File Coverage

blib/lib/Protocol/HTTP2/Frame.pm
Criterion Covered Total %
statement 69 78 88.4
branch 10 22 45.4
condition 6 12 50.0
subroutine 19 19 100.0
pod 0 5 0.0
total 104 136 76.4


line stmt bran cond sub pod time code
1             package Protocol::HTTP2::Frame;
2 11     11   52 use strict;
  11         19  
  11         277  
3 11     11   49 use warnings;
  11         21  
  11         311  
4 11     11   57 use Protocol::HTTP2::Trace qw(tracer);
  11         18  
  11         587  
5             use Protocol::HTTP2::Constants
6 11     11   56 qw(const_name :frame_types :errors :preface :states :flags :limits :settings);
  11         18  
  11         4721  
7 11     11   6208 use Protocol::HTTP2::Frame::Data;
  11         28  
  11         326  
8 11     11   6168 use Protocol::HTTP2::Frame::Headers;
  11         30  
  11         329  
9 11     11   6083 use Protocol::HTTP2::Frame::Priority;
  11         25  
  11         323  
10 11     11   6084 use Protocol::HTTP2::Frame::Rst_stream;
  11         153  
  11         326  
11 11     11   6201 use Protocol::HTTP2::Frame::Settings;
  11         30  
  11         310  
12 11     11   6196 use Protocol::HTTP2::Frame::Push_promise;
  11         26  
  11         342  
13 11     11   6004 use Protocol::HTTP2::Frame::Ping;
  11         28  
  11         303  
14 11     11   6073 use Protocol::HTTP2::Frame::Goaway;
  11         30  
  11         319  
15 11     11   6148 use Protocol::HTTP2::Frame::Window_update;
  11         30  
  11         315  
16 11     11   5944 use Protocol::HTTP2::Frame::Continuation;
  11         27  
  11         8727  
17              
18             # Table of payload decoders
19             my %frame_class = (
20             &DATA => 'Data',
21             &HEADERS => 'Headers',
22             &PRIORITY => 'Priority',
23             &RST_STREAM => 'Rst_stream',
24             &SETTINGS => 'Settings',
25             &PUSH_PROMISE => 'Push_promise',
26             &PING => 'Ping',
27             &GOAWAY => 'Goaway',
28             &WINDOW_UPDATE => 'Window_update',
29             &CONTINUATION => 'Continuation',
30             );
31              
32             my %decoder =
33             map { $_ => \&{ 'Protocol::HTTP2::Frame::' . $frame_class{$_} . '::decode' } }
34             keys %frame_class;
35              
36             my %encoder =
37             map { $_ => \&{ 'Protocol::HTTP2::Frame::' . $frame_class{$_} . '::encode' } }
38             keys %frame_class;
39              
40             sub frame_encode {
41 143     143 0 25030 my ( $con, $type, $flags, $stream_id, $data_ref ) = @_;
42              
43 143         25148 my $payload = $encoder{$type}->( $con, \$flags, $stream_id, $data_ref );
44 143         24524 my $l = length $payload;
45              
46 143         49507 pack( 'CnC2N', ( $l >> 16 ), ( $l & 0xFFFF ), $type, $flags, $stream_id )
47             . $payload;
48             }
49              
50             sub preface_decode {
51 12     12 0 2687 my ( $con, $buf_ref, $buf_offset ) = @_;
52 12 50       2728 return 0 if length($$buf_ref) - $buf_offset < length(PREFACE);
53             return
54 12 50       5389 index( $$buf_ref, PREFACE, $buf_offset ) == -1 ? undef : length(PREFACE);
55             }
56              
57             sub preface_encode {
58 15     15 0 5425 PREFACE;
59             }
60              
61             sub frame_header_decode {
62 194     194 0 38281 my ( undef, $buf_ref, $buf_offset ) = @_;
63              
64 194         38858 my ( $hl, $ll, $type, $flags, $stream_id ) =
65             unpack( 'CnC2N', substr( $$buf_ref, $buf_offset, FRAME_HEADER_SIZE ) );
66              
67 194         38323 my $length = ( $hl << 16 ) + $ll;
68 194         38154 $stream_id &= 0x7FFF_FFFF;
69 194         76318 return $length, $type, $flags, $stream_id;
70             }
71              
72             sub frame_decode {
73 265     265 0 52313 my ( $con, $buf_ref, $buf_offset ) = @_;
74 265 100       92733 return 0 if length($$buf_ref) - $buf_offset < FRAME_HEADER_SIZE;
75              
76 129         24684 my ( $length, $type, $flags, $stream_id ) =
77             $con->frame_header_decode( $buf_ref, $buf_offset );
78              
79 129 50       24832 if ( $length > $con->dec_setting(SETTINGS_MAX_FRAME_SIZE) ) {
80 0         0 tracer->error("Frame is too large: $length\n");
81 0         0 $con->error(PROTOCOL_ERROR);
82 0         0 return undef;
83             }
84              
85 129 50       24933 return 0
86             if length($$buf_ref) - $buf_offset - FRAME_HEADER_SIZE - $length < 0;
87              
88             tracer->debug(
89             sprintf "TYPE = %s(%i), FLAGS = %08b, STREAM_ID = %i, "
90             . "LENGTH = %i\n",
91 129 50       25199 exists $frame_class{$type}
92             ? const_name( "frame_types", $type )
93             : "UNKNOWN",
94             $type,
95             $flags,
96             $stream_id,
97             $length
98             );
99              
100 129         24958 my $pending_stream_id = $con->pending_stream;
101 129 0 0     24699 if ( $pending_stream_id
      33        
102             && ( $type != CONTINUATION || $pending_stream_id != $stream_id ) )
103             {
104 0         0 tracer->debug("Expected CONTINUATION for stream $pending_stream_id");
105 0         0 $con->error(PROTOCOL_ERROR);
106 0         0 return undef;
107             }
108              
109             # Unknown type of frame
110 129 50       24721 if ( !exists $frame_class{$type} ) {
111 0         0 tracer->info("ignore unknown frame type $type");
112 0         0 return FRAME_HEADER_SIZE + $length;
113             }
114              
115             $con->decode_context->{frame} = {
116 129         25356 type => $type,
117             flags => $flags,
118             length => $length,
119             stream => $stream_id,
120             };
121              
122             # Try to create new stream structure
123 129 50 100     25187 if ( $stream_id
      66        
124             && !$con->stream($stream_id)
125             && !$con->new_peer_stream($stream_id) )
126             {
127 0 0       0 return $con->error ? undef : FRAME_HEADER_SIZE + $length;
128             }
129              
130             return undef
131 129 50       25005 unless defined $decoder{$type}
132             ->( $con, $buf_ref, $buf_offset + FRAME_HEADER_SIZE, $length );
133              
134             # Arrived frame may change state of stream
135 129         24858 $con->state_machine( 'recv', $type, $flags, $stream_id );
136              
137 129         49131 return FRAME_HEADER_SIZE + $length;
138             }
139              
140             =pod
141              
142             =head1 NOTES
143              
144             =head2 Frame Types vs Flags and Stream ID
145              
146             Table represent possible combination of frame types and flags.
147             Last column -- Stream ID of frame types (x -- sid >= 1, 0 -- sid = 0)
148              
149              
150             +-END_STREAM 0x1
151             | +-ACK 0x1
152             | | +-END_HEADERS 0x4
153             | | | +-PADDED 0x8
154             | | | | +-PRIORITY 0x20
155             | | | | | +-stream id (value)
156             | | | | | |
157             | frame type\flag | V | V | V | V | V | | V |
158             | --------------- |:-:|:-:|:-:|:-:|:-:| - |:---:|
159             | DATA | x | | | x | | | x |
160             | HEADERS | x | | x | x | x | | x |
161             | PRIORITY | | | | | | | x |
162             | RST_STREAM | | | | | | | x |
163             | SETTINGS | | x | | | | | 0 |
164             | PUSH_PROMISE | | | x | x | | | x |
165             | PING | | x | | | | | 0 |
166             | GOAWAY | | | | | | | 0 |
167             | WINDOW_UPDATE | | | | | | | 0/x |
168             | CONTINUATION | | | x | x | | | x |
169              
170             =cut
171              
172             1;