File Coverage

blib/lib/Net/SPDY/Framer.pm
Criterion Covered Total %
statement 243 246 98.7
branch 80 108 74.0
condition 3 5 60.0
subroutine 31 31 100.0
pod 3 24 12.5
total 360 414 86.9


line stmt bran cond sub pod time code
1             package Net::SPDY::Framer;
2              
3             =head1 NAME
4              
5             Net::SPDY::Framer - SPDY protocol implementation
6              
7             =head1 ALPHA WARNING
8              
9             B This is an ALPHA stage software.
10             In particular this means that even though it probably won't kill your cat,
11             re-elect George W. Bush nor install Solaris 11 Express edition to your hard
12             drive, it is in active development, functionality is missing and no APIs are
13             stable.
14              
15             See F file in the distribution to learn about missing and planned
16             functionality. You are more than welcome to join the development and submit
17             patches with fixes or enhancements. Bug reports are probably not very useful
18             at this point.
19              
20             =head1 SYNOPSIS
21              
22             use Net::SPDY::Framer;
23              
24             my $framer = new Net::SPDY::Framer ({
25             compressor => new Net::SPDY::Compressor,
26             socket => $socket,
27             });
28              
29             $framer->write_frame(
30             type => Net::SPDY::Framer::PING,
31             data => 0x706c6c6d,
32             );
33             while (my %frame = $framer->read_frame) {
34             last if $frame{control} and $frame{type} eq Net::SPDY::Framer::PING;
35             }
36              
37             =head1 DESCRIPTION
38              
39             B provides SPDY protocol access on top of a network socket.
40             It serializes and deserializes packets as they are, without implementing any
41             other logic. For session management, see L.
42              
43             =cut
44              
45 17     17   16815 use strict;
  17         48  
  17         533  
46 17     17   83 use warnings;
  17         30  
  17         670  
47              
48             our $VERSION = '0.1';
49              
50 17     17   13686 use Errno qw/EINTR/;
  17         22414  
  17         2435  
51              
52             =head1 CONSTANTS
53              
54             For the actual values refer to the protocol specification.
55              
56             =over 4
57              
58             =item Frame types
59              
60             C, C, C, C, C, C,
61             C, C, C.
62              
63             =cut
64              
65             # Frame types
66             use constant {
67 17         2388 SYN_STREAM => 1,
68             SYN_REPLY => 2,
69             RST_STREAM => 3,
70             SETTINGS => 4,
71             PING => 6,
72             GOAWAY => 7,
73             HEADERS => 8,
74             WINDOW_UPDATE => 9,
75             CREDENTIAL => 10,
76 17     17   116 };
  17         21  
77              
78             =item Frame flags
79              
80             C, C, C.
81              
82             =cut
83              
84             use constant {
85             # For SYN_STREAM, SYN_RESPONSE, Data
86 17         1229 FLAG_FIN => 0x01,
87             FLAG_UNIDIRECTIONAL => 0x02,
88             # For SETTINGS
89             FLAG_SETTINGS_CLEAR_SETTINGS => 0x01,
90 17     17   96 };
  17         229  
91              
92             =item SETTINGS flags
93              
94             C, C.
95              
96             =cut
97              
98             use constant {
99 17         1219 FLAG_SETTINGS_PERSIST_VALUE => 0x1,
100             FLAG_SETTINGS_PERSISTED => 0x2,
101 17     17   91 };
  17         31  
102              
103             =item SETTINGS values
104              
105             C, C,
106             C, C,
107             C, C,
108             C, C.
109              
110             =cut
111              
112             use constant {
113 17         56208 SETTINGS_UPLOAD_BANDWIDTH => 1,
114             SETTINGS_DOWNLOAD_BANDWIDTH => 2,
115             SETTINGS_ROUND_TRIP_TIME => 3,
116             SETTINGS_MAX_CONCURRENT_STREAMS => 4,
117             SETTINGS_CURRENT_CWND => 5,
118             SETTINGS_DOWNLOAD_RETRANS_RATE => 6,
119             SETTINGS_INITIAL_WINDOW_SIZE => 7,
120             SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE => 8,
121 17     17   93 };
  17         28  
