File Coverage

blib/lib/Protocol/HTTP2/Upgrade.pm
Criterion Covered Total %
statement 63 64 98.4
branch 21 26 80.7
condition 15 21 71.4
subroutine 9 10 90.0
pod 0 4 0.0
total 108 125 86.4


line stmt bran cond sub pod time code
1             package Protocol::HTTP2::Upgrade;
2 11     11   53 use strict;
  11         20  
  11         277  
3 11     11   54 use warnings;
  11         19  
  11         277  
4 11     11   5030 use Protocol::HTTP2;
  11         26  
  11         331  
5 11     11   56 use Protocol::HTTP2::Constants qw(:frame_types :errors :states);
  11         20  
  11         2892  
6 11     11   60 use Protocol::HTTP2::Trace qw(tracer);
  11         19  
  11         482  
7 11     11   8318 use MIME::Base64 qw(encode_base64url decode_base64url);
  11         8617  
  11         11045  
8              
9             #use re 'debug';
10             my $end_headers_re = qr/\G.+?\x0d?\x0a\x0d?\x0a/s;
11             my $header_re = qr/\G[ \t]*(.+?)[ \t]*\:[ \t]*(.+?)[ \t]*\x0d?\x0a/;
12              
13             sub upgrade_request {
14 1     1 0 41 my ( $con, %h ) = @_;
15             my $request = sprintf "%s %s HTTP/1.1\x0d\x0aHost: %s\x0d\x0a",
16             $h{':method'}, $h{':path'},
17 1         25 $h{':authority'};
18 1         6 while ( my ( $h, $v ) = splice( @{ $h{headers} }, 0, 2 ) ) {
  3         47  
19 2 50       14 next if grep { lc($h) eq $_ } (qw(connection upgrade http2-settings));
  6         55  
20 2         16 $request .= $h . ': ' . $v . "\x0d\x0a";
21             }
22 1         28 $request .= join "\x0d\x0a",
23             'Connection: Upgrade, HTTP2-Settings',
24             'Upgrade: ' . Protocol::HTTP2::ident_plain,
25             'HTTP2-Settings: '
26             . encode_base64url( $con->frame_encode( SETTINGS, 0, 0, {} ) ),
27             '', '';
28             }
29              
30             sub upgrade_response {
31              
32 0     0 0 0 join "\x0d\x0a",
33             "HTTP/1.1 101 Switching Protocols",
34             "Connection: Upgrade",
35             "Upgrade: " . Protocol::HTTP2::ident_plain,
36             "", "";
37              
38             }
39              
40             sub decode_upgrade_request {
41 1     1 0 12 my ( $con, $buf_ref, $buf_offset, $headers_ref ) = @_;
42              
43 1         4 pos($$buf_ref) = $buf_offset;
44              
45             # Search end of headers
46 1 50       22 return 0 if $$buf_ref !~ /$end_headers_re/g;
47 1         3 my $end_headers_pos = pos($$buf_ref) - $buf_offset;
48              
49 1         3 pos($$buf_ref) = $buf_offset;
50              
51             # Request
52 1 50       8 return undef if $$buf_ref !~ m#\G(\w+) ([^ ]+) HTTP/1\.1\x0d?\x0a#g;
53 1         4 my ( $method, $uri ) = ( $1, $2 );
54              
55             # TODO: remove after http2 -> http/1.1 headers conversion implemented
56 1         3 push @$headers_ref, ":method", $method;
57 1         2 push @$headers_ref, ":path", $uri;
58 1         3 push @$headers_ref, ":scheme", 'http';
59              
60 1         4 my $success = 0;
61              
62             # Parse headers
63 1   66     14 while ( $success != 0b111 && $$buf_ref =~ /$header_re/gc ) {
64 4         13 my ( $header, $value ) = ( lc($1), $2 );
65              
66 4 100 66     29 if ( $header eq "connection" ) {
    100 66        
    100          
67 1         7 my %h = map { $_ => 1 } split /\s*,\s*/, lc($value);
  2         7  
68             $success |= 0b001
69 1 50 33     16 if exists $h{'upgrade'} && exists $h{'http2-settings'};
70             }
71             elsif (
72 1         4 $header eq "upgrade" && grep { $_ eq Protocol::HTTP2::ident_plain }
73             split /\s*,\s*/,
74             $value
75             )
76             {
77 1         12 $success |= 0b010;
78             }
79             elsif ( $header eq "http2-settings"
80             && defined $con->frame_decode( \decode_base64url($value), 0 ) )
81             {
82 1         4 $success |= 0b100;
83             }
84             else {
85 1         15 push @$headers_ref, $header, $value;
86             }
87             }
88              
89 1 50       4 return undef unless $success == 0b111;
90              
91             # TODO: method POST also can contain data...
92              
93 1         7 return $end_headers_pos;
94              
95             }
96              
97             sub decode_upgrade_response {
98 5     5 0 33 my ( $con, $buf_ref, $buf_offset ) = @_;
99              
100 5         22 pos($$buf_ref) = $buf_offset;
101              
102             # Search end of headers
103 5 100       108 return 0 if $$buf_ref !~ /$end_headers_re/g;
104 4         17 my $end_headers_pos = pos($$buf_ref) - $buf_offset;
105              
106 4         15 pos($$buf_ref) = $buf_offset;
107              
108             # Switch Protocols failed
109 4 100       31 return undef if $$buf_ref !~ m#\GHTTP/1\.1 101 .+?\x0d?\x0a#g;
110              
111 2         5 my $success = 0;
112              
113             # Parse headers
114 2   100     25 while ( $success != 0b11 && $$buf_ref =~ /$header_re/gc ) {
115 6         17 my ( $header, $value ) = ( lc($1), $2 );
116              
117 6 100 66     53 if ( $header eq "connection" && lc($value) eq "upgrade" ) {
    100 100        
118 2         23 $success |= 0b01;
119             }
120             elsif ( $header eq "upgrade" && $value eq Protocol::HTTP2::ident_plain )
121             {
122 1         4 $success |= 0b10;
123             }
124             }
125              
126 2 100       10 return undef unless $success == 0b11;
127              
128 1         5 return $end_headers_pos;
129             }
130              
131             1;