Mercurial > hg > nginx-tests
comparison lib/Test/Nginx/HTTP2.pm @ 949:c657aaffdaa8
Tests: style.
author | Sergey Kandaurov <pluknet@nginx.com> |
---|---|
date | Fri, 17 Jun 2016 11:41:30 +0300 |
parents | 4dc302d8e04f |
children | 9361c7eddfc1 |
comparison
equal
deleted
inserted
replaced
948:4dc302d8e04f | 949:c657aaffdaa8 |
---|---|
29 7 => { name => 'GOAWAY', value => \&goaway }, | 29 7 => { name => 'GOAWAY', value => \&goaway }, |
30 8 => { name => 'WINDOW_UPDATE', value => \&window_update }, | 30 8 => { name => 'WINDOW_UPDATE', value => \&window_update }, |
31 9 => { name => 'CONTINUATION', value => \&headers }, | 31 9 => { name => 'CONTINUATION', value => \&headers }, |
32 ); | 32 ); |
33 | 33 |
34 sub new { | |
35 my $class = shift; | |
36 my ($port, %extra) = @_; | |
37 | |
38 my $s = $extra{socket} || new_socket($port, %extra); | |
39 my $preface = $extra{preface} | |
40 || 'PRI * HTTP/2.0' . CRLF . CRLF . 'SM' . CRLF . CRLF; | |
41 | |
42 if ($extra{proxy}) { | |
43 raw_write($s, $extra{proxy}); | |
44 } | |
45 | |
46 # preface | |
47 | |
48 raw_write($s, $preface); | |
49 | |
50 my $self = bless { | |
51 socket => $s, last_stream => -1, | |
52 dynamic_encode => [ static_table() ], | |
53 dynamic_decode => [ static_table() ], | |
54 static_table_size => scalar @{[static_table()]}, | |
55 iws => 65535, conn_window => 65535, streams => {} | |
56 }, $class; | |
57 | |
58 return $self if $extra{pure}; | |
59 | |
60 # update windows, if any | |
61 | |
62 my $frames = $self->read(all => [ | |
63 { type => 'WINDOW_UPDATE' }, | |
64 { type => 'SETTINGS'} | |
65 ]); | |
66 | |
67 # 6.5.3. Settings Synchronization | |
68 | |
69 if (grep { $_->{type} eq "SETTINGS" && $_->{flags} == 0 } @$frames) { | |
70 $self->h2_settings(1); | |
71 } | |
72 | |
73 return $self; | |
74 } | |
75 | |
34 sub h2_ping { | 76 sub h2_ping { |
35 my ($sess, $payload) = @_; | 77 my ($self, $payload) = @_; |
36 | 78 |
37 raw_write($sess->{socket}, pack("x2C2x5a8", 8, 0x6, $payload)); | 79 raw_write($self->{socket}, pack("x2C2x5a8", 8, 0x6, $payload)); |
38 } | 80 } |
39 | 81 |
40 sub h2_rst { | 82 sub h2_rst { |
41 my ($sess, $stream, $error) = @_; | 83 my ($self, $stream, $error) = @_; |
42 | 84 |
43 raw_write($sess->{socket}, pack("x2C2xNN", 4, 0x3, $stream, $error)); | 85 raw_write($self->{socket}, pack("x2C2xNN", 4, 0x3, $stream, $error)); |
44 } | 86 } |
45 | 87 |
46 sub h2_goaway { | 88 sub h2_goaway { |
47 my ($sess, $stream, $lstream, $err, $debug, %extra) = @_; | 89 my ($self, $stream, $lstream, $err, $debug, %extra) = @_; |
48 $debug = '' unless defined $debug; | 90 $debug = '' unless defined $debug; |
49 my $len = defined $extra{len} ? $extra{len} : 8 + length($debug); | 91 my $len = defined $extra{len} ? $extra{len} : 8 + length($debug); |
50 my $buf = pack("x2C2xN3A*", $len, 0x7, $stream, $lstream, $err, $debug); | 92 my $buf = pack("x2C2xN3A*", $len, 0x7, $stream, $lstream, $err, $debug); |
51 | 93 |
52 my @bufs = map { | 94 my @bufs = map { |
53 raw_write($sess->{socket}, substr $buf, 0, $_, ""); | 95 raw_write($self->{socket}, substr $buf, 0, $_, ""); |
54 select undef, undef, undef, 0.2; | 96 select undef, undef, undef, 0.2; |
55 } @{$extra{split}}; | 97 } @{$extra{split}}; |
56 | 98 |
57 raw_write($sess->{socket}, $buf); | 99 raw_write($self->{socket}, $buf); |
58 } | 100 } |
59 | 101 |
60 sub h2_priority { | 102 sub h2_priority { |
61 my ($sess, $w, $stream, $dep, %extra) = @_; | 103 my ($self, $w, $stream, $dep, %extra) = @_; |
62 | 104 |
63 $stream = 0 unless defined $stream; | 105 $stream = 0 unless defined $stream; |
64 $dep = 0 unless defined $dep; | 106 $dep = 0 unless defined $dep; |
65 $dep |= $extra{excl} << 31 if exists $extra{excl}; | 107 $dep |= $extra{excl} << 31 if exists $extra{excl}; |
66 raw_write($sess->{socket}, pack("x2C2xNNC", 5, 0x2, $stream, $dep, $w)); | 108 raw_write($self->{socket}, pack("x2C2xNNC", 5, 0x2, $stream, $dep, $w)); |
67 } | 109 } |
68 | 110 |
69 sub h2_window { | 111 sub h2_window { |
70 my ($sess, $win, $stream) = @_; | 112 my ($self, $win, $stream) = @_; |
71 | 113 |
72 $stream = 0 unless defined $stream; | 114 $stream = 0 unless defined $stream; |
73 raw_write($sess->{socket}, pack("x2C2xNN", 4, 0x8, $stream, $win)); | 115 raw_write($self->{socket}, pack("x2C2xNN", 4, 0x8, $stream, $win)); |
74 } | 116 } |
75 | 117 |
76 sub h2_settings { | 118 sub h2_settings { |
77 my ($sess, $ack, %extra) = @_; | 119 my ($self, $ack, %extra) = @_; |
78 | 120 |
79 my $len = 6 * keys %extra; | 121 my $len = 6 * keys %extra; |
80 my $buf = pack_length($len) . pack "CCx4", 0x4, $ack ? 0x1 : 0x0; | 122 my $buf = pack_length($len) . pack "CCx4", 0x4, $ack ? 0x1 : 0x0; |
81 $buf .= join '', map { pack "nN", $_, $extra{$_} } keys %extra; | 123 $buf .= join '', map { pack "nN", $_, $extra{$_} } keys %extra; |
82 raw_write($sess->{socket}, $buf); | 124 raw_write($self->{socket}, $buf); |
83 } | 125 } |
84 | 126 |
85 sub h2_unknown { | 127 sub h2_unknown { |
86 my ($sess, $payload) = @_; | 128 my ($self, $payload) = @_; |
87 | 129 |
88 my $buf = pack_length(length($payload)) . pack("Cx5a*", 0xa, $payload); | 130 my $buf = pack_length(length($payload)) . pack("Cx5a*", 0xa, $payload); |
89 raw_write($sess->{socket}, $buf); | 131 raw_write($self->{socket}, $buf); |
90 } | 132 } |
91 | 133 |
92 sub h2_continue { | 134 sub h2_continue { |
93 my ($ctx, $stream, $uri) = @_; | 135 my ($ctx, $stream, $uri) = @_; |
94 | 136 |
95 $uri->{h2_continue} = 1; | 137 $uri->{h2_continue} = 1; |
96 return new_stream($ctx, $uri, $stream); | 138 return new_stream($ctx, $uri, $stream); |
97 } | 139 } |
98 | 140 |
99 sub h2_body { | 141 sub h2_body { |
100 my ($sess, $body, $extra) = @_; | 142 my ($self, $body, $extra) = @_; |
101 $extra = {} unless defined $extra; | 143 $extra = {} unless defined $extra; |
102 | 144 |
103 my $len = length $body; | 145 my $len = length $body; |
104 my $sid = $sess->{last_stream}; | 146 my $sid = $self->{last_stream}; |
105 | 147 |
106 if ($len > $sess->{conn_window} || $len > $sess->{streams}{$sid}) { | 148 if ($len > $self->{conn_window} || $len > $self->{streams}{$sid}) { |
107 $sess->read(all => [{ type => 'WINDOW_UPDATE' }]); | 149 $self->read(all => [{ type => 'WINDOW_UPDATE' }]); |
108 } | 150 } |
109 | 151 |
110 if ($len > $sess->{conn_window} || $len > $sess->{streams}{$sid}) { | 152 if ($len > $self->{conn_window} || $len > $self->{streams}{$sid}) { |
111 return; | 153 return; |
112 } | 154 } |
113 | 155 |
114 $sess->{conn_window} -= $len; | 156 $self->{conn_window} -= $len; |
115 $sess->{streams}{$sid} -= $len; | 157 $self->{streams}{$sid} -= $len; |
116 | 158 |
117 my $buf; | 159 my $buf; |
118 | 160 |
119 my $split = ref $extra->{body_split} && $extra->{body_split} || []; | 161 my $split = ref $extra->{body_split} && $extra->{body_split} || []; |
120 for (@$split) { | 162 for (@$split) { |
121 $buf .= pack_body($sess, substr($body, 0, $_, ""), 0x0, $extra); | 163 $buf .= pack_body($self, substr($body, 0, $_, ""), 0x0, $extra); |
122 } | 164 } |
123 | 165 |
124 $buf .= pack_body($sess, $body, 0x1, $extra) if defined $body; | 166 $buf .= pack_body($self, $body, 0x1, $extra) if defined $body; |
125 | 167 |
126 $split = ref $extra->{split} && $extra->{split} || []; | 168 $split = ref $extra->{split} && $extra->{split} || []; |
127 for (@$split) { | 169 for (@$split) { |
128 raw_write($sess->{socket}, substr($buf, 0, $_, "")); | 170 raw_write($self->{socket}, substr($buf, 0, $_, "")); |
129 return if $extra->{abort}; | 171 return if $extra->{abort}; |
130 select undef, undef, undef, ($extra->{split_delay} || 0.2); | 172 select undef, undef, undef, ($extra->{split_delay} || 0.2); |
131 } | 173 } |
132 | 174 |
133 raw_write($sess->{socket}, $buf); | 175 raw_write($self->{socket}, $buf); |
134 } | 176 } |
177 | |
178 sub new_stream { | |
179 my ($self, $uri, $stream) = @_; | |
180 my ($input, $buf); | |
181 my ($d, $status); | |
182 | |
183 $self->{headers} = ''; | |
184 | |
185 my $host = $uri->{host} || 'localhost'; | |
186 my $method = $uri->{method} || 'GET'; | |
187 my $scheme = $uri->{scheme} || 'http'; | |
188 my $path = $uri->{path} || '/'; | |
189 my $headers = $uri->{headers}; | |
190 my $body = $uri->{body}; | |
191 my $prio = $uri->{prio}; | |
192 my $dep = $uri->{dep}; | |
193 | |
194 my $pad = defined $uri->{padding} ? $uri->{padding} : 0; | |
195 my $padlen = defined $uri->{padding} ? 1 : 0; | |
196 | |
197 my $type = defined $uri->{h2_continue} ? 0x9 : 0x1; | |
198 my $flags = defined $uri->{continuation} ? 0x0 : 0x4; | |
199 $flags |= 0x1 unless defined $body || defined $uri->{body_more}; | |
200 $flags |= 0x8 if $padlen; | |
201 $flags |= 0x20 if defined $dep || defined $prio; | |
202 | |
203 if ($stream) { | |
204 $self->{last_stream} = $stream; | |
205 } else { | |
206 $self->{last_stream} += 2; | |
207 $self->{streams}{$self->{last_stream}} = $self->{iws}; | |
208 } | |
209 | |
210 $buf = pack("xxx"); # Length stub | |
211 $buf .= pack("CC", $type, $flags); # END_HEADERS | |
212 $buf .= pack("N", $self->{last_stream}); # Stream-ID | |
213 | |
214 $dep = 0 if defined $prio and not defined $dep; | |
215 $prio = 16 if defined $dep and not defined $prio; | |
216 | |
217 unless ($headers) { | |
218 $input = hpack($self, ":method", $method); | |
219 $input .= hpack($self, ":scheme", $scheme); | |
220 $input .= hpack($self, ":path", $path); | |
221 $input .= hpack($self, ":authority", $host); | |
222 $input .= hpack($self, "content-length", length($body)) | |
223 if $body; | |
224 | |
225 } else { | |
226 $input = join '', map { | |
227 hpack($self, $_->{name}, $_->{value}, | |
228 mode => $_->{mode}, huff => $_->{huff}) | |
229 } @$headers if $headers; | |
230 } | |
231 | |
232 $input = pack("B*", '001' . ipack(5, $uri->{table_size})) . $input | |
233 if defined $uri->{table_size}; | |
234 | |
235 my $split = ref $uri->{continuation} && $uri->{continuation} || []; | |
236 my @input = map { substr $input, 0, $_, "" } @$split; | |
237 push @input, $input; | |
238 | |
239 # set length, attach headers, padding, priority | |
240 | |
241 my $hlen = length($input[0]) + $pad + $padlen; | |
242 $hlen += 5 if $flags & 0x20; | |
243 $buf |= pack_length($hlen); | |
244 | |
245 $buf .= pack 'C', $pad if $padlen; # Pad Length? | |
246 $buf .= pack 'NC', $dep, $prio if $flags & 0x20; | |
247 $buf .= $input[0]; | |
248 $buf .= (pack 'C', 0) x $pad if $padlen; # Padding | |
249 | |
250 shift @input; | |
251 | |
252 while (@input) { | |
253 $input = shift @input; | |
254 $flags = @input ? 0x0 : 0x4; | |
255 $buf .= pack_length(length($input)); | |
256 $buf .= pack("CC", 0x9, $flags); | |
257 $buf .= pack("N", $self->{last_stream}); | |
258 $buf .= $input; | |
259 } | |
260 | |
261 $split = ref $uri->{body_split} && $uri->{body_split} || []; | |
262 for (@$split) { | |
263 $buf .= pack_body($self, substr($body, 0, $_, ""), 0x0, $uri); | |
264 } | |
265 | |
266 $buf .= pack_body($self, $body, 0x1, $uri) if defined $body; | |
267 | |
268 $split = ref $uri->{split} && $uri->{split} || []; | |
269 for (@$split) { | |
270 raw_write($self->{socket}, substr($buf, 0, $_, "")); | |
271 goto done if $uri->{abort}; | |
272 select undef, undef, undef, ($uri->{split_delay} || 0.2); | |
273 } | |
274 | |
275 raw_write($self->{socket}, $buf); | |
276 done: | |
277 return $self->{last_stream}; | |
278 } | |
279 | |
280 sub read { | |
281 my ($self, %extra) = @_; | |
282 my (@got); | |
283 my $s = $self->{socket}; | |
284 my $buf = ''; | |
285 my $wait = $extra{wait}; | |
286 | |
287 local $Data::Dumper::Terse = 1; | |
288 | |
289 while (1) { | |
290 $buf = raw_read($s, $buf, 9, $wait); | |
291 last if length $buf < 9; | |
292 | |
293 my $length = unpack_length($buf); | |
294 my $type = unpack('x3C', $buf); | |
295 my $flags = unpack('x4C', $buf); | |
296 | |
297 my $stream = unpack "x5 B32", $buf; | |
298 substr($stream, 0, 1) = 0; | |
299 $stream = unpack("N", pack("B32", $stream)); | |
300 | |
301 $buf = raw_read($s, $buf, $length + 9, $wait); | |
302 last if length($buf) < $length + 9; | |
303 | |
304 $buf = substr($buf, 9); | |
305 | |
306 my $frame = $cframe{$type}{value}($self, $buf, $length, $flags, | |
307 $stream); | |
308 $frame->{length} = $length; | |
309 $frame->{type} = $cframe{$type}{name}; | |
310 $frame->{flags} = $flags; | |
311 $frame->{sid} = $stream; | |
312 push @got, $frame; | |
313 | |
314 Test::Nginx::log_core('||', $_) for split "\n", Dumper $frame; | |
315 | |
316 $buf = substr($buf, $length); | |
317 | |
318 last unless $extra{all} && test_fin($got[-1], $extra{all}); | |
319 }; | |
320 return \@got; | |
321 } | |
322 | |
323 ############################################################################### | |
135 | 324 |
136 sub pack_body { | 325 sub pack_body { |
137 my ($ctx, $body, $flags, $extra) = @_; | 326 my ($ctx, $body, $flags, $extra) = @_; |
138 | 327 |
139 my $pad = defined $extra->{body_padding} ? $extra->{body_padding} : 0; | 328 my $pad = defined $extra->{body_padding} ? $extra->{body_padding} : 0; |
148 $buf .= $body; | 337 $buf .= $body; |
149 $buf .= pack "x$pad" if $padlen; # DATA Padding | 338 $buf .= pack "x$pad" if $padlen; # DATA Padding |
150 return $buf; | 339 return $buf; |
151 } | 340 } |
152 | 341 |
153 sub new_stream { | |
154 my ($ctx, $uri, $stream) = @_; | |
155 my ($input, $buf); | |
156 my ($d, $status); | |
157 | |
158 $ctx->{headers} = ''; | |
159 | |
160 my $host = $uri->{host} || 'localhost'; | |
161 my $method = $uri->{method} || 'GET'; | |
162 my $scheme = $uri->{scheme} || 'http'; | |
163 my $path = $uri->{path} || '/'; | |
164 my $headers = $uri->{headers}; | |
165 my $body = $uri->{body}; | |
166 my $prio = $uri->{prio}; | |
167 my $dep = $uri->{dep}; | |
168 | |
169 my $pad = defined $uri->{padding} ? $uri->{padding} : 0; | |
170 my $padlen = defined $uri->{padding} ? 1 : 0; | |
171 | |
172 my $type = defined $uri->{h2_continue} ? 0x9 : 0x1; | |
173 my $flags = defined $uri->{continuation} ? 0x0 : 0x4; | |
174 $flags |= 0x1 unless defined $body || defined $uri->{body_more}; | |
175 $flags |= 0x8 if $padlen; | |
176 $flags |= 0x20 if defined $dep || defined $prio; | |
177 | |
178 if ($stream) { | |
179 $ctx->{last_stream} = $stream; | |
180 } else { | |
181 $ctx->{last_stream} += 2; | |
182 $ctx->{streams}{$ctx->{last_stream}} = $ctx->{iws}; | |
183 } | |
184 | |
185 $buf = pack("xxx"); # Length stub | |
186 $buf .= pack("CC", $type, $flags); # END_HEADERS | |
187 $buf .= pack("N", $ctx->{last_stream}); # Stream-ID | |
188 | |
189 $dep = 0 if defined $prio and not defined $dep; | |
190 $prio = 16 if defined $dep and not defined $prio; | |
191 | |
192 unless ($headers) { | |
193 $input = hpack($ctx, ":method", $method); | |
194 $input .= hpack($ctx, ":scheme", $scheme); | |
195 $input .= hpack($ctx, ":path", $path); | |
196 $input .= hpack($ctx, ":authority", $host); | |
197 $input .= hpack($ctx, "content-length", length($body)) if $body; | |
198 | |
199 } else { | |
200 $input = join '', map { | |
201 hpack($ctx, $_->{name}, $_->{value}, | |
202 mode => $_->{mode}, huff => $_->{huff}) | |
203 } @$headers if $headers; | |
204 } | |
205 | |
206 $input = pack("B*", '001' . ipack(5, $uri->{table_size})) . $input | |
207 if defined $uri->{table_size}; | |
208 | |
209 my $split = ref $uri->{continuation} && $uri->{continuation} || []; | |
210 my @input = map { substr $input, 0, $_, "" } @$split; | |
211 push @input, $input; | |
212 | |
213 # set length, attach headers, padding, priority | |
214 | |
215 my $hlen = length($input[0]) + $pad + $padlen; | |
216 $hlen += 5 if $flags & 0x20; | |
217 $buf |= pack_length($hlen); | |
218 | |
219 $buf .= pack 'C', $pad if $padlen; # Pad Length? | |
220 $buf .= pack 'NC', $dep, $prio if $flags & 0x20; | |
221 $buf .= $input[0]; | |
222 $buf .= (pack 'C', 0) x $pad if $padlen; # Padding | |
223 | |
224 shift @input; | |
225 | |
226 while (@input) { | |
227 $input = shift @input; | |
228 $flags = @input ? 0x0 : 0x4; | |
229 $buf .= pack_length(length($input)); | |
230 $buf .= pack("CC", 0x9, $flags); | |
231 $buf .= pack("N", $ctx->{last_stream}); | |
232 $buf .= $input; | |
233 } | |
234 | |
235 $split = ref $uri->{body_split} && $uri->{body_split} || []; | |
236 for (@$split) { | |
237 $buf .= pack_body($ctx, substr($body, 0, $_, ""), 0x0, $uri); | |
238 } | |
239 | |
240 $buf .= pack_body($ctx, $body, 0x1, $uri) if defined $body; | |
241 | |
242 $split = ref $uri->{split} && $uri->{split} || []; | |
243 for (@$split) { | |
244 raw_write($ctx->{socket}, substr($buf, 0, $_, "")); | |
245 goto done if $uri->{abort}; | |
246 select undef, undef, undef, ($uri->{split_delay} || 0.2); | |
247 } | |
248 | |
249 raw_write($ctx->{socket}, $buf); | |
250 done: | |
251 return $ctx->{last_stream}; | |
252 } | |
253 | |
254 sub read { | |
255 my ($sess, %extra) = @_; | |
256 my (@got); | |
257 my $s = $sess->{socket}; | |
258 my $buf = ''; | |
259 my $wait = $extra{wait}; | |
260 | |
261 local $Data::Dumper::Terse = 1; | |
262 | |
263 while (1) { | |
264 $buf = raw_read($s, $buf, 9, \&log_in, $wait); | |
265 last if length $buf < 9; | |
266 | |
267 my $length = unpack_length($buf); | |
268 my $type = unpack('x3C', $buf); | |
269 my $flags = unpack('x4C', $buf); | |
270 | |
271 my $stream = unpack "x5 B32", $buf; | |
272 substr($stream, 0, 1) = 0; | |
273 $stream = unpack("N", pack("B32", $stream)); | |
274 | |
275 $buf = raw_read($s, $buf, $length + 9, \&log_in, $wait); | |
276 last if length($buf) < $length + 9; | |
277 | |
278 $buf = substr($buf, 9); | |
279 | |
280 my $frame = $cframe{$type}{value}($sess, $buf, $length, $flags, | |
281 $stream); | |
282 $frame->{length} = $length; | |
283 $frame->{type} = $cframe{$type}{name}; | |
284 $frame->{flags} = $flags; | |
285 $frame->{sid} = $stream; | |
286 push @got, $frame; | |
287 | |
288 Test::Nginx::log_core('||', $_) for split "\n", Dumper $frame; | |
289 | |
290 $buf = substr($buf, $length); | |
291 | |
292 last unless $extra{all} && test_fin($got[-1], $extra{all}); | |
293 }; | |
294 return \@got; | |
295 } | |
296 | |
297 sub test_fin { | 342 sub test_fin { |
298 my ($frame, $all) = @_; | 343 my ($frame, $all) = @_; |
299 my @test = @{$all}; | 344 my @test = @{$all}; |
300 | 345 |
301 # wait for the specified DATA length | 346 # wait for the specified DATA length |
400 sub unpack_length { | 445 sub unpack_length { |
401 unpack 'N', pack 'xc3', unpack 'c3', $_[0]; | 446 unpack 'N', pack 'xc3', unpack 'c3', $_[0]; |
402 } | 447 } |
403 | 448 |
404 sub raw_read { | 449 sub raw_read { |
405 my ($s, $buf, $len, $log, $timo) = @_; | 450 my ($s, $buf, $len, $timo) = @_; |
406 $log = \&log_in unless defined $log; | |
407 $timo = 3 unless $timo; | 451 $timo = 3 unless $timo; |
408 my $got = ''; | 452 my $got = ''; |
409 | 453 |
410 while (length($buf) < $len && IO::Select->new($s)->can_read($timo)) { | 454 while (length($buf) < $len && IO::Select->new($s)->can_read($timo)) { |
411 $s->sysread($got, 16384) or last; | 455 $s->sysread($got, 16384) or last; |
412 $log->($got); | 456 log_in($got); |
413 $buf .= $got; | 457 $buf .= $got; |
414 } | 458 } |
415 return $buf; | 459 return $buf; |
416 } | 460 } |
417 | 461 |
425 my $n = $s->syswrite($message); | 469 my $n = $s->syswrite($message); |
426 last unless $n; | 470 last unless $n; |
427 $message = substr($message, $n); | 471 $message = substr($message, $n); |
428 last unless length $message; | 472 last unless length $message; |
429 } | 473 } |
430 } | |
431 | |
432 sub new { | |
433 my $class = shift; | |
434 my ($port, %extra) = @_; | |
435 | |
436 my $s = $extra{socket} || new_socket($port, %extra); | |
437 my $preface = $extra{preface} | |
438 || 'PRI * HTTP/2.0' . CRLF . CRLF . 'SM' . CRLF . CRLF; | |
439 | |
440 if ($extra{proxy}) { | |
441 raw_write($s, $extra{proxy}); | |
442 } | |
443 | |
444 # preface | |
445 | |
446 raw_write($s, $preface); | |
447 | |
448 my $ctx = { socket => $s, last_stream => -1, | |
449 dynamic_encode => [ static_table() ], | |
450 dynamic_decode => [ static_table() ], | |
451 static_table_size => scalar @{[static_table()]}, | |
452 iws => 65535, conn_window => 65535, streams => {}}; | |
453 bless $ctx, $class; | |
454 | |
455 return $ctx if $extra{pure}; | |
456 | |
457 # update windows, if any | |
458 | |
459 my $frames = $ctx->read(all => [ | |
460 { type => 'WINDOW_UPDATE' }, | |
461 { type => 'SETTINGS'} | |
462 ]); | |
463 | |
464 # 6.5.3. Settings Synchronization | |
465 | |
466 if (grep { $_->{type} eq "SETTINGS" && $_->{flags} == 0 } @$frames) { | |
467 h2_settings($ctx, 1); | |
468 } | |
469 | |
470 return $ctx; | |
471 } | 474 } |
472 | 475 |
473 sub new_socket { | 476 sub new_socket { |
474 my ($port, %extra) = @_; | 477 my ($port, %extra) = @_; |
475 my $npn = $extra{'npn'}; | 478 my $npn = $extra{'npn'}; |