122              
123             =back
124              
125             =head1 PROPERTIES
126              
127             =over 4
128              
129             =item compressor
130              
131             L object representing the Zlib streams (one in each
132             direction) used by the framer.
133              
134             =item socket
135              
136             L instance that is used for actual network communication.
137              
138             =cut
139              
140             sub pack_nv
141             {
142 6     6 0 33 my $self = shift;
143              
144 6         216 my $name_value = pack 'N', (scalar @_ / 2);
145 6         84 while (my $name = shift) {
146 27         87 my $value = shift;
147 27 50       108 die 'No value' unless defined $value;
148 27 100 66     1970 $value = join "\x00", @$value if ref $value and ref $value eq 'ARRAY';
149 54         388 $name_value .= pack 'N a* N a*',
150 27         168 map { length $_ => $_ }
151             (lc ($name) => $value);
152             }
153 6         93 return $name_value;
154             }
155              
156             sub unpack_nv
157             {
158 26     26 0 51 my $self = shift;
159 26         50 my $buf = shift;
160 26         40 my @retval;
161              
162             my $entries;
163 26         263 my $name_value = $self->{compressor}->uncompress ($buf);
164              
165 26         94 ($entries, $name_value) = unpack 'N a*', $name_value;
166 26         199 foreach (1..$entries) {
167 136         166 my $len;
168             my $name;
169 0         0 my $value;
170              
171 136         334 ($len, $name_value) = unpack 'N a*', $name_value;
172 136         487 ($name, $name_value) = unpack "a$len a*", $name_value;
173              
174 136         343 ($len, $name_value) = unpack 'N a*', $name_value;
175 136         462 ($value, $name_value) = unpack "a$len a*", $name_value;
176              
177 136         372 my @values = split /\x00/, $value;
178 136 100       401 $value = [ @values ] if scalar @values > 1;
179              
180 136         343 push @retval, $name => $value;
181              
182             }
183              
184 26         198 return @retval;
185             }
186              
187             =back
188              
189             =cut
190              
191             sub reliable_read
192             {
193 194     194 0 285 my $handle = shift;
194 194         248 my $length = shift;
195              
196 194         366 my $buf = '';
197 194         550 while (length $buf < $length) {
198 194         1668 my $ret = $handle->read ($buf, $length - length $buf,
199             length $buf);
200 194 50       1224235 next if $!{EINTR};
201 194 50       2912 die 'Read error '.$! unless defined $ret;
202 194 50       1200 return '' if $ret == 0;
203             }
204              
205 194         1121 return $buf;
206             }
207              
208             =head1 FRAME FORMATS
209              
210             These are the data structures that are consumed by C and
211             produced by C methods. Their purpose is to coveniently represent
212             the fields of serialized SPDY frames. Please refer to the protocol
213             specification (L section) for descriptions of the actual fields.
214              
215             Not all fields are mandatory at all occassions. Serializer may assume sane
216             values for certain fields, that are marked as I below, or provided
217             with defaults.
218              
219             =over 4
220              
221             =item SYN_STREAM
222              
223             (
224             # Common to control frames
225             control => 1, # Input only
226             version => 3, # Input only
227             type => Net::SPDY::Framer::SYN_STREAM,
228             flags => , # Defaults to 0
229             length => , # Input only
230              
231             # Specific for SYN_STREAM
232             stream_id => ,
233             associated_stream_id => ,
234              
235             priority => ,
236             slot => ,
237              
238             headers => [
239             ':version' => , # E.g. 'HTTP/1.1'
240             ':scheme' => , # E.g. 'https'
241             ':host' => , # E.g. 'example.net:443',
242             ':method' => , # E.g. 'GET', 'HEAD',...
243             ':path' => , # E.g. '/something',
244             ... # HTTP headers, e.g. Accept => 'text/plain'
245             ],
246             )
247              
248             =cut
249              
250             sub write_syn_stream
251             {
252 3     3 0 9 my $self = shift;
253 3         28 my %frame = @_;
254              
255 3         30 $frame{data} = pack 'N N c c a*',
256             ($frame{stream_id} & 0x7fffffff),
257             ($frame{associated_stream_id} & 0x7fffffff),
258             ($frame{priority} & 0x07) << 5,
259             ($frame{slot} & 0xff),
260 3         23 $self->{compressor}->compress ($self->pack_nv (@{$frame{headers}}));
261              
262 3         47 return %frame;
263             }
264              
265             sub read_syn_stream
266             {
267 12     12 0 24 my $self = shift;
268 12         48 my %frame = @_;
269 12         24 my $buf;
270              
271 12 50       49 die 'Bad version '.$frame{version}
272             unless $frame{version} == 3;
273              
274 12         214 ($frame{stream_id}, $frame{associated_stream_id},
275             $frame{priority}, $frame{slot}, $frame{headers}) =
276             unpack 'N N c c a*', delete $frame{data};
277              
278 12         26 $frame{stream_id} &= 0x7fffffff;
279 12         23 $frame{associated_stream_id} &= 0x7fffffff;
280 12         25 $frame{priority} = ($frame{priority} & 0xe0) >> 5;
281 12         24 $frame{slot} &= 0xff;
282 12         46 $frame{headers} = [$self->unpack_nv ($frame{headers})];
283              
284 12         136 return %frame;
285             }
286              
287             =item SYN_REPLY
288              
289             (
290             # Common to control frames
291             control => 1, # Input only
292             version => 3, # Input only
293             type => Net::SPDY::Framer::SYN_REPLY,
294             flags => , # Defaults to 0
295             length => , # Input only
296              
297             # Specific for SYN_REPLY
298             stream_id => ,
299              
300             headers => [
301             ':version' => , # E.g. 'HTTP/1.1'
302             ':status' => , # E.g. '500 Front Fell Off',
303             ... # HTTP headers, e.g. 'Content-Type' => 'text/plain'
304             ],
305             )
306             =cut
307              
308             sub write_syn_reply
309             {
310 1     1 0 8 my $self = shift;
311 1         4 my %frame = @_;
312              
313 1         13 $frame{data} = pack 'N a*',
314             ($frame{stream_id} & 0x7fffffff),
315 1         10 $self->{compressor}->compress ($self->pack_nv (@{$frame{headers}}));
316              
317 1         37 return %frame;
318             }
319              
320             sub read_syn_reply
321             {
322 9     9 0 36 my $self = shift;
323 9         99 my %frame = @_;
324 9         72 my $buf;
325              
326 9 50       54 die 'Bad version '.$frame{version}
327             unless $frame{version} == 3;
328              
329 9         279 ($frame{stream_id}, $frame{headers}) =
330             unpack 'N a*', delete $frame{data};
331 9         153 $frame{headers} = [$self->unpack_nv ($frame{headers})];
332              
333 9         90 return %frame;
334             }
335              
336             =item RST_STREAM
337              
338             (
339             # Common to control frames
340             control => 1, # Input only
341             version => 3, # Input only
342             type => Net::SPDY::Framer::RST_STREAM
343             flags => , # Defaults to 0
344             length => , # Input only
345              
346             # Specific for RST_STREAM
347             stream_id => ,
348             status => ,
349             )
350              
351             =cut
352              
353             sub write_rst_stream
354             {
355 1     1 0 25 my $self = shift;
356 1         5 my %frame = @_;
357              
358 1         33 $frame{data} = pack 'N N',
359             ($frame{stream_id} & 0x7fffffff),
360             $frame{status};
361              
362 1         23 return %frame;
363             }
364              
365             sub read_rst_stream
366             {
367 8     8 0 48 my $self = shift;
368 8         72 my %frame = @_;
369              
370 8 50       64 die 'Bad version '.$frame{version}
371             unless $frame{version} == 3;
372 8 50       192 die 'Mis-sized rst_stream frame'
373             unless $frame{length} == 8;
374              
375 8         16 my $stream_id;
376 8         96 ($stream_id, $frame{status}) = unpack 'N N', delete $frame{data};
377 8         48 $frame{stream_id} = ($stream_id & 0x7fffffff);
378              
379 8         96 return %frame;
380             }
381              
382             =item SETTINGS
383              
384             (
385             # Common to control frames
386             control => 1, # Input only
387             version => 3, # Input only
388             type => Net::SPDY::Framer::SYN_SETTINGS
389             flags => , # Defaults to 0
390             length => , # Input only
391              
392             # Specific for SETTINGS
393             entries => , # Input only
394              
395             id_values => [
396             {
397             flags => ,
398             id => ,
399             value => ,
400             },
401             ...
402             ],
403             )
404              
405             =cut
406              
407             sub write_settings
408             {
409 5     5 0 26 my $self = shift;
410 5         42 my %frame = @_;
411              
412 5         23 $frame{data} = pack 'N', scalar @{$frame{id_values}};
  5         158  
413 5         37 foreach my $entry (@{$frame{id_values}}) {
  5         189  
414 8         50 $frame{data} .= pack 'N',
415             ($entry->{flags} & 0x000000ff) << 24 |
416             ($entry->{id} & 0x00ffffff);
417 8         40 $frame{data} .= pack 'N', $entry->{value};
418             }
419              
420 5         300 return %frame;
421             }
422              
423             sub read_settings
424             {
425 11     11 0 56 my $self = shift;
426 11         95 my %frame = @_;
427 11         22 my $buf;
428              
429 11 50       63 die 'Bad version '.$frame{version}
430             unless $frame{version} == 3;
431              
432 11         109 ($frame{entries}, $frame{data}) =
433             unpack 'N a*', $frame{data};
434 11         107 $frame{id_values} = [];
435              
436 11         88 foreach (1..$frame{entries}) {
437 14         25 my %entry;
438             my $head;
439 14         162 ($head, $entry{value}, $frame{data}) =
440             unpack 'N N a*', $frame{data};
441 14         48 $entry{id} = $head & 0x00ffffff;
442 14         27 $entry{flags} = ($head & 0xff000000) >> 24;
443 14         22 push @{$frame{id_values}}, \%entry;
  14         90  
444             }
445 11         37 delete $frame{data};
446              
447 11         113 return %frame;
448             }
449              
450             =item PING
451              
452             (
453             # Common to control frames
454             control => 1, # Input only
455             version => 3, # Input only
456             type => Net::SPDY::Framer::PING
457             flags => , # Defaults to 0
458             length => , # Input only
459              
460             # Specific for PING
461             id => , # E.g. 0x706c6c6d
462             )
463              
464              
465             =cut
466              
467             sub write_ping
468             {
469 2     2 0 11 my $self = shift;
470 2         16 my %frame = @_;
471              
472 2         36 $frame{data} = pack 'N', $frame{id};
473              
474 2         23 return %frame;
475             }
476              
477             sub read_ping
478             {
479 7     7 0 16 my $self = shift;
480 7         72 my %frame = @_;
481              
482 7 50       70 die 'Bad version '.$frame{version}
483             unless $frame{version} == 3;
484 7 50       66 die 'Mis-sized ping frame'
485             unless $frame{length} == 4;
486              
487 7         58 $frame{id} = unpack 'N', delete $frame{data};
488              
489 7         106 return %frame;
490             }
491              
492             =item GOAWAY
493              
494             (
495             # Common to control frames
496             control => 1, # Input only
497             version => 3, # Input only
498             type => Net::SPDY::Framer::GOAWAY
499             flags => , # Defaults to 0
500             length => , # Input only
501              
502             # Specific for GOAWAY
503             last_good_stream_id => ,
504             status => ,
505             )
506              
507             =cut
508              
509             sub write_goaway
510             {
511 2     2 0 5 my $self = shift;
512 2         19 my %frame = @_;
513              
514 2         39 $frame{data} = pack 'N N',
515             ($frame{last_good_stream_id} & 0x7fffffff),
516             $frame{status};
517              
518 2         30 return %frame;
519             }
520              
521             sub read_goaway
522             {
523 6     6 0 13 my $self = shift;
524 6         45 my %frame = @_;
525              
526 6 50       54 die 'Bad version '.$frame{version}
527             unless $frame{version} == 3;
528 6 50       72 die 'Mis-sized goaway frame'
529             unless $frame{length} == 8;
530              
531 6         47 my $last_good_stream_id;
532 6         75 ($last_good_stream_id, $frame{status}) = unpack 'N N', delete $frame{data};
533 6         40 $frame{last_good_stream_id} = ($last_good_stream_id & 0x7fffffff);
534              
535 6         110 return %frame;
536             }
537              
538             =item HEADERS
539              
540             (
541             # Common to control frames
542             control => 1, # Input only
543             version => 3, # Input only
544             type => Net::SPDY::Framer::HEADERS,
545             flags => , # Defaults to 0
546             length => , # Input only
547              
548             # Specific for HEADERS
549             stream_id => ,
550              
551             headers => [
552             ... # HTTP headers, e.g. Accept => 'text/plain'
553             ],
554             )
555              
556             =cut
557              
558             sub write_headers
559             {
560 2     2 0 10 my $self = shift;
561 2         19 my %frame = @_;
562              
563 2         50 $frame{data} = pack 'N a*',
564             ($frame{stream_id} & 0x7fffffff),
565 2         56 $self->{compressor}->compress ($self->pack_nv (@{$frame{headers}}));
566              
567 2         64 return %frame;
568             }
569              
570             sub read_headers
571             {
572 5     5 0 23 my $self = shift;
573 5         53 my %frame = @_;
574 5         26 my $buf;
575              
576 5 50       33 die 'Bad version '.$frame{version}
577             unless $frame{version} == 3;
578              
579 5         94 ($frame{stream_id}, $frame{headers}) =
580             unpack 'N a*', delete $frame{data};
581              
582 5         31 $frame{stream_id} &= 0x7fffffff;
583 5         49 $frame{headers} = [$self->unpack_nv ($frame{headers})];
584              
585 5         74 return %frame;
586             }
587              
588             =item WINDOW_UPDATE
589              
590             (
591             # Common to control frames
592             control => 1, # Input only
593             version => 3, # Input only
594             type => Net::SPDY::Framer::WINDOW_UPDATE
595             flags => , # Defaults to 0
596             length => , # Input only
597              
598             # Specific for WINDOW_UPDATE
599             stream_id => ,
600             delta_window_size => ,
601             )
602              
603             =cut
604              
605             sub write_window_update
606             {
607 1     1 0 3 my $self = shift;
608 1         21 my %frame = @_;
609              
610 1         50 $frame{data} = pack 'N N',
611             ($frame{stream_id} & 0x7fffffff),
612             ($frame{delta_window_size} & 0x7fffffff);
613              
614 1         11 return %frame;
615             }
616              
617             sub read_window_update
618             {
619 3     3 0 15 my $self = shift;
620 3         15 my %frame = @_;
621              
622 3 50       24 die 'Bad version '.$frame{version}
623             unless $frame{version} == 3;
624 3 50       30 die 'Mis-sized window_update frame'
625             unless $frame{length} == 8;
626              
627 3         15 my ($stream_id, $delta_window_size) = unpack 'N N', delete $frame{data};
628 3         15 $frame{stream_id} = ($stream_id & 0x7fffffff);
629 3         21 $frame{delta_window_size} = ($delta_window_size & 0x7fffffff);
630              
631 3         48 return %frame;
632             }
633              
634             =item CREDENTIAL
635              
636             (
637             # Common to control frames
638             control => 1, # Input only
639             version => 1, # Input only
640             type => Net::SPDY::Framer::CREDENTIAL
641             flags => , # Defaults to 0
642             length => , # Input only
643              
644             # Specific for CREDENTIAL
645             slot => ,
646             proof => ,
647             certificates => [ , ... ],
648             )
649              
650             =cut
651              
652             sub write_credential
653             {
654 1     1 0 11 my $self = shift;
655 1         17 my %frame = @_;
656              
657 1   50     22 $frame{version} ||= 1;
658 1         51 $frame{data} = pack 'n N a*', $frame{slot},
659             length $frame{proof}, $frame{proof};
660              
661 1         3 foreach my $credential (@{$frame{certificates}}) {
  1         18  
662 2         205 $frame{data} .= pack 'N a*', length $credential,
663             $credential;
664             }
665              
666 1         37 return %frame;
667             }
668              
669             sub read_credential
670             {
671 2     2 0 14 my $self = shift;
672 2         12 my %frame = @_;
673              
674 2 50       26 die 'Bad version '.$frame{version}
675             unless $frame{version} == 1;
676              
677 2         16 my $len;
678 2         26 ($frame{slot}, $len, $frame{data}) = unpack 'n N a*', $frame{data};
679 2         20 ($frame{proof}, $frame{data}) = unpack "a$len a*", $frame{data};
680 2         12 $frame{certificates} = [];
681              
682 2         18 while ($frame{data}) {
683 4         6 my $credential;
684 4         24 ($len, $frame{data}) = unpack 'N a*', $frame{data};
685 4         230 ($credential, $frame{data}) = unpack "a$len a*", $frame{data};
686 4         8 push @{$frame{certificates}}, $credential;
  4         26  
687             }
688              
689 2         42 return %frame;
690             }
691              
692             =back
693              
694             =head1 METHODS
695              
696             =over 4
697              
698             =item new { socket => SOCKET, compressor => COMPRESSOR }
699              
700             Creates a new framer instance. You need to create and pass both the socket for
701             the network communication and the compressor instance.
702              
703             =cut
704              
705             sub new
706             {
707 71     71 1 610 my $class = shift;
708 71         729 my $self = bless shift, $class;
709              
710 71         402 return $self;
711             }
712              
713             =item write_frame FRAME
714              
715             Serializes frame and writes it to the network socket.
716              
717             =cut
718              
719             sub write_frame
720             {
721 20     20 1 3158 my $self = shift;
722 20         302 my %frame = @_;
723              
724             # Serialize the payload
725 20 100       1148 if ($frame{type}) {
726 18 100       2361 if ($frame{type} == SYN_STREAM) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
727 3         412 %frame = $self->write_syn_stream (%frame);
728             } elsif ($frame{type} == SYN_REPLY) {
729 1         49 %frame = $self->write_syn_reply (%frame);
730             } elsif ($frame{type} == RST_STREAM) {
731 1         28 %frame = $self->write_rst_stream (%frame);
732             } elsif ($frame{type} == SETTINGS) {
733 5         267 %frame = $self->write_settings (%frame);
734             } elsif ($frame{type} == PING) {
735 2         47 %frame = $self->write_ping (%frame);
736             } elsif ($frame{type} == GOAWAY) {
737 2         41 %frame = $self->write_goaway (%frame);
738             } elsif ($frame{type} == HEADERS) {
739 2         118 %frame = $self->write_headers (%frame);
740             } elsif ($frame{type} == WINDOW_UPDATE) {
741 1         26 %frame = $self->write_window_update (%frame);
742             } elsif ($frame{type} == CREDENTIAL) {
743 1         106 %frame = $self->write_credential (%frame);
744             } else {
745 0         0 die 'Not implemented: Unsupported frame '.$frame{type};
746             }
747              
748 18 50       377 $frame{control} = 1 unless exists $frame{control};
749 18 100       127 $frame{version} = 3 unless exists $frame{version};
750 18 100       483 $frame{flags} = 0 unless exists $frame{flags};
751             }
752              
753 20         292 $frame{length} = length $frame{data};
754              
755 20 100       2079 $self->{socket}->print (pack 'N', ($frame{control} ? (
    50          
756             $frame{control} << 31 |
757             $frame{version} << 16 |
758             $frame{type}
759             ) : (
760             $frame{stream_id}
761             ))) or die 'Short write';
762              
763 20 50       696 $self->{socket}->print (pack 'N', (
764             $frame{flags} << 24 |
765             $frame{length}
766             )) or die 'Short write';
767              
768 20 100       398 if ($frame{data}) {
769 19 50       97 $self->{socket}->print ($frame{data})
770             or die "Short write $! $self->{socket}";
771             }
772              
773 20         236 return %frame;
774             }
775              
776             =item read_frame
777              
778             Reads frame from the network socket and returns it deserialized.
779              
780             =cut
781              
782             sub read_frame
783             {
784 65     65 1 30894810 my $self = shift;
785              
786             # First word of the frame header
787 65 50       804 return () unless $self->{socket};
788 65         893 my $buf = reliable_read ($self->{socket}, 4);
789 65 50       336 die 'Short read' unless defined $buf;
790 65 50       213 return () if $buf eq '';
791 65         366 my $head = unpack 'N', $buf;
792 65         409 my %frame = (control => ($head & 0x80000000) >> 31);
793              
794 65 100       253 if ($frame{control}) {
795 63         145 $frame{version} = ($head & 0x7fff0000) >> 16;
796 63         316 $frame{type} = ($head & 0x0000ffff);
797             } else {
798 2         16 $frame{stream_id} = ($head & 0x7fffffff);
799             };
800              
801             # Common parts of the header
802 65 50       200 $buf = reliable_read ($self->{socket}, 4) or die 'Read error';
803 65         192 my $body = unpack 'N', $buf;
804 65         328 $frame{flags} = ($body & 0xff000000) >> 24;
805 65         299 $frame{length} = ($body & 0x00ffffff);
806              
807             # Frame payload
808 65 100       176 unless ($frame{length}) {
809 1         3 $frame{data} = '';
810 1         13 return %frame;
811             }
812 64 50       210 $frame{data} = reliable_read ($self->{socket}, $frame{length})
813             or die 'Read error';
814              
815             # Grok the payload
816 64 100       197 if ($frame{control}) {
817 63 100       925 if ($frame{type} == SYN_STREAM) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
818 12         264 %frame = $self->read_syn_stream (%frame);
819             } elsif ($frame{type} == SYN_REPLY) {
820 9         153 %frame = $self->read_syn_reply (%frame);
821             } elsif ($frame{type} == RST_STREAM) {
822 8         144 %frame = $self->read_rst_stream (%frame);
823             } elsif ($frame{type} == SETTINGS) {
824 11         163 %frame = $self->read_settings (%frame);
825             } elsif ($frame{type} == PING) {
826 7         116 %frame = $self->read_ping (%frame);
827             } elsif ($frame{type} == GOAWAY) {
828 6         103 %frame = $self->read_goaway (%frame);
829             } elsif ($frame{type} == HEADERS) {
830 5         92 %frame = $self->read_headers (%frame);
831             } elsif ($frame{type} == WINDOW_UPDATE) {
832 3         72 %frame = $self->read_window_update (%frame);
833             } elsif ($frame{type} == CREDENTIAL) {
834 2         40 %frame = $self->read_credential (%frame);
835             } else {
836             # We SHOULD ignore these, if we did implement everything
837             # that we MUST implement.
838 0         0 die 'Not implemented: Unsupported control frame '.$frame{type};
839             }
840             }
841              
842 64         1205 return %frame;
843             }
844              
845             =back
846              
847             =head1 SEE ALSO
848              
849             =over
850              
851             =item *
852              
853             L -- SPDY project web site
854              
855             =item *
856              
857             L -- Protocol specification
858              
859             =item *
860              
861             L -- SPDY session implementation
862              
863             =item *
864              
865             L -- SPDY header compression
866              
867             =back
868              
869             =head1 CONTRIBUTING
870              
871             Source code for I is kept in a public GIT repository.
872             Visit L.
873              
874             Bugs reports and feature enhancement requests are tracked at
875             L.
876              
877             =head1 COPYRIGHT
878              
879             Copyright 2012, Lubomir Rintel
880              
881             This program is free software; you can redistribute it and/or modify it
882             under the same terms as Perl itself.
883              
884             =head1 AUTHOR
885              
886             Lubomir Rintel C
887              
888             =cut
889              
890             1;