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 ###############################################################################