Mercurial > hg > nginx-tests
comparison h2.t @ 948:4dc302d8e04f
Tests: changed HTTP2 package to act as a class.
Stopped exporting any subroutines. A subset of them now act as class methods.
author | Sergey Kandaurov <pluknet@nginx.com> |
---|---|
date | Fri, 17 Jun 2016 11:36:33 +0300 |
parents | d73bef563aea |
children | e9064d691790 |
comparison
equal
deleted
inserted
replaced
947:b9e42c554ba7 | 948:4dc302d8e04f |
---|---|
16 | 16 |
17 BEGIN { use FindBin; chdir($FindBin::Bin); } | 17 BEGIN { use FindBin; chdir($FindBin::Bin); } |
18 | 18 |
19 use lib 'lib'; | 19 use lib 'lib'; |
20 use Test::Nginx; | 20 use Test::Nginx; |
21 use Test::Nginx::HTTP2 qw/ :DEFAULT :frame :io /; | 21 use Test::Nginx::HTTP2; |
22 | 22 |
23 ############################################################################### | 23 ############################################################################### |
24 | 24 |
25 select STDERR; $| = 1; | 25 select STDERR; $| = 1; |
26 select STDOUT; $| = 1; | 26 select STDOUT; $| = 1; |
185 | 185 |
186 } | 186 } |
187 | 187 |
188 # SETTINGS | 188 # SETTINGS |
189 | 189 |
190 my $sess = new_session(8080, pure => 1); | 190 my $s = Test::Nginx::HTTP2->new(8080, pure => 1); |
191 my $frames = h2_read($sess, all => [ | 191 my $frames = $s->read(all => [ |
192 { type => 'WINDOW_UPDATE' }, | 192 { type => 'WINDOW_UPDATE' }, |
193 { type => 'SETTINGS'} | 193 { type => 'SETTINGS'} |
194 ]); | 194 ]); |
195 | 195 |
196 my ($frame) = grep { $_->{type} eq 'WINDOW_UPDATE' } @$frames; | 196 my ($frame) = grep { $_->{type} eq 'WINDOW_UPDATE' } @$frames; |
202 ($frame) = grep { $_->{type} eq 'SETTINGS' } @$frames; | 202 ($frame) = grep { $_->{type} eq 'SETTINGS' } @$frames; |
203 ok($frame, 'SETTINGS frame'); | 203 ok($frame, 'SETTINGS frame'); |
204 is($frame->{flags}, 0, 'SETTINGS flags'); | 204 is($frame->{flags}, 0, 'SETTINGS flags'); |
205 is($frame->{sid}, 0, 'SETTINGS stream'); | 205 is($frame->{sid}, 0, 'SETTINGS stream'); |
206 | 206 |
207 h2_settings($sess, 1); | 207 $s->h2_settings(1); |
208 h2_settings($sess, 0); | 208 $s->h2_settings(0); |
209 | 209 |
210 $frames = h2_read($sess, all => [{ type => 'SETTINGS' }]); | 210 $frames = $s->read(all => [{ type => 'SETTINGS' }]); |
211 | 211 |
212 ($frame) = grep { $_->{type} eq 'SETTINGS' } @$frames; | 212 ($frame) = grep { $_->{type} eq 'SETTINGS' } @$frames; |
213 ok($frame, 'SETTINGS frame ack'); | 213 ok($frame, 'SETTINGS frame ack'); |
214 is($frame->{flags}, 1, 'SETTINGS flags ack'); | 214 is($frame->{flags}, 1, 'SETTINGS flags ack'); |
215 | 215 |
216 # PING | 216 # PING |
217 | 217 |
218 h2_ping($sess, 'SEE-THIS'); | 218 $s->h2_ping('SEE-THIS'); |
219 $frames = h2_read($sess, all => [{ type => 'PING' }]); | 219 $frames = $s->read(all => [{ type => 'PING' }]); |
220 | 220 |
221 ($frame) = grep { $_->{type} eq "PING" } @$frames; | 221 ($frame) = grep { $_->{type} eq "PING" } @$frames; |
222 ok($frame, 'PING frame'); | 222 ok($frame, 'PING frame'); |
223 is($frame->{value}, 'SEE-THIS', 'PING payload'); | 223 is($frame->{value}, 'SEE-THIS', 'PING payload'); |
224 is($frame->{flags}, 1, 'PING flags ack'); | 224 is($frame->{flags}, 1, 'PING flags ack'); |
227 # timeouts | 227 # timeouts |
228 | 228 |
229 SKIP: { | 229 SKIP: { |
230 skip 'long tests', 6 unless $ENV{TEST_NGINX_UNSAFE}; | 230 skip 'long tests', 6 unless $ENV{TEST_NGINX_UNSAFE}; |
231 | 231 |
232 push my @sess, new_session(8089, pure => 1); | 232 push my @s, Test::Nginx::HTTP2->new(8089, pure => 1); |
233 push @sess, new_session(8089, pure => 1); | 233 push @s, Test::Nginx::HTTP2->new(8089, pure => 1); |
234 h2_ping($sess[-1], 'SEE-THIS'); | 234 $s[-1]->h2_ping('SEE-THIS'); |
235 push @sess, new_session(8090, pure => 1); | 235 push @s, Test::Nginx::HTTP2->new(8090, pure => 1); |
236 push @sess, new_session(8090, pure => 1); | 236 push @s, Test::Nginx::HTTP2->new(8090, pure => 1); |
237 h2_ping($sess[-1], 'SEE-THIS'); | 237 $s[-1]->h2_ping('SEE-THIS'); |
238 | 238 |
239 select undef, undef, undef, 2.1; | 239 select undef, undef, undef, 2.1; |
240 | 240 |
241 $frames = h2_read(shift @sess, all => [{ type => "GOAWAY" }]); | 241 $frames = (shift @s)->read(all => [{ type => "GOAWAY" }]); |
242 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; | 242 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; |
243 ok($frame, 'recv timeout - new connection GOAWAY'); | 243 ok($frame, 'recv timeout - new connection GOAWAY'); |
244 is($frame->{code}, 1, 'recv timeout - new connection code'); | 244 is($frame->{code}, 1, 'recv timeout - new connection code'); |
245 | 245 |
246 $frames = h2_read(shift @sess, all => [{ type => "GOAWAY" }]); | 246 $frames = (shift @s)->read(all => [{ type => "GOAWAY" }]); |
247 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; | 247 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; |
248 is($frame, undef, 'recv timeout - idle connection GOAWAY'); | 248 is($frame, undef, 'recv timeout - idle connection GOAWAY'); |
249 | 249 |
250 $frames = h2_read(shift @sess, all => [{ type => "GOAWAY" }]); | 250 $frames = (shift @s)->read(all => [{ type => "GOAWAY" }]); |
251 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; | 251 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; |
252 is($frame, undef, 'idle timeout - new connection GOAWAY'); | 252 is($frame, undef, 'idle timeout - new connection GOAWAY'); |
253 | 253 |
254 $frames = h2_read(shift @sess, all => [{ type => "GOAWAY" }]); | 254 $frames = (shift @s)->read(all => [{ type => "GOAWAY" }]); |
255 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; | 255 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; |
256 ok($frame, 'idle timeout - idle connection GOAWAY'); | 256 ok($frame, 'idle timeout - idle connection GOAWAY'); |
257 is($frame->{code}, 0, 'idle timeout - idle connection code'); | 257 is($frame->{code}, 0, 'idle timeout - idle connection code'); |
258 | 258 |
259 } | 259 } |
260 | 260 |
261 # GOAWAY | 261 # GOAWAY |
262 | 262 |
263 h2_goaway(new_session(), 0, 0, 5); | 263 Test::Nginx::HTTP2->new()->h2_goaway(0, 0, 5); |
264 h2_goaway(new_session(), 0, 0, 5, 'foobar'); | 264 Test::Nginx::HTTP2->new()->h2_goaway(0, 0, 5, 'foobar'); |
265 h2_goaway(new_session(), 0, 0, 5, 'foobar', split => [ 8, 8, 4 ]); | 265 Test::Nginx::HTTP2->new()->h2_goaway(0, 0, 5, 'foobar', split => [ 8, 8, 4 ]); |
266 | 266 |
267 $sess = new_session(); | 267 $s = Test::Nginx::HTTP2->new(); |
268 h2_goaway($sess, 0, 0, 5); | 268 $s->h2_goaway(0, 0, 5); |
269 h2_goaway($sess, 0, 0, 5); | 269 $s->h2_goaway(0, 0, 5); |
270 | 270 |
271 $sess = new_session(); | 271 $s = Test::Nginx::HTTP2->new(); |
272 h2_goaway($sess, 0, 0, 5, 'foobar', len => 0); | 272 $s->h2_goaway(0, 0, 5, 'foobar', len => 0); |
273 $frames = h2_read($sess, all => [{ type => "GOAWAY" }]); | 273 $frames = $s->read(all => [{ type => "GOAWAY" }]); |
274 | 274 |
275 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; | 275 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; |
276 ok($frame, 'GOAWAY invalid length - GOAWAY frame'); | 276 ok($frame, 'GOAWAY invalid length - GOAWAY frame'); |
277 is($frame->{code}, 6, 'GOAWAY invalid length - GOAWAY FRAME_SIZE_ERROR'); | 277 is($frame->{code}, 6, 'GOAWAY invalid length - GOAWAY FRAME_SIZE_ERROR'); |
278 | 278 |
281 # than 0x0 as a connection error (Section 5.4.1) of type PROTOCOL_ERROR. | 281 # than 0x0 as a connection error (Section 5.4.1) of type PROTOCOL_ERROR. |
282 | 282 |
283 TODO: { | 283 TODO: { |
284 local $TODO = 'not yet'; | 284 local $TODO = 'not yet'; |
285 | 285 |
286 $sess = new_session(); | 286 $s = Test::Nginx::HTTP2->new(); |
287 h2_goaway($sess, 1, 0, 5, 'foobar'); | 287 $s->h2_goaway(1, 0, 5, 'foobar'); |
288 $frames = h2_read($sess, all => [{ type => "GOAWAY" }], wait => 0.5); | 288 $frames = $s->read(all => [{ type => "GOAWAY" }], wait => 0.5); |
289 | 289 |
290 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; | 290 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; |
291 ok($frame, 'GOAWAY invalid stream - GOAWAY frame'); | 291 ok($frame, 'GOAWAY invalid stream - GOAWAY frame'); |
292 is($frame->{code}, 1, 'GOAWAY invalid stream - GOAWAY PROTOCOL_ERROR'); | 292 is($frame->{code}, 1, 'GOAWAY invalid stream - GOAWAY PROTOCOL_ERROR'); |
293 | 293 |
294 } | 294 } |
295 | 295 |
296 # client-initiated PUSH_PROMISE, just to ensure nothing went wrong | 296 # client-initiated PUSH_PROMISE, just to ensure nothing went wrong |
297 # N.B. other implementation returns zero code, which is not anyhow regulated | 297 # N.B. other implementation returns zero code, which is not anyhow regulated |
298 | 298 |
299 $sess = new_session(); | 299 $s = Test::Nginx::HTTP2->new(); |
300 raw_write($sess->{socket}, pack("x2C2xN", 4, 0x5, 1)); | 300 syswrite($s->{socket}, pack("x2C2xN", 4, 0x5, 1)); |
301 $frames = h2_read($sess, all => [{ type => "GOAWAY" }]); | 301 $frames = $s->read(all => [{ type => "GOAWAY" }]); |
302 | 302 |
303 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; | 303 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; |
304 ok($frame, 'client-initiated PUSH_PROMISE - GOAWAY frame'); | 304 ok($frame, 'client-initiated PUSH_PROMISE - GOAWAY frame'); |
305 is($frame->{code}, 1, 'client-initiated PUSH_PROMISE - GOAWAY PROTOCOL_ERROR'); | 305 is($frame->{code}, 1, 'client-initiated PUSH_PROMISE - GOAWAY PROTOCOL_ERROR'); |
306 | 306 |
307 # GET | 307 # GET |
308 | 308 |
309 $sess = new_session(); | 309 $s = Test::Nginx::HTTP2->new(); |
310 my $sid = new_stream($sess); | 310 my $sid = $s->new_stream(); |
311 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 311 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
312 | 312 |
313 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 313 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
314 ok($frame, 'HEADERS frame'); | 314 ok($frame, 'HEADERS frame'); |
315 is($frame->{sid}, $sid, 'HEADERS stream'); | 315 is($frame->{sid}, $sid, 'HEADERS stream'); |
316 is($frame->{headers}->{':status'}, 200, 'HEADERS status'); | 316 is($frame->{headers}->{':status'}, 200, 'HEADERS status'); |
321 is($frame->{length}, length 'body', 'DATA length'); | 321 is($frame->{length}, length 'body', 'DATA length'); |
322 is($frame->{data}, 'body', 'DATA payload'); | 322 is($frame->{data}, 'body', 'DATA payload'); |
323 | 323 |
324 # GET in the new stream on same connection | 324 # GET in the new stream on same connection |
325 | 325 |
326 $sid = new_stream($sess); | 326 $sid = $s->new_stream(); |
327 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 327 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
328 | 328 |
329 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 329 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
330 is($frame->{sid}, $sid, 'HEADERS stream 2'); | 330 is($frame->{sid}, $sid, 'HEADERS stream 2'); |
331 is($frame->{headers}->{':status'}, 200, 'HEADERS status 2'); | 331 is($frame->{headers}->{':status'}, 200, 'HEADERS status 2'); |
332 is($frame->{headers}->{'x-header'}, 'X-Foo', 'HEADERS header 2'); | 332 is($frame->{headers}->{'x-header'}, 'X-Foo', 'HEADERS header 2'); |
337 is($frame->{length}, length 'body', 'DATA length 2'); | 337 is($frame->{length}, length 'body', 'DATA length 2'); |
338 is($frame->{data}, 'body', 'DATA payload 2'); | 338 is($frame->{data}, 'body', 'DATA payload 2'); |
339 | 339 |
340 # HEAD | 340 # HEAD |
341 | 341 |
342 $sess = new_session(); | 342 $s = Test::Nginx::HTTP2->new(); |
343 $sid = new_stream($sess, { method => 'HEAD' }); | 343 $sid = $s->new_stream({ method => 'HEAD' }); |
344 $frames = h2_read($sess, all => [{ sid => $sid, fin => 0x4 }]); | 344 $frames = $s->read(all => [{ sid => $sid, fin => 0x4 }]); |
345 | 345 |
346 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 346 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
347 is($frame->{sid}, $sid, 'HEAD - HEADERS'); | 347 is($frame->{sid}, $sid, 'HEAD - HEADERS'); |
348 is($frame->{headers}->{':status'}, 200, 'HEAD - HEADERS status'); | 348 is($frame->{headers}->{':status'}, 200, 'HEAD - HEADERS status'); |
349 is($frame->{headers}->{'x-header'}, 'X-Foo', 'HEAD - HEADERS header'); | 349 is($frame->{headers}->{'x-header'}, 'X-Foo', 'HEAD - HEADERS header'); |
351 ($frame) = grep { $_->{type} eq "DATA" } @$frames; | 351 ($frame) = grep { $_->{type} eq "DATA" } @$frames; |
352 is($frame, undef, 'HEAD - no body'); | 352 is($frame, undef, 'HEAD - no body'); |
353 | 353 |
354 # range filter | 354 # range filter |
355 | 355 |
356 $sess = new_session(); | 356 $s = Test::Nginx::HTTP2->new(); |
357 $sid = new_stream($sess, { headers => [ | 357 $sid = $s->new_stream({ headers => [ |
358 { name => ':method', value => 'GET', mode => 0 }, | 358 { name => ':method', value => 'GET', mode => 0 }, |
359 { name => ':scheme', value => 'http', mode => 0 }, | 359 { name => ':scheme', value => 'http', mode => 0 }, |
360 { name => ':path', value => '/t1.html', mode => 1 }, | 360 { name => ':path', value => '/t1.html', mode => 1 }, |
361 { name => ':authority', value => 'localhost', mode => 1 }, | 361 { name => ':authority', value => 'localhost', mode => 1 }, |
362 { name => 'range', value => 'bytes=10-19', mode => 1 }]}); | 362 { name => 'range', value => 'bytes=10-19', mode => 1 }]}); |
363 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 363 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
364 | 364 |
365 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 365 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
366 is($frame->{headers}->{':status'}, 206, 'range - HEADERS status'); | 366 is($frame->{headers}->{':status'}, 206, 'range - HEADERS status'); |
367 | 367 |
368 ($frame) = grep { $_->{type} eq "DATA" } @$frames; | 368 ($frame) = grep { $_->{type} eq "DATA" } @$frames; |
369 is($frame->{length}, 10, 'range - DATA length'); | 369 is($frame->{length}, 10, 'range - DATA length'); |
370 is($frame->{data}, '002XXXX000', 'range - DATA payload'); | 370 is($frame->{data}, '002XXXX000', 'range - DATA payload'); |
371 | 371 |
372 # http2_chunk_size=1 | 372 # http2_chunk_size=1 |
373 | 373 |
374 $sess = new_session(); | 374 $s = Test::Nginx::HTTP2->new(); |
375 $sid = new_stream($sess, { path => '/chunk_size' }); | 375 $sid = $s->new_stream({ path => '/chunk_size' }); |
376 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 376 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
377 | 377 |
378 my @data = grep { $_->{type} eq "DATA" } @$frames; | 378 my @data = grep { $_->{type} eq "DATA" } @$frames; |
379 is(@data, 4, 'chunk_size frames'); | 379 is(@data, 4, 'chunk_size frames'); |
380 is(join(' ', map { $_->{data} } @data), 'b o d y', 'chunk_size data'); | 380 is(join(' ', map { $_->{data} } @data), 'b o d y', 'chunk_size data'); |
381 is(join(' ', map { $_->{flags} } @data), '0 0 0 1', 'chunk_size flags'); | 381 is(join(' ', map { $_->{flags} } @data), '0 0 0 1', 'chunk_size flags'); |
382 | 382 |
383 # CONTINUATION | 383 # CONTINUATION |
384 | 384 |
385 $sess = new_session(); | 385 $s = Test::Nginx::HTTP2->new(); |
386 $sid = new_stream($sess, { continuation => 1, headers => [ | 386 $sid = $s->new_stream({ continuation => 1, headers => [ |
387 { name => ':method', value => 'HEAD', mode => 1 }, | 387 { name => ':method', value => 'HEAD', mode => 1 }, |
388 { name => ':scheme', value => 'http', mode => 0 }, | 388 { name => ':scheme', value => 'http', mode => 0 }, |
389 { name => ':path', value => '/', mode => 0 }, | 389 { name => ':path', value => '/', mode => 0 }, |
390 { name => ':authority', value => 'localhost', mode => 1 }]}); | 390 { name => ':authority', value => 'localhost', mode => 1 }]}); |
391 h2_continue($sess, $sid, { continuation => 1, headers => [ | 391 $s->h2_continue($sid, { continuation => 1, headers => [ |
392 { name => 'x-foo', value => 'X-Bar', mode => 2 }]}); | 392 { name => 'x-foo', value => 'X-Bar', mode => 2 }]}); |
393 h2_continue($sess, $sid, { headers => [ | 393 $s->h2_continue($sid, { headers => [ |
394 { name => 'referer', value => 'foo', mode => 2 }]}); | 394 { name => 'referer', value => 'foo', mode => 2 }]}); |
395 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 395 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
396 | 396 |
397 ($frame) = grep { $_->{type} eq "DATA" } @$frames; | 397 ($frame) = grep { $_->{type} eq "DATA" } @$frames; |
398 is($frame, undef, 'CONTINUATION - fragment 1'); | 398 is($frame, undef, 'CONTINUATION - fragment 1'); |
399 | 399 |
400 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 400 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
401 is($frame->{headers}->{'x-sent-foo'}, 'X-Bar', 'CONTINUATION - fragment 2'); | 401 is($frame->{headers}->{'x-sent-foo'}, 'X-Bar', 'CONTINUATION - fragment 2'); |
402 is($frame->{headers}->{'x-referer'}, 'foo', 'CONTINUATION - fragment 3'); | 402 is($frame->{headers}->{'x-referer'}, 'foo', 'CONTINUATION - fragment 3'); |
403 | 403 |
404 # CONTINUATION - in the middle of request header field | 404 # CONTINUATION - in the middle of request header field |
405 | 405 |
406 $sess = new_session(); | 406 $s = Test::Nginx::HTTP2->new(); |
407 $sid = new_stream($sess, { continuation => [ 2, 4, 1, 5 ], headers => [ | 407 $sid = $s->new_stream({ continuation => [ 2, 4, 1, 5 ], headers => [ |
408 { name => ':method', value => 'HEAD', mode => 1 }, | 408 { name => ':method', value => 'HEAD', mode => 1 }, |
409 { name => ':scheme', value => 'http', mode => 0 }, | 409 { name => ':scheme', value => 'http', mode => 0 }, |
410 { name => ':path', value => '/', mode => 0 }, | 410 { name => ':path', value => '/', mode => 0 }, |
411 { name => ':authority', value => 'localhost', mode => 1 }]}); | 411 { name => ':authority', value => 'localhost', mode => 1 }]}); |
412 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 412 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
413 | 413 |
414 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 414 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
415 is($frame->{headers}->{':status'}, 200, 'CONTINUATION - in header field'); | 415 is($frame->{headers}->{':status'}, 200, 'CONTINUATION - in header field'); |
416 | 416 |
417 # CONTINUATION on a closed stream | 417 # CONTINUATION on a closed stream |
418 | 418 |
419 h2_continue($sess, 1, { headers => [ | 419 $s->h2_continue(1, { headers => [ |
420 { name => 'x-foo', value => 'X-Bar', mode => 2 }]}); | 420 { name => 'x-foo', value => 'X-Bar', mode => 2 }]}); |
421 $frames = h2_read($sess, all => [{ sid => 1, fin => 1 }]); | 421 $frames = $s->read(all => [{ sid => 1, fin => 1 }]); |
422 | 422 |
423 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; | 423 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; |
424 is($frame->{type}, 'GOAWAY', 'GOAWAY - CONTINUATION closed stream'); | 424 is($frame->{type}, 'GOAWAY', 'GOAWAY - CONTINUATION closed stream'); |
425 is($frame->{code}, 1, 'GOAWAY - CONTINUATION closed stream - PROTOCOL_ERROR'); | 425 is($frame->{code}, 1, 'GOAWAY - CONTINUATION closed stream - PROTOCOL_ERROR'); |
426 | 426 |
427 # frame padding | 427 # frame padding |
428 | 428 |
429 $sess = new_session(); | 429 $s = Test::Nginx::HTTP2->new(); |
430 $sid = new_stream($sess, { padding => 42, headers => [ | 430 $sid = $s->new_stream({ padding => 42, headers => [ |
431 { name => ':method', value => 'GET', mode => 0 }, | 431 { name => ':method', value => 'GET', mode => 0 }, |
432 { name => ':scheme', value => 'http', mode => 0 }, | 432 { name => ':scheme', value => 'http', mode => 0 }, |
433 { name => ':path', value => '/', mode => 0 }, | 433 { name => ':path', value => '/', mode => 0 }, |
434 { name => ':authority', value => 'localhost', mode => 1 }]}); | 434 { name => ':authority', value => 'localhost', mode => 1 }]}); |
435 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 435 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
436 | 436 |
437 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 437 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
438 is($frame->{headers}->{':status'}, 200, 'padding - HEADERS status'); | 438 is($frame->{headers}->{':status'}, 200, 'padding - HEADERS status'); |
439 | 439 |
440 $sid = new_stream($sess, { headers => [ | 440 $sid = $s->new_stream({ headers => [ |
441 { name => ':method', value => 'GET', mode => 0 }, | 441 { name => ':method', value => 'GET', mode => 0 }, |
442 { name => ':scheme', value => 'http', mode => 0 }, | 442 { name => ':scheme', value => 'http', mode => 0 }, |
443 { name => ':path', value => '/', mode => 0 }, | 443 { name => ':path', value => '/', mode => 0 }, |
444 { name => ':authority', value => 'localhost', mode => 1 }]}); | 444 { name => ':authority', value => 'localhost', mode => 1 }]}); |
445 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 445 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
446 | 446 |
447 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 447 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
448 is($frame->{headers}->{':status'}, 200, 'padding - next stream'); | 448 is($frame->{headers}->{':status'}, 200, 'padding - next stream'); |
449 | 449 |
450 # padding followed by CONTINUATION | 450 # padding followed by CONTINUATION |
451 | 451 |
452 TODO: { | 452 TODO: { |
453 local $TODO = 'not yet' unless $t->has_version('1.9.11'); | 453 local $TODO = 'not yet' unless $t->has_version('1.9.11'); |
454 | 454 |
455 $sess = new_session(); | 455 $s = Test::Nginx::HTTP2->new(); |
456 $sid = new_stream($sess, { padding => 42, continuation => [ 2, 4, 1, 5 ], | 456 $sid = $s->new_stream({ padding => 42, continuation => [ 2, 4, 1, 5 ], |
457 headers => [ | 457 headers => [ |
458 { name => ':method', value => 'GET', mode => 1 }, | 458 { name => ':method', value => 'GET', mode => 1 }, |
459 { name => ':scheme', value => 'http', mode => 0 }, | 459 { name => ':scheme', value => 'http', mode => 0 }, |
460 { name => ':path', value => '/', mode => 0 }, | 460 { name => ':path', value => '/', mode => 0 }, |
461 { name => ':authority', value => 'localhost', mode => 1 }]}); | 461 { name => ':authority', value => 'localhost', mode => 1 }]}); |
462 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 462 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
463 | 463 |
464 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 464 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
465 is($frame->{headers}->{':status'}, 200, 'padding - CONTINUATION'); | 465 is($frame->{headers}->{':status'}, 200, 'padding - CONTINUATION'); |
466 | 466 |
467 } | 467 } |
468 | 468 |
469 # internal redirect | 469 # internal redirect |
470 | 470 |
471 $sess = new_session(); | 471 $s = Test::Nginx::HTTP2->new(); |
472 $sid = new_stream($sess, { path => '/redirect' }); | 472 $sid = $s->new_stream({ path => '/redirect' }); |
473 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 473 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
474 | 474 |
475 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 475 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
476 is($frame->{headers}->{':status'}, 405, 'redirect - HEADERS'); | 476 is($frame->{headers}->{':status'}, 405, 'redirect - HEADERS'); |
477 | 477 |
478 ($frame) = grep { $_->{type} eq "DATA" } @$frames; | 478 ($frame) = grep { $_->{type} eq "DATA" } @$frames; |
479 ok($frame, 'redirect - DATA'); | 479 ok($frame, 'redirect - DATA'); |
480 is($frame->{data}, 'body', 'redirect - DATA payload'); | 480 is($frame->{data}, 'body', 'redirect - DATA payload'); |
481 | 481 |
482 # return 301 with absolute URI | 482 # return 301 with absolute URI |
483 | 483 |
484 $sess = new_session(); | 484 $s = Test::Nginx::HTTP2->new(); |
485 $sid = new_stream($sess, { path => '/return301_absolute' }); | 485 $sid = $s->new_stream({ path => '/return301_absolute' }); |
486 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 486 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
487 | 487 |
488 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 488 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
489 is($frame->{headers}->{':status'}, 301, 'return 301 absolute - status'); | 489 is($frame->{headers}->{':status'}, 301, 'return 301 absolute - status'); |
490 is($frame->{headers}->{'location'}, 'text', 'return 301 absolute - location'); | 490 is($frame->{headers}->{'location'}, 'text', 'return 301 absolute - location'); |
491 | 491 |
492 # return 301 with relative URI | 492 # return 301 with relative URI |
493 | 493 |
494 $sess = new_session(); | 494 $s = Test::Nginx::HTTP2->new(); |
495 $sid = new_stream($sess, { path => '/return301_relative' }); | 495 $sid = $s->new_stream({ path => '/return301_relative' }); |
496 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 496 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
497 | 497 |
498 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 498 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
499 is($frame->{headers}->{':status'}, 301, 'return 301 relative - status'); | 499 is($frame->{headers}->{':status'}, 301, 'return 301 relative - status'); |
500 is($frame->{headers}->{'location'}, 'http://localhost:8080/', | 500 is($frame->{headers}->{'location'}, 'http://localhost:8080/', |
501 'return 301 relative - location'); | 501 'return 301 relative - location'); |
502 | 502 |
503 # return 301 with relative URI and ':authority' request header field | 503 # return 301 with relative URI and ':authority' request header field |
504 | 504 |
505 $sess = new_session(); | 505 $s = Test::Nginx::HTTP2->new(); |
506 $sid = new_stream($sess, { headers => [ | 506 $sid = $s->new_stream({ headers => [ |
507 { name => ':method', value => 'GET', mode => 0 }, | 507 { name => ':method', value => 'GET', mode => 0 }, |
508 { name => ':scheme', value => 'http', mode => 0 }, | 508 { name => ':scheme', value => 'http', mode => 0 }, |
509 { name => ':path', value => '/return301_relative', mode => 2 }, | 509 { name => ':path', value => '/return301_relative', mode => 2 }, |
510 { name => ':authority', value => 'localhost', mode => 2 }]}); | 510 { name => ':authority', value => 'localhost', mode => 2 }]}); |
511 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 511 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
512 | 512 |
513 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 513 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
514 is($frame->{headers}->{':status'}, 301, | 514 is($frame->{headers}->{':status'}, 301, |
515 'return 301 relative - authority - status'); | 515 'return 301 relative - authority - status'); |
516 is($frame->{headers}->{'location'}, 'http://localhost:8080/', | 516 is($frame->{headers}->{'location'}, 'http://localhost:8080/', |
517 'return 301 relative - authority - location'); | 517 'return 301 relative - authority - location'); |
518 | 518 |
519 # return 301 with relative URI and 'host' request header field | 519 # return 301 with relative URI and 'host' request header field |
520 | 520 |
521 $sess = new_session(); | 521 $s = Test::Nginx::HTTP2->new(); |
522 $sid = new_stream($sess, { headers => [ | 522 $sid = $s->new_stream({ headers => [ |
523 { name => ':method', value => 'GET', mode => 0 }, | 523 { name => ':method', value => 'GET', mode => 0 }, |
524 { name => ':scheme', value => 'http', mode => 0 }, | 524 { name => ':scheme', value => 'http', mode => 0 }, |
525 { name => ':path', value => '/return301_relative', mode => 2 }, | 525 { name => ':path', value => '/return301_relative', mode => 2 }, |
526 { name => 'host', value => 'localhost', mode => 2 }]}); | 526 { name => 'host', value => 'localhost', mode => 2 }]}); |
527 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 527 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
528 | 528 |
529 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 529 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
530 is($frame->{headers}->{':status'}, 301, | 530 is($frame->{headers}->{':status'}, 301, |
531 'return 301 relative - host - status'); | 531 'return 301 relative - host - status'); |
532 is($frame->{headers}->{'location'}, 'http://localhost:8080/', | 532 is($frame->{headers}->{'location'}, 'http://localhost:8080/', |
533 'return 301 relative - host - location'); | 533 'return 301 relative - host - location'); |
534 | 534 |
535 # virtual host | 535 # virtual host |
536 | 536 |
537 $sess = new_session(8085); | 537 $s = Test::Nginx::HTTP2->new(8085); |
538 $sid = new_stream($sess, { headers => [ | 538 $sid = $s->new_stream({ headers => [ |
539 { name => ':method', value => 'GET', mode => 0 }, | 539 { name => ':method', value => 'GET', mode => 0 }, |
540 { name => ':scheme', value => 'http', mode => 0 }, | 540 { name => ':scheme', value => 'http', mode => 0 }, |
541 { name => ':path', value => '/', mode => 0 }, | 541 { name => ':path', value => '/', mode => 0 }, |
542 { name => 'host', value => 'localhost', mode => 2 }]}); | 542 { name => 'host', value => 'localhost', mode => 2 }]}); |
543 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 543 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
544 | 544 |
545 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 545 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
546 is($frame->{headers}->{':status'}, 200, | 546 is($frame->{headers}->{':status'}, 200, |
547 'virtual host - host - status'); | 547 'virtual host - host - status'); |
548 | 548 |
549 ($frame) = grep { $_->{type} eq "DATA" } @$frames; | 549 ($frame) = grep { $_->{type} eq "DATA" } @$frames; |
550 is($frame->{data}, 'first', 'virtual host - host - DATA'); | 550 is($frame->{data}, 'first', 'virtual host - host - DATA'); |
551 | 551 |
552 $sid = new_stream($sess, { headers => [ | 552 $sid = $s->new_stream({ headers => [ |
553 { name => ':method', value => 'GET', mode => 0 }, | 553 { name => ':method', value => 'GET', mode => 0 }, |
554 { name => ':scheme', value => 'http', mode => 0 }, | 554 { name => ':scheme', value => 'http', mode => 0 }, |
555 { name => ':path', value => '/', mode => 0 }, | 555 { name => ':path', value => '/', mode => 0 }, |
556 { name => ':authority', value => 'localhost', mode => 2 }]}); | 556 { name => ':authority', value => 'localhost', mode => 2 }]}); |
557 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 557 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
558 | 558 |
559 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 559 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
560 is($frame->{headers}->{':status'}, 200, | 560 is($frame->{headers}->{':status'}, 200, |
561 'virtual host - authority - status'); | 561 'virtual host - authority - status'); |
562 | 562 |
563 ($frame) = grep { $_->{type} eq "DATA" } @$frames; | 563 ($frame) = grep { $_->{type} eq "DATA" } @$frames; |
564 is($frame->{data}, 'first', 'virtual host - authority - DATA'); | 564 is($frame->{data}, 'first', 'virtual host - authority - DATA'); |
565 | 565 |
566 # virtual host - second | 566 # virtual host - second |
567 | 567 |
568 $sid = new_stream($sess, { headers => [ | 568 $sid = $s->new_stream({ headers => [ |
569 { name => ':method', value => 'GET', mode => 0 }, | 569 { name => ':method', value => 'GET', mode => 0 }, |
570 { name => ':scheme', value => 'http', mode => 0 }, | 570 { name => ':scheme', value => 'http', mode => 0 }, |
571 { name => ':path', value => '/', mode => 0 }, | 571 { name => ':path', value => '/', mode => 0 }, |
572 { name => 'host', value => 'localhost2', mode => 2 }]}); | 572 { name => 'host', value => 'localhost2', mode => 2 }]}); |
573 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 573 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
574 | 574 |
575 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 575 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
576 is($frame->{headers}->{':status'}, 200, | 576 is($frame->{headers}->{':status'}, 200, |
577 'virtual host 2 - host - status'); | 577 'virtual host 2 - host - status'); |
578 | 578 |
579 ($frame) = grep { $_->{type} eq "DATA" } @$frames; | 579 ($frame) = grep { $_->{type} eq "DATA" } @$frames; |
580 is($frame->{data}, 'second', 'virtual host 2 - host - DATA'); | 580 is($frame->{data}, 'second', 'virtual host 2 - host - DATA'); |
581 | 581 |
582 $sid = new_stream($sess, { headers => [ | 582 $sid = $s->new_stream({ headers => [ |
583 { name => ':method', value => 'GET', mode => 0 }, | 583 { name => ':method', value => 'GET', mode => 0 }, |
584 { name => ':scheme', value => 'http', mode => 0 }, | 584 { name => ':scheme', value => 'http', mode => 0 }, |
585 { name => ':path', value => '/', mode => 0 }, | 585 { name => ':path', value => '/', mode => 0 }, |
586 { name => ':authority', value => 'localhost2', mode => 2 }]}); | 586 { name => ':authority', value => 'localhost2', mode => 2 }]}); |
587 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 587 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
588 | 588 |
589 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 589 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
590 is($frame->{headers}->{':status'}, 200, | 590 is($frame->{headers}->{':status'}, 200, |
591 'virtual host 2 - authority - status'); | 591 'virtual host 2 - authority - status'); |
592 | 592 |
593 ($frame) = grep { $_->{type} eq "DATA" } @$frames; | 593 ($frame) = grep { $_->{type} eq "DATA" } @$frames; |
594 is($frame->{data}, 'second', 'virtual host 2 - authority - DATA'); | 594 is($frame->{data}, 'second', 'virtual host 2 - authority - DATA'); |
595 | 595 |
596 # gzip tests for internal nginx version | 596 # gzip tests for internal nginx version |
597 | 597 |
598 $sess = new_session(); | 598 $s = Test::Nginx::HTTP2->new(); |
599 $sid = new_stream($sess, { headers => [ | 599 $sid = $s->new_stream({ headers => [ |
600 { name => ':method', value => 'GET', mode => 0 }, | 600 { name => ':method', value => 'GET', mode => 0 }, |
601 { name => ':scheme', value => 'http', mode => 0 }, | 601 { name => ':scheme', value => 'http', mode => 0 }, |
602 { name => ':path', value => '/gzip.html' }, | 602 { name => ':path', value => '/gzip.html' }, |
603 { name => ':authority', value => 'localhost', mode => 1 }, | 603 { name => ':authority', value => 'localhost', mode => 1 }, |
604 { name => 'accept-encoding', value => 'gzip' }]}); | 604 { name => 'accept-encoding', value => 'gzip' }]}); |
605 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 605 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
606 | 606 |
607 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 607 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
608 is($frame->{headers}->{'content-encoding'}, 'gzip', 'gzip - encoding'); | 608 is($frame->{headers}->{'content-encoding'}, 'gzip', 'gzip - encoding'); |
609 is($frame->{headers}->{'vary'}, 'Accept-Encoding', 'gzip - vary'); | 609 is($frame->{headers}->{'vary'}, 'Accept-Encoding', 'gzip - vary'); |
610 | 610 |
611 ($frame) = grep { $_->{type} eq "DATA" } @$frames; | 611 ($frame) = grep { $_->{type} eq "DATA" } @$frames; |
612 gunzip_like($frame->{data}, qr/^SEE-THIS\Z/, 'gzip - DATA'); | 612 gunzip_like($frame->{data}, qr/^SEE-THIS\Z/, 'gzip - DATA'); |
613 | 613 |
614 # charset | 614 # charset |
615 | 615 |
616 $sess = new_session(); | 616 $s = Test::Nginx::HTTP2->new(); |
617 $sid = new_stream($sess, { path => '/charset' }); | 617 $sid = $s->new_stream({ path => '/charset' }); |
618 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 618 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
619 | 619 |
620 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 620 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
621 is($frame->{headers}->{'content-type'}, 'text/plain; charset=utf-8', 'charset'); | 621 is($frame->{headers}->{'content-type'}, 'text/plain; charset=utf-8', 'charset'); |
622 | 622 |
623 # partial request header frame received (field split), | 623 # partial request header frame received (field split), |
624 # the rest of frame is received after client header timeout | 624 # the rest of frame is received after client header timeout |
625 | 625 |
626 TODO: { | 626 TODO: { |
627 local $TODO = 'not yet' unless $t->has_version('1.9.12'); | 627 local $TODO = 'not yet' unless $t->has_version('1.9.12'); |
628 | 628 |
629 $sess = new_session(8093); | 629 $s = Test::Nginx::HTTP2->new(8093); |
630 $sid = new_stream($sess, { path => '/t2.html', split => [35], | 630 $sid = $s->new_stream({ path => '/t2.html', split => [35], |
631 split_delay => 2.1 }); | 631 split_delay => 2.1 }); |
632 $frames = h2_read($sess, all => [{ type => 'RST_STREAM' }]); | 632 $frames = $s->read(all => [{ type => 'RST_STREAM' }]); |
633 | 633 |
634 ($frame) = grep { $_->{type} eq "RST_STREAM" } @$frames; | 634 ($frame) = grep { $_->{type} eq "RST_STREAM" } @$frames; |
635 ok($frame, 'client header timeout'); | 635 ok($frame, 'client header timeout'); |
636 is($frame->{code}, 1, 'client header timeout - protocol error'); | 636 is($frame->{code}, 1, 'client header timeout - protocol error'); |
637 | 637 |
638 } | 638 } |
639 | 639 |
640 h2_ping($sess, 'SEE-THIS'); | 640 $s->h2_ping('SEE-THIS'); |
641 $frames = h2_read($sess, all => [{ type => 'PING' }]); | 641 $frames = $s->read(all => [{ type => 'PING' }]); |
642 | 642 |
643 ($frame) = grep { $_->{type} eq "PING" && $_->{flags} & 0x1 } @$frames; | 643 ($frame) = grep { $_->{type} eq "PING" && $_->{flags} & 0x1 } @$frames; |
644 ok($frame, 'client header timeout - PING'); | 644 ok($frame, 'client header timeout - PING'); |
645 | 645 |
646 # partial request body data frame received, the rest is after body timeout | 646 # partial request body data frame received, the rest is after body timeout |
647 | 647 |
648 TODO: { | 648 TODO: { |
649 local $TODO = 'not yet' unless $t->has_version('1.9.12'); | 649 local $TODO = 'not yet' unless $t->has_version('1.9.12'); |
650 | 650 |
651 $sess = new_session(8093); | 651 $s = Test::Nginx::HTTP2->new(8093); |
652 $sid = new_stream($sess, { path => '/proxy/t2.html', body_more => 1 }); | 652 $sid = $s->new_stream({ path => '/proxy/t2.html', body_more => 1 }); |
653 h2_body($sess, 'TEST', { split => [10], split_delay => 2.1 }); | 653 $s->h2_body('TEST', { split => [10], split_delay => 2.1 }); |
654 $frames = h2_read($sess, all => [{ type => 'RST_STREAM' }]); | 654 $frames = $s->read(all => [{ type => 'RST_STREAM' }]); |
655 | 655 |
656 ($frame) = grep { $_->{type} eq "RST_STREAM" } @$frames; | 656 ($frame) = grep { $_->{type} eq "RST_STREAM" } @$frames; |
657 ok($frame, 'client body timeout'); | 657 ok($frame, 'client body timeout'); |
658 is($frame->{code}, 1, 'client body timeout - protocol error'); | 658 is($frame->{code}, 1, 'client body timeout - protocol error'); |
659 | 659 |
660 } | 660 } |
661 | 661 |
662 h2_ping($sess, 'SEE-THIS'); | 662 $s->h2_ping('SEE-THIS'); |
663 $frames = h2_read($sess, all => [{ type => 'PING' }]); | 663 $frames = $s->read(all => [{ type => 'PING' }]); |
664 | 664 |
665 ($frame) = grep { $_->{type} eq "PING" && $_->{flags} & 0x1 } @$frames; | 665 ($frame) = grep { $_->{type} eq "PING" && $_->{flags} & 0x1 } @$frames; |
666 ok($frame, 'client body timeout - PING'); | 666 ok($frame, 'client body timeout - PING'); |
667 | 667 |
668 | 668 |
669 # proxied request with logging pristine request header field (e.g., referer) | 669 # proxied request with logging pristine request header field (e.g., referer) |
670 | 670 |
671 $sess = new_session(); | 671 $s = Test::Nginx::HTTP2->new(); |
672 $sid = new_stream($sess, { headers => [ | 672 $sid = $s->new_stream({ headers => [ |
673 { name => ':method', value => 'GET' }, | 673 { name => ':method', value => 'GET' }, |
674 { name => ':scheme', value => 'http' }, | 674 { name => ':scheme', value => 'http' }, |
675 { name => ':path', value => '/proxy2/' }, | 675 { name => ':path', value => '/proxy2/' }, |
676 { name => ':authority', value => 'localhost' }, | 676 { name => ':authority', value => 'localhost' }, |
677 { name => 'referer', value => 'foo' }]}); | 677 { name => 'referer', value => 'foo' }]}); |
678 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 678 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
679 | 679 |
680 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 680 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
681 is($frame->{headers}->{':status'}, 200, 'proxy with logging request headers'); | 681 is($frame->{headers}->{':status'}, 200, 'proxy with logging request headers'); |
682 | 682 |
683 $sid = new_stream($sess); | 683 $sid = $s->new_stream(); |
684 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 684 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
685 | 685 |
686 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 686 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
687 ok($frame->{headers}, 'proxy with logging request headers - next'); | 687 ok($frame->{headers}, 'proxy with logging request headers - next'); |
688 | 688 |
689 # initial window size, client side | 689 # initial window size, client side |
691 # 6.9.2. Initial Flow-Control Window Size | 691 # 6.9.2. Initial Flow-Control Window Size |
692 # When an HTTP/2 connection is first established, new streams are | 692 # When an HTTP/2 connection is first established, new streams are |
693 # created with an initial flow-control window size of 65,535 octets. | 693 # created with an initial flow-control window size of 65,535 octets. |
694 # The connection flow-control window is also 65,535 octets. | 694 # The connection flow-control window is also 65,535 octets. |
695 | 695 |
696 $sess = new_session(); | 696 $s = Test::Nginx::HTTP2->new(); |
697 $sid = new_stream($sess, { path => '/t1.html' }); | 697 $sid = $s->new_stream({ path => '/t1.html' }); |
698 $frames = h2_read($sess, all => [{ sid => $sid, length => 2**16 - 1 }]); | 698 $frames = $s->read(all => [{ sid => $sid, length => 2**16 - 1 }]); |
699 | 699 |
700 # with the default http2_chunk_size, data is divided into 8 data frames | 700 # with the default http2_chunk_size, data is divided into 8 data frames |
701 | 701 |
702 @data = grep { $_->{type} eq "DATA" } @$frames; | 702 @data = grep { $_->{type} eq "DATA" } @$frames; |
703 my $lengths = join ' ', map { $_->{length} } @data; | 703 my $lengths = join ' ', map { $_->{length} } @data; |
704 is($lengths, '8192 8192 8192 8192 8192 8192 8192 8191', | 704 is($lengths, '8192 8192 8192 8192 8192 8192 8192 8191', |
705 'iws - stream blocked on initial window size'); | 705 'iws - stream blocked on initial window size'); |
706 | 706 |
707 h2_ping($sess, 'SEE-THIS'); | 707 $s->h2_ping('SEE-THIS'); |
708 $frames = h2_read($sess, all => [{ type => 'PING' }]); | 708 $frames = $s->read(all => [{ type => 'PING' }]); |
709 | 709 |
710 ($frame) = grep { $_->{type} eq "PING" && $_->{flags} & 0x1 } @$frames; | 710 ($frame) = grep { $_->{type} eq "PING" && $_->{flags} & 0x1 } @$frames; |
711 ok($frame, 'iws - PING not blocked'); | 711 ok($frame, 'iws - PING not blocked'); |
712 | 712 |
713 h2_window($sess, 2**16, $sid); | 713 $s->h2_window(2**16, $sid); |
714 $frames = h2_read($sess, wait => 0.2); | 714 $frames = $s->read(wait => 0.2); |
715 is(@$frames, 0, 'iws - updated stream window'); | 715 is(@$frames, 0, 'iws - updated stream window'); |
716 | 716 |
717 h2_window($sess, 2**16); | 717 $s->h2_window(2**16); |
718 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 718 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
719 | 719 |
720 @data = grep { $_->{type} eq "DATA" } @$frames; | 720 @data = grep { $_->{type} eq "DATA" } @$frames; |
721 my $sum = eval join '+', map { $_->{length} } @data; | 721 my $sum = eval join '+', map { $_->{length} } @data; |
722 is($sum, 81, 'iws - updated connection window'); | 722 is($sum, 81, 'iws - updated connection window'); |
723 | 723 |
727 # Both endpoints can adjust the initial window size for new streams by | 727 # Both endpoints can adjust the initial window size for new streams by |
728 # including a value for SETTINGS_INITIAL_WINDOW_SIZE in the SETTINGS | 728 # including a value for SETTINGS_INITIAL_WINDOW_SIZE in the SETTINGS |
729 # frame that forms part of the connection preface. The connection | 729 # frame that forms part of the connection preface. The connection |
730 # flow-control window can only be changed using WINDOW_UPDATE frames. | 730 # flow-control window can only be changed using WINDOW_UPDATE frames. |
731 | 731 |
732 $sess = new_session(); | 732 $s = Test::Nginx::HTTP2->new(); |
733 h2_settings($sess, 0, 0x4 => 2**17); | 733 $s->h2_settings(0, 0x4 => 2**17); |
734 h2_window($sess, 2**17); | 734 $s->h2_window(2**17); |
735 | 735 |
736 $sid = new_stream($sess, { path => '/t1.html' }); | 736 $sid = $s->new_stream({ path => '/t1.html' }); |
737 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 737 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
738 | 738 |
739 @data = grep { $_->{type} eq "DATA" } @$frames; | 739 @data = grep { $_->{type} eq "DATA" } @$frames; |
740 $sum = eval join '+', map { $_->{length} } @data; | 740 $sum = eval join '+', map { $_->{length} } @data; |
741 is($sum, 2**16 + 80, 'iws - increased'); | 741 is($sum, 2**16 + 80, 'iws - increased'); |
742 | 742 |
747 # space in a flow-control window to become negative. A sender MUST | 747 # space in a flow-control window to become negative. A sender MUST |
748 # track the negative flow-control window and MUST NOT send new flow- | 748 # track the negative flow-control window and MUST NOT send new flow- |
749 # controlled frames until it receives WINDOW_UPDATE frames that cause | 749 # controlled frames until it receives WINDOW_UPDATE frames that cause |
750 # the flow-control window to become positive. | 750 # the flow-control window to become positive. |
751 | 751 |
752 $sess = new_session(); | 752 $s = Test::Nginx::HTTP2->new(); |
753 $sid = new_stream($sess, { path => '/t1.html' }); | 753 $sid = $s->new_stream({ path => '/t1.html' }); |
754 h2_read($sess, all => [{ sid => $sid, length => 2**16 - 1 }]); | 754 $s->read(all => [{ sid => $sid, length => 2**16 - 1 }]); |
755 | 755 |
756 h2_window($sess, 1); | 756 $s->h2_window(1); |
757 h2_settings($sess, 0, 0x4 => 42); | 757 $s->h2_settings(0, 0x4 => 42); |
758 h2_window($sess, 1024, $sid); | 758 $s->h2_window(1024, $sid); |
759 | 759 |
760 $frames = h2_read($sess, all => [{ type => 'SETTINGS' }]); | 760 $frames = $s->read(all => [{ type => 'SETTINGS' }]); |
761 | 761 |
762 ($frame) = grep { $_->{type} eq 'SETTINGS' } @$frames; | 762 ($frame) = grep { $_->{type} eq 'SETTINGS' } @$frames; |
763 ok($frame, 'negative window - SETTINGS frame ack'); | 763 ok($frame, 'negative window - SETTINGS frame ack'); |
764 is($frame->{flags}, 1, 'negative window - SETTINGS flags ack'); | 764 is($frame->{flags}, 1, 'negative window - SETTINGS flags ack'); |
765 | 765 |
766 ($frame) = grep { $_->{type} ne 'SETTINGS' } @$frames; | 766 ($frame) = grep { $_->{type} ne 'SETTINGS' } @$frames; |
767 is($frame, undef, 'negative window - no data'); | 767 is($frame, undef, 'negative window - no data'); |
768 | 768 |
769 # predefined window size, minus new iws settings, minus window update | 769 # predefined window size, minus new iws settings, minus window update |
770 | 770 |
771 h2_window($sess, 2**16 - 1 - 42 - 1024, $sid); | 771 $s->h2_window(2**16 - 1 - 42 - 1024, $sid); |
772 | 772 |
773 $frames = h2_read($sess, wait => 0.2); | 773 $frames = $s->read(wait => 0.2); |
774 is(@$frames, 0, 'zero window - no data'); | 774 is(@$frames, 0, 'zero window - no data'); |
775 | 775 |
776 h2_window($sess, 1, $sid); | 776 $s->h2_window(1, $sid); |
777 | 777 |
778 $frames = h2_read($sess, all => [{ sid => $sid, length => 1 }]); | 778 $frames = $s->read(all => [{ sid => $sid, length => 1 }]); |
779 is(@$frames, 1, 'positive window'); | 779 is(@$frames, 1, 'positive window'); |
780 | 780 |
781 SKIP: { | 781 SKIP: { |
782 skip 'failed connection', 2 unless @$frames; | 782 skip 'failed connection', 2 unless @$frames; |
783 | 783 |
786 | 786 |
787 } | 787 } |
788 | 788 |
789 # ask write handler in sending large response | 789 # ask write handler in sending large response |
790 | 790 |
791 $sid = new_stream($sess, { path => '/tbig.html' }); | 791 $sid = $s->new_stream({ path => '/tbig.html' }); |
792 | 792 |
793 h2_window($sess, 2**30, $sid); | 793 $s->h2_window(2**30, $sid); |
794 h2_window($sess, 2**30); | 794 $s->h2_window(2**30); |
795 | 795 |
796 sleep 1; | 796 sleep 1; |
797 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 797 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
798 | 798 |
799 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 799 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
800 is($frame->{headers}->{':status'}, 200, 'large response - HEADERS'); | 800 is($frame->{headers}->{':status'}, 200, 'large response - HEADERS'); |
801 | 801 |
802 @data = grep { $_->{type} eq "DATA" } @$frames; | 802 @data = grep { $_->{type} eq "DATA" } @$frames; |
815 skip 'tolerant operating system', 1 unless $^O eq 'darwin' or $^O eq 'netbsd'; | 815 skip 'tolerant operating system', 1 unless $^O eq 'darwin' or $^O eq 'netbsd'; |
816 | 816 |
817 TODO: { | 817 TODO: { |
818 local $TODO = 'not yet'; | 818 local $TODO = 'not yet'; |
819 | 819 |
820 $sid = new_stream($sess); | 820 $sid = $s->new_stream(); |
821 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 821 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
822 | 822 |
823 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 823 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
824 is($frame->{headers}->{':status'}, 200, 'new stream after large response'); | 824 is($frame->{headers}->{':status'}, 200, 'new stream after large response'); |
825 | 825 |
826 } | 826 } |
827 | 827 |
828 } | 828 } |
829 | 829 |
830 # write event send timeout | 830 # write event send timeout |
831 | 831 |
832 $sess = new_session(8091); | 832 $s = Test::Nginx::HTTP2->new(8091); |
833 $sid = new_stream($sess, { path => '/tbig.html' }); | 833 $sid = $s->new_stream({ path => '/tbig.html' }); |
834 h2_window($sess, 2**30, $sid); | 834 $s->h2_window(2**30, $sid); |
835 h2_window($sess, 2**30); | 835 $s->h2_window(2**30); |
836 | 836 |
837 select undef, undef, undef, 2.1; | 837 select undef, undef, undef, 2.1; |
838 | 838 |
839 h2_ping($sess, 'SEE-THIS'); | 839 $s->h2_ping('SEE-THIS'); |
840 | 840 |
841 $frames = h2_read($sess, all => [{ type => 'PING' }]); | 841 $frames = $s->read(all => [{ type => 'PING' }]); |
842 ok(!grep ({ $_->{type} eq "PING" } @$frames), 'large response - send timeout'); | 842 ok(!grep ({ $_->{type} eq "PING" } @$frames), 'large response - send timeout'); |
843 | 843 |
844 # stream with large response queued on write - RST_STREAM handling | 844 # stream with large response queued on write - RST_STREAM handling |
845 | 845 |
846 $sess = new_session(); | 846 $s = Test::Nginx::HTTP2->new(); |
847 $sid = new_stream($sess, { path => '/tbig.html' }); | 847 $sid = $s->new_stream({ path => '/tbig.html' }); |
848 | 848 |
849 h2_window($sess, 2**30, $sid); | 849 $s->h2_window(2**30, $sid); |
850 h2_window($sess, 2**30); | 850 $s->h2_window(2**30); |
851 | 851 |
852 select undef, undef, undef, 0.4; | 852 select undef, undef, undef, 0.4; |
853 | 853 |
854 h2_rst($sess, $sid, 8); | 854 $s->h2_rst($sid, 8); |
855 h2_read($sess, all => [{ sid => $sid, fin => 1 }], wait => 0.2); | 855 $s->read(all => [{ sid => $sid, fin => 1 }], wait => 0.2); |
856 | 856 |
857 $sid = new_stream($sess); | 857 $sid = $s->new_stream(); |
858 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 858 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
859 | 859 |
860 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; | 860 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; |
861 is($frame->{sid}, 3, 'large response - queued with RST_STREAM'); | 861 is($frame->{sid}, 3, 'large response - queued with RST_STREAM'); |
862 | 862 |
863 # SETTINGS_MAX_FRAME_SIZE | 863 # SETTINGS_MAX_FRAME_SIZE |
864 | 864 |
865 $sess = new_session(); | 865 $s = Test::Nginx::HTTP2->new(); |
866 $sid = new_stream($sess, { path => '/frame_size' }); | 866 $sid = $s->new_stream({ path => '/frame_size' }); |
867 h2_window($sess, 2**18, 1); | 867 $s->h2_window(2**18, 1); |
868 h2_window($sess, 2**18); | 868 $s->h2_window(2**18); |
869 | 869 |
870 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 870 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
871 @data = grep { $_->{type} eq "DATA" } @$frames; | 871 @data = grep { $_->{type} eq "DATA" } @$frames; |
872 is($data[0]->{length}, 2**14, 'max frame size - default'); | 872 is($data[0]->{length}, 2**14, 'max frame size - default'); |
873 | 873 |
874 $sess = new_session(); | 874 $s = Test::Nginx::HTTP2->new(); |
875 h2_settings($sess, 0, 0x5 => 2**15); | 875 $s->h2_settings(0, 0x5 => 2**15); |
876 $sid = new_stream($sess, { path => '/frame_size' }); | 876 $sid = $s->new_stream({ path => '/frame_size' }); |
877 h2_window($sess, 2**18, 1); | 877 $s->h2_window(2**18, 1); |
878 h2_window($sess, 2**18); | 878 $s->h2_window(2**18); |
879 | 879 |
880 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 880 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
881 @data = grep { $_->{type} eq "DATA" } @$frames; | 881 @data = grep { $_->{type} eq "DATA" } @$frames; |
882 is($data[0]->{length}, 2**15, 'max frame size - custom'); | 882 is($data[0]->{length}, 2**15, 'max frame size - custom'); |
883 | 883 |
884 # stream multiplexing + WINDOW_UPDATE | 884 # stream multiplexing + WINDOW_UPDATE |
885 | 885 |
886 $sess = new_session(); | 886 $s = Test::Nginx::HTTP2->new(); |
887 $sid = new_stream($sess, { path => '/t1.html' }); | 887 $sid = $s->new_stream({ path => '/t1.html' }); |
888 $frames = h2_read($sess, all => [{ sid => $sid, length => 2**16 - 1 }]); | 888 $frames = $s->read(all => [{ sid => $sid, length => 2**16 - 1 }]); |
889 | 889 |
890 @data = grep { $_->{type} eq "DATA" } @$frames; | 890 @data = grep { $_->{type} eq "DATA" } @$frames; |
891 $sum = eval join '+', map { $_->{length} } @data; | 891 $sum = eval join '+', map { $_->{length} } @data; |
892 is($sum, 2**16 - 1, 'multiple - stream1 data'); | 892 is($sum, 2**16 - 1, 'multiple - stream1 data'); |
893 | 893 |
894 my $sid2 = new_stream($sess, { path => '/t1.html' }); | 894 my $sid2 = $s->new_stream({ path => '/t1.html' }); |
895 $frames = h2_read($sess, all => [{ sid => $sid2, fin => 0x4 }]); | 895 $frames = $s->read(all => [{ sid => $sid2, fin => 0x4 }]); |
896 | 896 |
897 @data = grep { $_->{type} eq "DATA" } @$frames; | 897 @data = grep { $_->{type} eq "DATA" } @$frames; |
898 is(@data, 0, 'multiple - stream2 no data'); | 898 is(@data, 0, 'multiple - stream2 no data'); |
899 | 899 |
900 h2_window($sess, 2**17, $sid); | 900 $s->h2_window(2**17, $sid); |
901 h2_window($sess, 2**17, $sid2); | 901 $s->h2_window(2**17, $sid2); |
902 h2_window($sess, 2**17); | 902 $s->h2_window(2**17); |
903 | 903 |
904 $frames = h2_read($sess, all => [ | 904 $frames = $s->read(all => [ |
905 { sid => $sid, fin => 1 }, | 905 { sid => $sid, fin => 1 }, |
906 { sid => $sid2, fin => 1 } | 906 { sid => $sid2, fin => 1 } |
907 ]); | 907 ]); |
908 | 908 |
909 @data = grep { $_->{type} eq "DATA" && $_->{sid} == $sid } @$frames; | 909 @data = grep { $_->{type} eq "DATA" && $_->{sid} == $sid } @$frames; |
914 $sum = eval join '+', map { $_->{length} } @data; | 914 $sum = eval join '+', map { $_->{length} } @data; |
915 is($sum, 2**16 + 80, 'multiple - stream2 full data'); | 915 is($sum, 2**16 + 80, 'multiple - stream2 full data'); |
916 | 916 |
917 # http2_max_concurrent_streams | 917 # http2_max_concurrent_streams |
918 | 918 |
919 $sess = new_session(8086, pure => 1); | 919 $s = Test::Nginx::HTTP2->new(8086, pure => 1); |
920 $frames = h2_read($sess, all => [{ type => 'SETTINGS' }]); | 920 $frames = $s->read(all => [{ type => 'SETTINGS' }]); |
921 | 921 |
922 ($frame) = grep { $_->{type} eq 'SETTINGS' } @$frames; | 922 ($frame) = grep { $_->{type} eq 'SETTINGS' } @$frames; |
923 is($frame->{3}, 1, 'http2_max_concurrent_streams SETTINGS'); | 923 is($frame->{3}, 1, 'http2_max_concurrent_streams SETTINGS'); |
924 | 924 |
925 h2_window($sess, 2**18); | 925 $s->h2_window(2**18); |
926 | 926 |
927 $sid = new_stream($sess, { path => '/t1.html' }); | 927 $sid = $s->new_stream({ path => '/t1.html' }); |
928 $frames = h2_read($sess, all => [{ sid => $sid, length => 2 ** 16 - 1 }]); | 928 $frames = $s->read(all => [{ sid => $sid, length => 2 ** 16 - 1 }]); |
929 | 929 |
930 ($frame) = grep { $_->{type} eq "HEADERS" && $_->{sid} == $sid } @$frames; | 930 ($frame) = grep { $_->{type} eq "HEADERS" && $_->{sid} == $sid } @$frames; |
931 is($frame->{headers}->{':status'}, 200, 'http2_max_concurrent_streams'); | 931 is($frame->{headers}->{':status'}, 200, 'http2_max_concurrent_streams'); |
932 | 932 |
933 $sid2 = new_stream($sess, { path => '/t1.html' }); | 933 $sid2 = $s->new_stream({ path => '/t1.html' }); |
934 $frames = h2_read($sess, all => [{ type => 'RST_STREAM' }]); | 934 $frames = $s->read(all => [{ type => 'RST_STREAM' }]); |
935 | 935 |
936 ($frame) = grep { $_->{type} eq "HEADERS" && $_->{sid} == $sid2 } @$frames; | 936 ($frame) = grep { $_->{type} eq "HEADERS" && $_->{sid} == $sid2 } @$frames; |
937 isnt($frame->{headers}->{':status'}, 200, 'http2_max_concurrent_streams 2'); | 937 isnt($frame->{headers}->{':status'}, 200, 'http2_max_concurrent_streams 2'); |
938 | 938 |
939 ($frame) = grep { $_->{type} eq "RST_STREAM" && $_->{sid} == $sid2 } @$frames; | 939 ($frame) = grep { $_->{type} eq "RST_STREAM" && $_->{sid} == $sid2 } @$frames; |
942 is($frame->{flags}, 0, 'http2_max_concurrent_streams RST_STREAM flags'); | 942 is($frame->{flags}, 0, 'http2_max_concurrent_streams RST_STREAM flags'); |
943 is($frame->{code}, 7, 'http2_max_concurrent_streams RST_STREAM code'); | 943 is($frame->{code}, 7, 'http2_max_concurrent_streams RST_STREAM code'); |
944 | 944 |
945 # properly skip header field that's not/never indexed from discarded streams | 945 # properly skip header field that's not/never indexed from discarded streams |
946 | 946 |
947 $sid2 = new_stream($sess, { headers => [ | 947 $sid2 = $s->new_stream({ headers => [ |
948 { name => ':method', value => 'GET' }, | 948 { name => ':method', value => 'GET' }, |
949 { name => ':scheme', value => 'http' }, | 949 { name => ':scheme', value => 'http' }, |
950 { name => ':path', value => '/', mode => 6 }, | 950 { name => ':path', value => '/', mode => 6 }, |
951 { name => ':authority', value => 'localhost' }, | 951 { name => ':authority', value => 'localhost' }, |
952 { name => 'x-foo', value => 'Foo', mode => 2 }]}); | 952 { name => 'x-foo', value => 'Foo', mode => 2 }]}); |
953 $frames = h2_read($sess, all => [{ type => 'RST_STREAM' }]); | 953 $frames = $s->read(all => [{ type => 'RST_STREAM' }]); |
954 | 954 |
955 # also if split across writes | 955 # also if split across writes |
956 | 956 |
957 $sid2 = new_stream($sess, { split => [ 22 ], headers => [ | 957 $sid2 = $s->new_stream({ split => [ 22 ], headers => [ |
958 { name => ':method', value => 'GET' }, | 958 { name => ':method', value => 'GET' }, |
959 { name => ':scheme', value => 'http' }, | 959 { name => ':scheme', value => 'http' }, |
960 { name => ':path', value => '/', mode => 6 }, | 960 { name => ':path', value => '/', mode => 6 }, |
961 { name => ':authority', value => 'localhost' }, | 961 { name => ':authority', value => 'localhost' }, |
962 { name => 'x-bar', value => 'Bar', mode => 2 }]}); | 962 { name => 'x-bar', value => 'Bar', mode => 2 }]}); |
963 $frames = h2_read($sess, all => [{ type => 'RST_STREAM' }]); | 963 $frames = $s->read(all => [{ type => 'RST_STREAM' }]); |
964 | 964 |
965 # also if split across frames | 965 # also if split across frames |
966 | 966 |
967 $sid2 = new_stream($sess, { continuation => [ 17 ], headers => [ | 967 $sid2 = $s->new_stream({ continuation => [ 17 ], headers => [ |
968 { name => ':method', value => 'GET' }, | 968 { name => ':method', value => 'GET' }, |
969 { name => ':scheme', value => 'http' }, | 969 { name => ':scheme', value => 'http' }, |
970 { name => ':path', value => '/', mode => 6 }, | 970 { name => ':path', value => '/', mode => 6 }, |
971 { name => ':authority', value => 'localhost' }, | 971 { name => ':authority', value => 'localhost' }, |
972 { name => 'x-baz', value => 'Baz', mode => 2 }]}); | 972 { name => 'x-baz', value => 'Baz', mode => 2 }]}); |
973 $frames = h2_read($sess, all => [{ type => 'RST_STREAM' }]); | 973 $frames = $s->read(all => [{ type => 'RST_STREAM' }]); |
974 | 974 |
975 h2_window($sess, 2**16, $sid); | 975 $s->h2_window(2**16, $sid); |
976 h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 976 $s->read(all => [{ sid => $sid, fin => 1 }]); |
977 | 977 |
978 $sid = new_stream($sess, { headers => [ | 978 $sid = $s->new_stream({ headers => [ |
979 { name => ':method', value => 'GET' }, | 979 { name => ':method', value => 'GET' }, |
980 { name => ':scheme', value => 'http' }, | 980 { name => ':scheme', value => 'http' }, |
981 { name => ':path', value => '/t2.html' }, | 981 { name => ':path', value => '/t2.html' }, |
982 { name => ':authority', value => 'localhost' }, | 982 { name => ':authority', value => 'localhost' }, |
983 # make sure that discarded streams updated dynamic table | 983 # make sure that discarded streams updated dynamic table |
984 { name => 'x-foo', value => 'Foo', mode => 0 }, | 984 { name => 'x-foo', value => 'Foo', mode => 0 }, |
985 { name => 'x-bar', value => 'Bar', mode => 0 }, | 985 { name => 'x-bar', value => 'Bar', mode => 0 }, |
986 { name => 'x-baz', value => 'Baz', mode => 0 }]}); | 986 { name => 'x-baz', value => 'Baz', mode => 0 }]}); |
987 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 987 $frames = $s->read(all => [{ sid => $sid, fin => 1 }]); |
988 | 988 |
989 ($frame) = grep { $_->{type} eq "HEADERS" && $_->{sid} == $sid } @$frames; | 989 ($frame) = grep { $_->{type} eq "HEADERS" && $_->{sid} == $sid } @$frames; |
990 is($frame->{headers}->{':status'}, 200, 'http2_max_concurrent_streams 3'); | 990 is($frame->{headers}->{':status'}, 200, 'http2_max_concurrent_streams 3'); |
991 | 991 |
992 | 992 |
993 # some invalid cases below | 993 # some invalid cases below |
994 | 994 |
995 # invalid connection preface | 995 # invalid connection preface |
996 | 996 |
997 $sess = new_session(8080, preface => 'x' x 16, pure => 1); | 997 $s = Test::Nginx::HTTP2->new(8080, preface => 'x' x 16, pure => 1); |
998 $frames = h2_read($sess, all => [{ type => 'GOAWAY' }]); | 998 $frames = $s->read(all => [{ type => 'GOAWAY' }]); |
999 | 999 |
1000 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; | 1000 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; |
1001 ok($frame, 'invalid preface - GOAWAY frame'); | 1001 ok($frame, 'invalid preface - GOAWAY frame'); |
1002 is($frame->{code}, 1, 'invalid preface - error code'); | 1002 is($frame->{code}, 1, 'invalid preface - error code'); |
1003 | 1003 |
1004 $sess = new_session(8080, preface => 'PRI * HTTP/2.0' . CRLF . CRLF . 'x' x 8, | 1004 my $preface = 'PRI * HTTP/2.0' . CRLF . CRLF . 'x' x 8; |
1005 pure => 1); | 1005 $s = Test::Nginx::HTTP2->new(8080, preface => $preface, pure => 1); |
1006 $frames = h2_read($sess, all => [{ type => 'GOAWAY' }]); | 1006 $frames = $s->read(all => [{ type => 'GOAWAY' }]); |
1007 | 1007 |
1008 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; | 1008 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; |
1009 ok($frame, 'invalid preface 2 - GOAWAY frame'); | 1009 ok($frame, 'invalid preface 2 - GOAWAY frame'); |
1010 is($frame->{code}, 1, 'invalid preface 2 - error code'); | 1010 is($frame->{code}, 1, 'invalid preface 2 - error code'); |
1011 | 1011 |
1012 # GOAWAY on SYN_STREAM with even StreamID | 1012 # GOAWAY on SYN_STREAM with even StreamID |
1013 | 1013 |
1014 $sess = new_session(); | 1014 $s = Test::Nginx::HTTP2->new(); |
1015 new_stream($sess, { path => '/' }, 2); | 1015 $s->new_stream({ path => '/' }, 2); |
1016 $frames = h2_read($sess, all => [{ type => 'GOAWAY' }]); | 1016 $frames = $s->read(all => [{ type => 'GOAWAY' }]); |
1017 | 1017 |
1018 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; | 1018 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; |
1019 ok($frame, 'even stream - GOAWAY frame'); | 1019 ok($frame, 'even stream - GOAWAY frame'); |
1020 is($frame->{code}, 1, 'even stream - error code'); | 1020 is($frame->{code}, 1, 'even stream - error code'); |
1021 is($frame->{last_sid}, 0, 'even stream - last stream'); | 1021 is($frame->{last_sid}, 0, 'even stream - last stream'); |
1024 | 1024 |
1025 # 5.1.1. Stream Identifiers | 1025 # 5.1.1. Stream Identifiers |
1026 # The first use of a new stream identifier implicitly closes all | 1026 # The first use of a new stream identifier implicitly closes all |
1027 # streams in the "idle" state <..> with a lower-valued stream identifier. | 1027 # streams in the "idle" state <..> with a lower-valued stream identifier. |
1028 | 1028 |
1029 $sess = new_session(); | 1029 $s = Test::Nginx::HTTP2->new(); |
1030 $sid = new_stream($sess, { path => '/' }, 3); | 1030 $sid = $s->new_stream({ path => '/' }, 3); |
1031 h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 1031 $s->read(all => [{ sid => $sid, fin => 1 }]); |
1032 | 1032 |
1033 $sid2 = new_stream($sess, { path => '/' }, 1); | 1033 $sid2 = $s->new_stream({ path => '/' }, 1); |
1034 $frames = h2_read($sess, all => [{ type => 'GOAWAY' }]); | 1034 $frames = $s->read(all => [{ type => 'GOAWAY' }]); |
1035 | 1035 |
1036 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; | 1036 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; |
1037 ok($frame, 'backward stream - GOAWAY frame'); | 1037 ok($frame, 'backward stream - GOAWAY frame'); |
1038 is($frame->{code}, 1, 'backward stream - error code'); | 1038 is($frame->{code}, 1, 'backward stream - error code'); |
1039 is($frame->{last_sid}, $sid, 'backward stream - last stream'); | 1039 is($frame->{last_sid}, $sid, 'backward stream - last stream'); |
1040 | 1040 |
1041 # GOAWAY on the second SYN_STREAM with same StreamID | 1041 # GOAWAY on the second SYN_STREAM with same StreamID |
1042 | 1042 |
1043 $sess = new_session(); | 1043 $s = Test::Nginx::HTTP2->new(); |
1044 $sid = new_stream($sess, { path => '/' }); | 1044 $sid = $s->new_stream({ path => '/' }); |
1045 h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 1045 $s->read(all => [{ sid => $sid, fin => 1 }]); |
1046 | 1046 |
1047 $sid2 = new_stream($sess, { path => '/' }, $sid); | 1047 $sid2 = $s->new_stream({ path => '/' }, $sid); |
1048 $frames = h2_read($sess, all => [{ type => 'GOAWAY' }]); | 1048 $frames = $s->read(all => [{ type => 'GOAWAY' }]); |
1049 | 1049 |
1050 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; | 1050 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; |
1051 ok($frame, 'dup stream - GOAWAY frame'); | 1051 ok($frame, 'dup stream - GOAWAY frame'); |
1052 is($frame->{code}, 1, 'dup stream - error code'); | 1052 is($frame->{code}, 1, 'dup stream - error code'); |
1053 is($frame->{last_sid}, $sid, 'dup stream - last stream'); | 1053 is($frame->{last_sid}, $sid, 'dup stream - last stream'); |
1054 | 1054 |
1055 # aborted stream with zero HEADERS payload followed by client connection close | 1055 # aborted stream with zero HEADERS payload followed by client connection close |
1056 | 1056 |
1057 new_stream(new_session(), { split => [ 9 ], abort => 1 }); | 1057 Test::Nginx::HTTP2->new()->new_stream({ split => [ 9 ], abort => 1 }); |
1058 | 1058 |
1059 # unknown frame type | 1059 # unknown frame type |
1060 | 1060 |
1061 $sess = new_session(); | 1061 $s = Test::Nginx::HTTP2->new(); |
1062 h2_unknown($sess, 'payload'); | 1062 $s->h2_unknown('payload'); |
1063 h2_ping($sess, 'SEE-THIS'); | 1063 $s->h2_ping('SEE-THIS'); |
1064 $frames = h2_read($sess, all => [{ type => 'PING' }]); | 1064 $frames = $s->read(all => [{ type => 'PING' }]); |
1065 | 1065 |
1066 ($frame) = grep { $_->{type} eq "PING" } @$frames; | 1066 ($frame) = grep { $_->{type} eq "PING" } @$frames; |
1067 is($frame->{value}, 'SEE-THIS', 'unknown frame type'); | 1067 is($frame->{value}, 'SEE-THIS', 'unknown frame type'); |
1068 | 1068 |
1069 # GOAWAY - force closing a connection by server | 1069 # GOAWAY - force closing a connection by server |
1070 | 1070 |
1071 $sid = new_stream($sess); | 1071 $sid = $s->new_stream(); |
1072 h2_read($sess, all => [{ sid => $sid, fin => 1 }]); | 1072 $s->read(all => [{ sid => $sid, fin => 1 }]); |
1073 | 1073 |
1074 # graceful shutdown with stream waiting on HEADERS payload | 1074 # graceful shutdown with stream waiting on HEADERS payload |
1075 | 1075 |
1076 my $grace = new_session(8089); | 1076 my $grace = Test::Nginx::HTTP2->new(8089); |
1077 new_stream($grace, { split => [ 9 ], abort => 1 }); | 1077 $grace->new_stream({ split => [ 9 ], abort => 1 }); |
1078 | 1078 |
1079 # graceful shutdown with stream waiting on WINDOW_UPDATE | 1079 # graceful shutdown with stream waiting on WINDOW_UPDATE |
1080 | 1080 |
1081 my $grace2 = new_session(8089); | 1081 my $grace2 = Test::Nginx::HTTP2->new(8089); |
1082 $sid = new_stream($grace2, { path => '/t1.html' }); | 1082 $sid = $grace2->new_stream({ path => '/t1.html' }); |
1083 h2_read($grace2, all => [{ sid => $sid, length => 2**16 - 1 }]); | 1083 $grace2->read(all => [{ sid => $sid, length => 2**16 - 1 }]); |
1084 | 1084 |
1085 # graceful shutdown waiting on incomplete request body DATA frames | 1085 # graceful shutdown waiting on incomplete request body DATA frames |
1086 | 1086 |
1087 my $grace3 = new_session(8090); | 1087 my $grace3 = Test::Nginx::HTTP2->new(8090); |
1088 $sid = new_stream($grace3, { path => '/proxy2/t2.html', body_more => 1 }); | 1088 $sid = $grace3->new_stream({ path => '/proxy2/t2.html', body_more => 1 }); |
1089 h2_body($grace3, 'TEST', { body_more => 1 }); | 1089 $grace3->h2_body('TEST', { body_more => 1 }); |
1090 | 1090 |
1091 # partial request body data frame with connection close after body timeout | 1091 # partial request body data frame with connection close after body timeout |
1092 | 1092 |
1093 my $grace4 = new_session(8093); | 1093 my $grace4 = Test::Nginx::HTTP2->new(8093); |
1094 $sid = new_stream($grace4, { path => '/proxy/t2.html', body_more => 1 }); | 1094 $sid = $grace4->new_stream({ path => '/proxy/t2.html', body_more => 1 }); |
1095 h2_body($grace4, 'TEST', { split => [ 12 ], abort => 1 }); | 1095 $grace4->h2_body('TEST', { split => [ 12 ], abort => 1 }); |
1096 | 1096 |
1097 select undef, undef, undef, 1.1; | 1097 select undef, undef, undef, 1.1; |
1098 undef $grace4; | 1098 undef $grace4; |
1099 | 1099 |
1100 $t->stop(); | 1100 $t->stop(); |
1101 | 1101 |
1102 $frames = h2_read($sess, all => [{ type => 'GOAWAY' }]); | 1102 $frames = $s->read(all => [{ type => 'GOAWAY' }]); |
1103 | 1103 |
1104 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; | 1104 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; |
1105 ok($frame, 'GOAWAY on connection close'); | 1105 ok($frame, 'GOAWAY on connection close'); |
1106 | 1106 |
1107 ############################################################################### | 1107 ############################################################################### |