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