comparison h2.t @ 844:3a334b20208e

Tests: made HTTP/2 tests respect request body flow-control windows.
author Sergey Kandaurov <pluknet@nginx.com>
date Thu, 04 Feb 2016 18:05:40 +0300
parents 5e7845d36e54
children e31f1b5bb569
comparison
equal deleted inserted replaced
843:5e7845d36e54 844:3a334b20208e
336 336
337 } 337 }
338 338
339 # SETTINGS 339 # SETTINGS
340 340
341 my $sess = new_session(); 341 my $sess = new_session(8080, pure => 1);
342 my $frames = h2_read($sess, all => [ 342 my $frames = h2_read($sess, all => [
343 { type => 'WINDOW_UPDATE' }, 343 { type => 'WINDOW_UPDATE' },
344 { type => 'SETTINGS'} 344 { type => 'SETTINGS'}
345 ]); 345 ]);
346 346
1412 'proxy cache HEAD buffering off - no body'); 1412 'proxy cache HEAD buffering off - no body');
1413 1413
1414 # request body (uses proxied response) 1414 # request body (uses proxied response)
1415 1415
1416 $sess = new_session(); 1416 $sess = new_session();
1417 $sid = new_stream($sess, { path => '/proxy2/t2.html', body => 'TEST' }); 1417 $sid = new_stream($sess, { path => '/proxy2/t2.html', body_more => 1 });
1418 h2_body($sess, 'TEST');
1418 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1419 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1419 1420
1420 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1421 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1421 is($frame->{headers}->{'x-body'}, 'TEST', 'request body'); 1422 is($frame->{headers}->{'x-body'}, 'TEST', 'request body');
1422 1423
1423 # request body with padding (uses proxied response) 1424 # request body with padding (uses proxied response)
1424 1425
1425 $sess = new_session(); 1426 $sess = new_session();
1426 $sid = new_stream($sess, 1427 $sid = new_stream($sess, { path => '/proxy2/t2.html', body_more => 1 });
1427 { path => '/proxy2/t2.html', body => 'TEST', body_padding => 42 }); 1428 h2_body($sess, 'TEST', { body_padding => 42 });
1428 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1429 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1429 1430
1430 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1431 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1431 is($frame->{headers}->{'x-body'}, 'TEST', 'request body with padding'); 1432 is($frame->{headers}->{'x-body'}, 'TEST', 'request body with padding');
1432 1433
1437 is($frame->{headers}->{':status'}, '200', 'request body with padding - next'); 1438 is($frame->{headers}->{':status'}, '200', 'request body with padding - next');
1438 1439
1439 # request body sent in multiple DATA frames (uses proxied response) 1440 # request body sent in multiple DATA frames (uses proxied response)
1440 1441
1441 $sess = new_session(); 1442 $sess = new_session();
1442 $sid = new_stream($sess, 1443 $sid = new_stream($sess, { path => '/proxy2/t2.html', body_more => 1 });
1443 { path => '/proxy2/t2.html', body => 'TEST', body_split => [2] }); 1444 h2_body($sess, 'TEST', { body_split => [2] });
1444 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1445 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1445 1446
1446 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1447 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1447 is($frame->{headers}->{'x-body'}, 'TEST', 'request body in multiple frames'); 1448 is($frame->{headers}->{'x-body'}, 'TEST', 'request body in multiple frames');
1448 1449
1449 # request body with an empty DATA frame 1450 # request body with an empty DATA frame
1450 # "zero size buf in output" alerts seen 1451 # "zero size buf in output" alerts seen
1451 1452
1452 $sess = new_session(); 1453 $sess = new_session();
1453 $sid = new_stream($sess, { body => '', headers => [ 1454 $sid = new_stream($sess, { path => '/proxy2/', body_more => 1 });
1454 { name => ':method', value => 'GET', mode => 2 }, 1455 h2_body($sess, '');
1455 { name => ':scheme', value => 'http', mode => 2 },
1456 { name => ':path', value => '/proxy2/', mode => 2 },
1457 { name => ':authority', value => 'localhost', mode => 2 }]});
1458 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1456 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1459 1457
1460 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1458 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1461 is($frame->{headers}->{':status'}, 200, 'request body - empty'); 1459 is($frame->{headers}->{':status'}, 200, 'request body - empty');
1462 1460
1463 # request body delayed in limit_req 1461 # request body delayed in limit_req
1464 1462
1465 $sess = new_session(); 1463 $sess = new_session();
1466 $sid = new_stream($sess, { path => '/proxy_limit_req/', body => 'TEST' }); 1464 $sid = new_stream($sess, { path => '/proxy_limit_req/', body_more => 1 });
1465 h2_body($sess, 'TEST');
1467 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1466 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1468 1467
1469 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1468 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1470 is($frame->{headers}->{'x-body'}, 'TEST', 'request body - limit req'); 1469 is($frame->{headers}->{'x-body'}, 'TEST', 'request body - limit req');
1470
1471 # predict send windows
1472
1473 $sid = new_stream($sess);
1474 my ($maxwin) = sort {$a <=> $b} $sess->{streams}{$sid}, $sess->{conn_window};
1471 1475
1472 SKIP: { 1476 SKIP: {
1473 skip 'leaves coredump', 1 unless $t->has_version('1.9.7'); 1477 skip 'leaves coredump', 1 unless $t->has_version('1.9.7');
1474 1478 skip 'not enough window', 1 if $maxwin < 5;
1479
1480 $sess = new_session();
1475 $sid = new_stream($sess, { path => '/proxy_limit_req/', body => 'TEST2' }); 1481 $sid = new_stream($sess, { path => '/proxy_limit_req/', body => 'TEST2' });
1476 select undef, undef, undef, 1.1; 1482 select undef, undef, undef, 1.1;
1477 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1483 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1478 1484
1479 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1485 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1484 # partial request body data frame received (to be discarded) within request 1490 # partial request body data frame received (to be discarded) within request
1485 # delayed in limit_req, the rest of data frame is received after response 1491 # delayed in limit_req, the rest of data frame is received after response
1486 1492
1487 $sess = new_session(); 1493 $sess = new_session();
1488 1494
1495 SKIP: {
1496 skip 'not enough window', 1 if $maxwin < 4;
1497
1489 TODO: { 1498 TODO: {
1490 todo_skip 'use-after-free', 1 unless $ENV{TEST_NGINX_UNSAFE}; 1499 todo_skip 'use-after-free', 1 unless $ENV{TEST_NGINX_UNSAFE};
1491 1500
1492 $sid = new_stream($sess, { path => '/limit_req', body => 'TEST', split => [61], 1501 $sid = new_stream($sess, { path => '/limit_req', body => 'TEST', split => [61],
1493 split_delay => 1.1 }); 1502 split_delay => 1.1 });
1496 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1505 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1497 is($frame->{headers}->{':status'}, '200', 'discard body - limit req - limited'); 1506 is($frame->{headers}->{':status'}, '200', 'discard body - limit req - limited');
1498 1507
1499 } 1508 }
1500 1509
1510 }
1511
1501 $sid = new_stream($sess, { path => '/' }); 1512 $sid = new_stream($sess, { path => '/' });
1502 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1513 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1503 1514
1504 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1515 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1505 is($frame->{headers}->{':status'}, '200', 'discard body - limit req - next'); 1516 is($frame->{headers}->{':status'}, '200', 'discard body - limit req - next');
1506 1517
1507 # ditto, but instead of receiving the rest of data frame, connection is closed 1518 # ditto, but instead of receiving the rest of data frame, connection is closed
1508 # 'http request already closed while closing request' alert can be produced 1519 # 'http request already closed while closing request' alert can be produced
1509 1520
1521 SKIP: {
1522 skip 'not enough window', 1 if $maxwin < 4;
1523
1510 TODO: { 1524 TODO: {
1511 todo_skip 'use-after-free', 1 unless $ENV{TEST_NGINX_UNSAFE}; 1525 todo_skip 'use-after-free', 1 unless $ENV{TEST_NGINX_UNSAFE};
1512 1526
1513 $sess = new_session(); 1527 $sess = new_session();
1514 $sid = new_stream($sess, { path => '/limit_req', body => 'TEST', split => [61], 1528 $sid = new_stream($sess, { path => '/limit_req', body => 'TEST', split => [61],
1518 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1532 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1519 is($frame->{headers}->{':status'}, '200', 'discard body - limit req - eof'); 1533 is($frame->{headers}->{':status'}, '200', 'discard body - limit req - eof');
1520 1534
1521 select undef, undef, undef, 1.1; 1535 select undef, undef, undef, 1.1;
1522 undef $sess; 1536 undef $sess;
1537
1538 }
1523 1539
1524 } 1540 }
1525 1541
1526 # partial request header frame received (field split), 1542 # partial request header frame received (field split),
1527 # the rest of frame is received after client header timeout 1543 # the rest of frame is received after client header timeout
1550 1566
1551 TODO: { 1567 TODO: {
1552 local $TODO = 'not yet'; 1568 local $TODO = 'not yet';
1553 1569
1554 $sess = new_session(8093); 1570 $sess = new_session(8093);
1555 $sid = new_stream($sess, { path => '/proxy/t2.html', body => 'TEST', 1571 $sid = new_stream($sess, { path => '/proxy/t2.html', body_more => 1 });
1556 split => [67], split_delay => 2.1 }); 1572 h2_body($sess, 'TEST', { split => [10], split_delay => 2.1 });
1557 $frames = h2_read($sess, all => [{ type => 'RST_STREAM' }]); 1573 $frames = h2_read($sess, all => [{ type => 'RST_STREAM' }]);
1558 1574
1559 ($frame) = grep { $_->{type} eq "RST_STREAM" } @$frames; 1575 ($frame) = grep { $_->{type} eq "RST_STREAM" } @$frames;
1560 ok($frame, 'client body timeout'); 1576 ok($frame, 'client body timeout');
1561 is($frame->{code}, 1, 'client body timeout - protocol error'); 1577 is($frame->{code}, 1, 'client body timeout - protocol error');
1570 1586
1571 # malformed request body length not equal to content-length 1587 # malformed request body length not equal to content-length
1572 1588
1573 $sess = new_session(); 1589 $sess = new_session();
1574 $sid = new_stream($sess, 1590 $sid = new_stream($sess,
1575 { path => '/proxy2/t2.html', body => 'TEST', headers => [ 1591 { path => '/proxy2/t2.html', body_more => 1, headers => [
1576 { name => ':method', value => 'GET', mode => 0 }, 1592 { name => ':method', value => 'GET', mode => 0 },
1577 { name => ':scheme', value => 'http', mode => 0 }, 1593 { name => ':scheme', value => 'http', mode => 0 },
1578 { name => ':path', value => '/client_max_body_size', mode => 1 }, 1594 { name => ':path', value => '/client_max_body_size', mode => 1 },
1579 { name => ':authority', value => 'localhost', mode => 1 }, 1595 { name => ':authority', value => 'localhost', mode => 1 },
1580 { name => 'content-length', value => '5', mode => 1 }]}); 1596 { name => 'content-length', value => '5', mode => 1 }]});
1597 h2_body($sess, 'TEST');
1581 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1598 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1582 1599
1583 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1600 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1584 is($frame->{headers}->{':status'}, 400, 'request body less than content-length'); 1601 is($frame->{headers}->{':status'}, 400, 'request body less than content-length');
1585 1602
1586 $sid = new_stream($sess, 1603 $sid = new_stream($sess,
1587 { path => '/proxy2/t2.html', body => 'TEST', headers => [ 1604 { path => '/proxy2/t2.html', body_more => 1, headers => [
1588 { name => ':method', value => 'GET', mode => 0 }, 1605 { name => ':method', value => 'GET', mode => 0 },
1589 { name => ':scheme', value => 'http', mode => 0 }, 1606 { name => ':scheme', value => 'http', mode => 0 },
1590 { name => ':path', value => '/client_max_body_size', mode => 1 }, 1607 { name => ':path', value => '/client_max_body_size', mode => 1 },
1591 { name => ':authority', value => 'localhost', mode => 1 }, 1608 { name => ':authority', value => 'localhost', mode => 1 },
1592 { name => 'content-length', value => '3', mode => 1 }]}); 1609 { name => 'content-length', value => '3', mode => 1 }]});
1610 h2_body($sess, 'TEST');
1593 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1611 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1594 1612
1595 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1613 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1596 is($frame->{headers}->{':status'}, 400, 'request body more than content-length'); 1614 is($frame->{headers}->{':status'}, 400, 'request body more than content-length');
1597 1615
1598 # client_max_body_size 1616 # client_max_body_size
1599 1617
1600 $sess = new_session(); 1618 $sess = new_session();
1601 $sid = new_stream($sess, { path => '/client_max_body_size/t2.html', 1619 $sid = new_stream($sess, { path => '/client_max_body_size/t2.html',
1602 body => 'TESTTEST12' }); 1620 body_more => 1 });
1621 h2_body($sess, 'TESTTEST12');
1603 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1622 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1604 1623
1605 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1624 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1606 is($frame->{headers}->{':status'}, 200, 'client_max_body_size - status'); 1625 is($frame->{headers}->{':status'}, 200, 'client_max_body_size - status');
1607 is(read_body_file($frame->{headers}->{'x-body-file'}), 'TESTTEST12', 1626 is(read_body_file($frame->{headers}->{'x-body-file'}), 'TESTTEST12',
1609 1628
1610 # client_max_body_size - limited 1629 # client_max_body_size - limited
1611 1630
1612 $sess = new_session(); 1631 $sess = new_session();
1613 $sid = new_stream($sess, { path => '/client_max_body_size/t2.html', 1632 $sid = new_stream($sess, { path => '/client_max_body_size/t2.html',
1614 body => 'TESTTEST123' }); 1633 body_more => 1 });
1634 h2_body($sess, 'TESTTEST123');
1615 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1635 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1616 1636
1617 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1637 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1618 is($frame->{headers}->{':status'}, 413, 'client_max_body_size - limited'); 1638 is($frame->{headers}->{':status'}, 413, 'client_max_body_size - limited');
1619 1639
1620 # client_max_body_size - many DATA frames 1640 # client_max_body_size - many DATA frames
1621 1641
1622 $sess = new_session(); 1642 $sess = new_session();
1623 $sid = new_stream($sess, { path => '/client_max_body_size/t2.html', 1643 $sid = new_stream($sess, { path => '/client_max_body_size/t2.html',
1624 body => 'TESTTEST12', body_split => [2] }); 1644 body_more => 1 });
1645 h2_body($sess, 'TESTTEST12', { body_split => [2] });
1625 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1646 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1626 1647
1627 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1648 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1628 is($frame->{headers}->{':status'}, 200, 'client_max_body_size many - status'); 1649 is($frame->{headers}->{':status'}, 200, 'client_max_body_size many - status');
1629 is(read_body_file($frame->{headers}->{'x-body-file'}), 'TESTTEST12', 1650 is(read_body_file($frame->{headers}->{'x-body-file'}), 'TESTTEST12',
1631 1652
1632 # client_max_body_size - many DATA frames - limited 1653 # client_max_body_size - many DATA frames - limited
1633 1654
1634 $sess = new_session(); 1655 $sess = new_session();
1635 $sid = new_stream($sess, { path => '/client_max_body_size/t2.html', 1656 $sid = new_stream($sess, { path => '/client_max_body_size/t2.html',
1636 body => 'TESTTEST123', body_split => [2] }); 1657 body_more => 1 });
1658 h2_body($sess, 'TESTTEST123', { body_split => [2] });
1637 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1659 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1638 1660
1639 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1661 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1640 is($frame->{headers}->{':status'}, 413, 'client_max_body_size many - limited'); 1662 is($frame->{headers}->{':status'}, 413, 'client_max_body_size many - limited');
1641 1663
1642 # client_max_body_size - padded DATA 1664 # client_max_body_size - padded DATA
1643 1665
1644 $sess = new_session(); 1666 $sess = new_session();
1645 $sid = new_stream($sess, { path => '/client_max_body_size/t2.html', 1667 $sid = new_stream($sess, { path => '/client_max_body_size/t2.html',
1646 body => 'TESTTEST12', body_padding => 42 }); 1668 body_more => 1 });
1669 h2_body($sess, 'TESTTEST12', { body_padding => 42 });
1647 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1670 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1648 1671
1649 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1672 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1650 is($frame->{headers}->{':status'}, 200, 'client_max_body_size pad - status'); 1673 is($frame->{headers}->{':status'}, 200, 'client_max_body_size pad - status');
1651 is(read_body_file($frame->{headers}->{'x-body-file'}), 'TESTTEST12', 1674 is(read_body_file($frame->{headers}->{'x-body-file'}), 'TESTTEST12',
1653 1676
1654 # client_max_body_size - padded DATA - limited 1677 # client_max_body_size - padded DATA - limited
1655 1678
1656 $sess = new_session(); 1679 $sess = new_session();
1657 $sid = new_stream($sess, { path => '/client_max_body_size/t2.html', 1680 $sid = new_stream($sess, { path => '/client_max_body_size/t2.html',
1658 body => 'TESTTEST123', body_padding => 42 }); 1681 body_more => 1 });
1682 h2_body($sess, 'TESTTEST123', { body_padding => 42 });
1659 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1683 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1660 1684
1661 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1685 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1662 is($frame->{headers}->{':status'}, 413, 'client_max_body_size pad - limited'); 1686 is($frame->{headers}->{':status'}, 413, 'client_max_body_size pad - limited');
1663 1687
1664 # client_max_body_size - many padded DATA frames 1688 # client_max_body_size - many padded DATA frames
1665 1689
1666 $sess = new_session(); 1690 $sess = new_session();
1667 $sid = new_stream($sess, { path => '/client_max_body_size/t2.html', 1691 $sid = new_stream($sess, { path => '/client_max_body_size/t2.html',
1668 body => 'TESTTEST12', body_padding => 42, body_split => [2] }); 1692 body_more => 1 });
1693 h2_body($sess, 'TESTTEST12', { body_padding => 42, body_split => [2] });
1669 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1694 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1670 1695
1671 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1696 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1672 is($frame->{headers}->{':status'}, 200, 1697 is($frame->{headers}->{':status'}, 200,
1673 'client_max_body_size many pad - status'); 1698 'client_max_body_size many pad - status');
1676 1701
1677 # client_max_body_size - many padded DATA frames - limited 1702 # client_max_body_size - many padded DATA frames - limited
1678 1703
1679 $sess = new_session(); 1704 $sess = new_session();
1680 $sid = new_stream($sess, { path => '/client_max_body_size/t2.html', 1705 $sid = new_stream($sess, { path => '/client_max_body_size/t2.html',
1681 body => 'TESTTEST123', body_padding => 42, body_split => [2] }); 1706 body_more => 1 });
1707 h2_body($sess, 'TESTTEST123', { body_padding => 42, body_split => [2] });
1682 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1708 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1683 1709
1684 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1710 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1685 is($frame->{headers}->{':status'}, 413, 1711 is($frame->{headers}->{':status'}, 413,
1686 'client_max_body_size many pad - limited'); 1712 'client_max_body_size many pad - limited');
1687 1713
1688 # request body without content-length 1714 # request body without content-length
1689 1715
1690 $sess = new_session(); 1716 $sess = new_session();
1691 $sid = new_stream($sess, { body => 'TESTTEST12', headers => [ 1717 $sid = new_stream($sess, { body_more => 1, headers => [
1692 { name => ':method', value => 'GET', mode => 2 }, 1718 { name => ':method', value => 'GET', mode => 2 },
1693 { name => ':scheme', value => 'http', mode => 2 }, 1719 { name => ':scheme', value => 'http', mode => 2 },
1694 { name => ':path', value => '/client_max_body_size', mode => 2 }, 1720 { name => ':path', value => '/client_max_body_size', mode => 2 },
1695 { name => ':authority', value => 'localhost', mode => 2 }]}); 1721 { name => ':authority', value => 'localhost', mode => 2 }]});
1722 h2_body($sess, 'TESTTEST12');
1696 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1723 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1697 1724
1698 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1725 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1699 is($frame->{headers}->{':status'}, 200, 1726 is($frame->{headers}->{':status'}, 200,
1700 'request body without content-length - status'); 1727 'request body without content-length - status');
1702 'request body without content-length - body'); 1729 'request body without content-length - body');
1703 1730
1704 # request body without content-length - limited 1731 # request body without content-length - limited
1705 1732
1706 $sess = new_session(); 1733 $sess = new_session();
1707 $sid = new_stream($sess, { body => 'TESTTEST123', headers => [ 1734 $sid = new_stream($sess, { body_more => 1, headers => [
1708 { name => ':method', value => 'GET', mode => 2 }, 1735 { name => ':method', value => 'GET', mode => 2 },
1709 { name => ':scheme', value => 'http', mode => 2 }, 1736 { name => ':scheme', value => 'http', mode => 2 },
1710 { name => ':path', value => '/client_max_body_size', mode => 2 }, 1737 { name => ':path', value => '/client_max_body_size', mode => 2 },
1711 { name => ':authority', value => 'localhost', mode => 2 }]}); 1738 { name => ':authority', value => 'localhost', mode => 2 }]});
1739 h2_body($sess, 'TESTTEST123');
1712 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1740 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1713 1741
1714 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1742 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1715 is($frame->{headers}->{':status'}, 413, 1743 is($frame->{headers}->{':status'}, 413,
1716 'request body without content-length - limited'); 1744 'request body without content-length - limited');
1717 1745
1718 # request body without content-length - many DATA frames 1746 # request body without content-length - many DATA frames
1719 1747
1720 $sess = new_session(); 1748 $sess = new_session();
1721 $sid = new_stream($sess, { body => 'TESTTEST12', body_split => [2], 1749 $sid = new_stream($sess, { body_more => 1, headers => [
1722 headers => [
1723 { name => ':method', value => 'GET', mode => 2 }, 1750 { name => ':method', value => 'GET', mode => 2 },
1724 { name => ':scheme', value => 'http', mode => 2 }, 1751 { name => ':scheme', value => 'http', mode => 2 },
1725 { name => ':path', value => '/client_max_body_size', mode => 2 }, 1752 { name => ':path', value => '/client_max_body_size', mode => 2 },
1726 { name => ':authority', value => 'localhost', mode => 2 }]}); 1753 { name => ':authority', value => 'localhost', mode => 2 }]});
1754 h2_body($sess, 'TESTTEST12', { body_split => [2] });
1727 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1755 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1728 1756
1729 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1757 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1730 is($frame->{headers}->{':status'}, 200, 1758 is($frame->{headers}->{':status'}, 200,
1731 'request body without content-length many - status'); 1759 'request body without content-length many - status');
1733 'request body without content-length many - body'); 1761 'request body without content-length many - body');
1734 1762
1735 # request body without content-length - many DATA frames - limited 1763 # request body without content-length - many DATA frames - limited
1736 1764
1737 $sess = new_session(); 1765 $sess = new_session();
1738 $sid = new_stream($sess, { body => 'TESTTEST123', body_split => [2], 1766 $sid = new_stream($sess, { body_more => 1, headers => [
1739 headers => [
1740 { name => ':method', value => 'GET', mode => 2 }, 1767 { name => ':method', value => 'GET', mode => 2 },
1741 { name => ':scheme', value => 'http', mode => 2 }, 1768 { name => ':scheme', value => 'http', mode => 2 },
1742 { name => ':path', value => '/client_max_body_size', mode => 2 }, 1769 { name => ':path', value => '/client_max_body_size', mode => 2 },
1743 { name => ':authority', value => 'localhost', mode => 2 }]}); 1770 { name => ':authority', value => 'localhost', mode => 2 }]});
1771 h2_body($sess, 'TESTTEST123', { body_split => [2] });
1744 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1772 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1745 1773
1746 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1774 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1747 is($frame->{headers}->{':status'}, 413, 1775 is($frame->{headers}->{':status'}, 413,
1748 'request body without content-length many - limited'); 1776 'request body without content-length many - limited');
1749 1777
1750 # request body without content-length - padding 1778 # request body without content-length - padding
1751 1779
1752 $sess = new_session(); 1780 $sess = new_session();
1753 $sid = new_stream($sess, { body => 'TESTTEST12', body_padding => 42, 1781 $sid = new_stream($sess, { body_more => 1, headers => [
1754 headers => [
1755 { name => ':method', value => 'GET', mode => 2 }, 1782 { name => ':method', value => 'GET', mode => 2 },
1756 { name => ':scheme', value => 'http', mode => 2 }, 1783 { name => ':scheme', value => 'http', mode => 2 },
1757 { name => ':path', value => '/client_max_body_size', mode => 2 }, 1784 { name => ':path', value => '/client_max_body_size', mode => 2 },
1758 { name => ':authority', value => 'localhost', mode => 2 }]}); 1785 { name => ':authority', value => 'localhost', mode => 2 }]});
1786 h2_body($sess, 'TESTTEST12', { body_padding => 42 });
1759 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1787 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1760 1788
1761 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1789 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1762 is($frame->{headers}->{':status'}, 200, 1790 is($frame->{headers}->{':status'}, 200,
1763 'request body without content-length pad - status'); 1791 'request body without content-length pad - status');
1765 'request body without content-length pad - body'); 1793 'request body without content-length pad - body');
1766 1794
1767 # request body without content-length - padding - limited 1795 # request body without content-length - padding - limited
1768 1796
1769 $sess = new_session(); 1797 $sess = new_session();
1770 $sid = new_stream($sess, { body => 'TESTTEST123', body_padding => 42, 1798 $sid = new_stream($sess, { body_more => 1, headers => [
1771 headers => [
1772 { name => ':method', value => 'GET', mode => 2 }, 1799 { name => ':method', value => 'GET', mode => 2 },
1773 { name => ':scheme', value => 'http', mode => 2 }, 1800 { name => ':scheme', value => 'http', mode => 2 },
1774 { name => ':path', value => '/client_max_body_size', mode => 2 }, 1801 { name => ':path', value => '/client_max_body_size', mode => 2 },
1775 { name => ':authority', value => 'localhost', mode => 2 }]}); 1802 { name => ':authority', value => 'localhost', mode => 2 }]});
1803 h2_body($sess, 'TESTTEST123', { body_padding => 42 });
1776 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1804 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1777 1805
1778 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1806 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1779 is($frame->{headers}->{':status'}, 413, 1807 is($frame->{headers}->{':status'}, 413,
1780 'request body without content-length pad - limited'); 1808 'request body without content-length pad - limited');
1781 1809
1782 # request body without content-length - padding with many DATA frames 1810 # request body without content-length - padding with many DATA frames
1783 1811
1784 $sess = new_session(); 1812 $sess = new_session();
1785 $sid = new_stream($sess, { body => 'TESTTEST', body_padding => 42, 1813 $sid = new_stream($sess, { body_more => 1, headers => [
1786 body_split => [2], headers => [
1787 { name => ':method', value => 'GET', mode => 2 }, 1814 { name => ':method', value => 'GET', mode => 2 },
1788 { name => ':scheme', value => 'http', mode => 2 }, 1815 { name => ':scheme', value => 'http', mode => 2 },
1789 { name => ':path', value => '/client_max_body_size', mode => 2 }, 1816 { name => ':path', value => '/client_max_body_size', mode => 2 },
1790 { name => ':authority', value => 'localhost', mode => 2 }]}); 1817 { name => ':authority', value => 'localhost', mode => 2 }]});
1818 h2_body($sess, 'TESTTEST', { body_padding => 42, body_split => [2] });
1791 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1819 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1792 1820
1793 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1821 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1794 is($frame->{headers}->{':status'}, 200, 1822 is($frame->{headers}->{':status'}, 200,
1795 'request body without content-length many pad - status'); 1823 'request body without content-length many pad - status');
1797 'request body without content-length many pad - body'); 1825 'request body without content-length many pad - body');
1798 1826
1799 # request body without content-length - padding with many DATA frames - limited 1827 # request body without content-length - padding with many DATA frames - limited
1800 1828
1801 $sess = new_session(); 1829 $sess = new_session();
1802 $sid = new_stream($sess, { body => 'TESTTEST123', body_padding => 42, 1830 $sid = new_stream($sess, { body_more => 1, headers => [
1803 body_split => [2], headers => [
1804 { name => ':method', value => 'GET', mode => 2 }, 1831 { name => ':method', value => 'GET', mode => 2 },
1805 { name => ':scheme', value => 'http', mode => 2 }, 1832 { name => ':scheme', value => 'http', mode => 2 },
1806 { name => ':path', value => '/client_max_body_size', mode => 2 }, 1833 { name => ':path', value => '/client_max_body_size', mode => 2 },
1807 { name => ':authority', value => 'localhost', mode => 2 }]}); 1834 { name => ':authority', value => 'localhost', mode => 2 }]});
1835 h2_body($sess, 'TESTTEST123', { body_padding => 42, body_split => [2] });
1808 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 1836 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
1809 1837
1810 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames; 1838 ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
1811 is($frame->{headers}->{':status'}, 413, 1839 is($frame->{headers}->{':status'}, 413,
1812 'request body without content-length many pad - limited'); 1840 'request body without content-length many pad - limited');
2779 ($frame) = grep { $_->{type} eq "HEADERS" && $_->{sid} == $sid2 } @$frames; 2807 ($frame) = grep { $_->{type} eq "HEADERS" && $_->{sid} == $sid2 } @$frames;
2780 is($frame->{headers}->{':status'}, 200, 'RST_STREAM 2'); 2808 is($frame->{headers}->{':status'}, 200, 'RST_STREAM 2');
2781 2809
2782 # http2_max_concurrent_streams 2810 # http2_max_concurrent_streams
2783 2811
2784 $sess = new_session(8086); 2812 $sess = new_session(8086, pure => 1);
2785 $frames = h2_read($sess, all => [{ type => 'SETTINGS' }]); 2813 $frames = h2_read($sess, all => [{ type => 'SETTINGS' }]);
2786 2814
2787 ($frame) = grep { $_->{type} eq 'SETTINGS' } @$frames; 2815 ($frame) = grep { $_->{type} eq 'SETTINGS' } @$frames;
2788 is($frame->{3}, 1, 'http2_max_concurrent_streams SETTINGS'); 2816 is($frame->{3}, 1, 'http2_max_concurrent_streams SETTINGS');
2789 2817
2857 2885
2858 # some invalid cases below 2886 # some invalid cases below
2859 2887
2860 # invalid connection preface 2888 # invalid connection preface
2861 2889
2862 $sess = new_session(8080, preface => 'bogus preface'); 2890 $sess = new_session(8080, preface => 'x' x 16, pure => 1);
2863 $sid = new_stream($sess, { path => '/pp' }); 2891 $frames = h2_read($sess, all => [{ type => 'GOAWAY' }]);
2864 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]);
2865 2892
2866 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; 2893 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames;
2867 ok($frame, 'invalid preface - GOAWAY frame'); 2894 ok($frame, 'invalid preface - GOAWAY frame');
2868 is($frame->{code}, 1, 'invalid preface - error code'); 2895 is($frame->{code}, 1, 'invalid preface - error code');
2869 2896
2870 $sess = new_session(8080, preface => 'PRI * HTTP/2.0' . CRLF . CRLF . 'bogus'); 2897 $sess = new_session(8080, preface => 'PRI * HTTP/2.0' . CRLF . CRLF . 'x' x 8,
2871 $sid = new_stream($sess, { path => '/pp' }); 2898 pure => 1);
2872 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 2899 $frames = h2_read($sess, all => [{ type => 'GOAWAY' }]);
2873 2900
2874 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; 2901 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames;
2875 ok($frame, 'invalid preface 2 - GOAWAY frame'); 2902 ok($frame, 'invalid preface 2 - GOAWAY frame');
2876 is($frame->{code}, 1, 'invalid preface 2 - error code'); 2903 is($frame->{code}, 1, 'invalid preface 2 - error code');
2877 2904
2878 # invalid PROXY protocol string 2905 # invalid PROXY protocol string
2879 2906
2880 $sess = new_session(8082, proxy => 'bogus'); 2907 $sess = new_session(8082, proxy => 'BOGUS TCP4 192.0.2.1 192.0.2.2 1234 5678',
2881 $sid = new_stream($sess, { path => '/pp' }); 2908 pure => 1);
2882 $frames = h2_read($sess, all => [{ sid => $sid, fin => 1 }]); 2909 $frames = h2_read($sess, all => [{ type => 'GOAWAY' }]);
2883 2910
2884 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames; 2911 ($frame) = grep { $_->{type} eq "GOAWAY" } @$frames;
2885 ok($frame, 'invalid PROXY - GOAWAY frame'); 2912 ok($frame, 'invalid PROXY - GOAWAY frame');
2886 is($frame->{code}, 1, 'invalid PROXY - error code'); 2913 is($frame->{code}, 1, 'invalid PROXY - error code');
2887 2914
3049 h2_read($grace2, all => [{ sid => $sid, length => 2**16 - 1 }]); 3076 h2_read($grace2, all => [{ sid => $sid, length => 2**16 - 1 }]);
3050 3077
3051 # graceful shutdown waiting on incomplete request body DATA frames 3078 # graceful shutdown waiting on incomplete request body DATA frames
3052 3079
3053 my $grace3 = new_session(8090); 3080 my $grace3 = new_session(8090);
3054 $sid = new_stream($grace3, { path => '/proxy2/t2.html', body => 'TEST', 3081 $sid = new_stream($grace3, { path => '/proxy2/t2.html', body_more => 1 });
3055 body_split => [ 2 ], split => [ 67 ], abort => 1 }); 3082 h2_body($grace3, 'TEST', { body_more => 1 });
3056 3083
3057 # partial request body data frame with connection close after body timeout 3084 # partial request body data frame with connection close after body timeout
3058 3085
3059 my $grace4 = new_session(8093); 3086 my $grace4 = new_session(8093);
3060 $sid = new_stream($grace4, { path => '/proxy/t2.html', body => 'TEST', 3087 $sid = new_stream($grace4, { path => '/proxy/t2.html', body_more => 1 });
3061 split => [67], abort => 1 }); 3088 h2_body($grace4, 'TEST', { split => [ 12 ], abort => 1 });
3062 3089
3063 select undef, undef, undef, 1.1; 3090 select undef, undef, undef, 1.1;
3064 undef $grace4; 3091 undef $grace4;
3065 3092
3066 $t->stop(); 3093 $t->stop();
3135 3162
3136 $uri->{h2_continue} = 1; 3163 $uri->{h2_continue} = 1;
3137 return new_stream($ctx, $uri, $stream); 3164 return new_stream($ctx, $uri, $stream);
3138 } 3165 }
3139 3166
3167 sub h2_body {
3168 my ($sess, $body, $extra) = @_;
3169 $extra = {} unless defined $extra;
3170
3171 my $len = length $body;
3172 my $sid = $sess->{last_stream};
3173
3174 if ($len > $sess->{conn_window} || $len > $sess->{streams}{$sid}) {
3175 h2_read($sess, all => [{ type => 'WINDOW_UPDATE' }]);
3176 }
3177
3178 if ($len > $sess->{conn_window} || $len > $sess->{streams}{$sid}) {
3179 return;
3180 }
3181
3182 $sess->{conn_window} -= $len;
3183 $sess->{streams}{$sid} -= $len;
3184
3185 my $buf;
3186
3187 my $split = ref $extra->{body_split} && $extra->{body_split} || [];
3188 for (@$split) {
3189 $buf .= pack_body($sess, substr($body, 0, $_, ""), 0x0, $extra);
3190 }
3191
3192 $buf .= pack_body($sess, $body, 0x1, $extra) if defined $body;
3193
3194 $split = ref $extra->{split} && $extra->{split} || [];
3195 for (@$split) {
3196 raw_write($sess->{socket}, substr($buf, 0, $_, ""));
3197 return if $extra->{abort};
3198 select undef, undef, undef, ($extra->{split_delay} || 0.2);
3199 }
3200
3201 raw_write($sess->{socket}, $buf);
3202 }
3203
3140 sub pack_body { 3204 sub pack_body {
3141 my ($ctx, $body, $flags, $extra) = @_; 3205 my ($ctx, $body, $flags, $extra) = @_;
3142 3206
3143 my $pad = defined $extra->{body_padding} ? $extra->{body_padding} : 0; 3207 my $pad = defined $extra->{body_padding} ? $extra->{body_padding} : 0;
3144 my $padlen = defined $extra->{body_padding} ? 1 : 0; 3208 my $padlen = defined $extra->{body_padding} ? 1 : 0;
3145 3209
3146 my $buf = pack_length(length($body) + $pad + $padlen); 3210 my $buf = pack_length(length($body) + $pad + $padlen);
3147 $flags |= 0x8 if $padlen; 3211 $flags |= 0x8 if $padlen;
3212 vec($flags, 0, 1) = 0 if $extra->{body_more};
3148 $buf .= pack 'CC', 0x0, $flags; # DATA, END_STREAM 3213 $buf .= pack 'CC', 0x0, $flags; # DATA, END_STREAM
3149 $buf .= pack 'N', $ctx->{last_stream}; 3214 $buf .= pack 'N', $ctx->{last_stream};
3150 $buf .= pack 'C', $pad if $padlen; # DATA Pad Length? 3215 $buf .= pack 'C', $pad if $padlen; # DATA Pad Length?
3151 $buf .= $body; 3216 $buf .= $body;
3152 $buf .= pack "x$pad" if $padlen; # DATA Padding 3217 $buf .= pack "x$pad" if $padlen; # DATA Padding
3172 my $pad = defined $uri->{padding} ? $uri->{padding} : 0; 3237 my $pad = defined $uri->{padding} ? $uri->{padding} : 0;
3173 my $padlen = defined $uri->{padding} ? 1 : 0; 3238 my $padlen = defined $uri->{padding} ? 1 : 0;
3174 3239
3175 my $type = defined $uri->{h2_continue} ? 0x9 : 0x1; 3240 my $type = defined $uri->{h2_continue} ? 0x9 : 0x1;
3176 my $flags = defined $uri->{continuation} ? 0x0 : 0x4; 3241 my $flags = defined $uri->{continuation} ? 0x0 : 0x4;
3177 $flags |= 0x1 unless defined $body; 3242 $flags |= 0x1 unless defined $body || defined $uri->{body_more};
3178 $flags |= 0x8 if $padlen; 3243 $flags |= 0x8 if $padlen;
3179 $flags |= 0x20 if defined $dep || defined $prio; 3244 $flags |= 0x20 if defined $dep || defined $prio;
3180 3245
3181 if ($stream) { 3246 if ($stream) {
3182 $ctx->{last_stream} = $stream; 3247 $ctx->{last_stream} = $stream;
3183 } else { 3248 } else {
3184 $ctx->{last_stream} += 2; 3249 $ctx->{last_stream} += 2;
3250 $ctx->{streams}{$ctx->{last_stream}} = $ctx->{iws};
3185 } 3251 }
3186 3252
3187 $buf = pack("xxx"); # Length stub 3253 $buf = pack("xxx"); # Length stub
3188 $buf .= pack("CC", $type, $flags); # END_HEADERS 3254 $buf .= pack("CC", $type, $flags); # END_HEADERS
3189 $buf .= pack("N", $ctx->{last_stream}); # Stream-ID 3255 $buf .= pack("N", $ctx->{last_stream}); # Stream-ID
3274 $buf = raw_read($s, $buf, $length + 9); 3340 $buf = raw_read($s, $buf, $length + 9);
3275 last if length($buf) < $length + 9; 3341 last if length($buf) < $length + 9;
3276 3342
3277 $buf = substr($buf, 9); 3343 $buf = substr($buf, 9);
3278 3344
3279 my $frame = $cframe{$type}{value}($sess, $buf, $length, $flags); 3345 my $frame = $cframe{$type}{value}($sess, $buf, $length, $flags,
3346 $stream);
3280 $frame->{length} = $length; 3347 $frame->{length} = $length;
3281 $frame->{type} = $cframe{$type}{name}; 3348 $frame->{type} = $cframe{$type}{name};
3282 $frame->{flags} = $flags; 3349 $frame->{flags} = $flags;
3283 $frame->{sid} = $stream; 3350 $frame->{sid} = $stream;
3284 push @got, $frame; 3351 push @got, $frame;
3338 my $skip = 0; 3405 my $skip = 0;
3339 3406
3340 for (1 .. $len / 6) { 3407 for (1 .. $len / 6) {
3341 my $id = hex unpack "\@$skip n", $buf; $skip += 2; 3408 my $id = hex unpack "\@$skip n", $buf; $skip += 2;
3342 $payload{$id} = unpack "\@$skip N", $buf; $skip += 4; 3409 $payload{$id} = unpack "\@$skip N", $buf; $skip += 4;
3410
3411 $ctx->{iws} = $payload{$id} if $id == 4;
3343 } 3412 }
3344 return \%payload; 3413 return \%payload;
3345 } 3414 }
3346 3415
3347 sub ping { 3416 sub ping {
3368 $payload{debug} = unpack "x8 A$len", $buf; 3437 $payload{debug} = unpack "x8 A$len", $buf;
3369 return \%payload; 3438 return \%payload;
3370 } 3439 }
3371 3440
3372 sub window_update { 3441 sub window_update {
3373 my ($ctx, $buf, $len) = @_; 3442 my ($ctx, $buf, $len, $flags, $sid) = @_;
3374 my $value = unpack "B32", $buf; 3443 my $value = unpack "B32", $buf;
3375 substr($value, 0, 1) = 0; 3444 substr($value, 0, 1) = 0;
3376 return { wdelta => unpack("N", pack("B32", $value)) }; 3445 $value = unpack("N", pack("B32", $value));
3446
3447 unless ($sid) {
3448 $ctx->{conn_window} += $value;
3449
3450 } else {
3451 $ctx->{streams}{$sid} = $ctx->{iws}
3452 unless defined $ctx->{streams}{$sid};
3453 $ctx->{streams}{$sid} += $value;
3454 }
3455
3456 return { wdelta => $value };
3377 } 3457 }
3378 3458
3379 sub pack_length { 3459 sub pack_length {
3380 pack 'c3', unpack 'xc3', pack 'N', $_[0]; 3460 pack 'c3', unpack 'xc3', pack 'N', $_[0];
3381 } 3461 }
3423 3503
3424 # preface 3504 # preface
3425 3505
3426 raw_write($s, $preface); 3506 raw_write($s, $preface);
3427 3507
3428 return { socket => $s, last_stream => -1, 3508 my $ctx = { socket => $s, last_stream => -1,
3429 dynamic_encode => [ static_table() ], 3509 dynamic_encode => [ static_table() ],
3430 dynamic_decode => [ static_table() ], 3510 dynamic_decode => [ static_table() ],
3431 static_table_size => scalar @{[static_table()]} }; 3511 static_table_size => scalar @{[static_table()]},
3512 iws => 65535, conn_window => 65535, streams => {}};
3513
3514 return $ctx if $extra{pure};
3515
3516 # update windows, if any
3517
3518 h2_read($ctx, all => [
3519 { type => 'WINDOW_UPDATE' },
3520 { type => 'SETTINGS'}
3521 ]);
3522
3523 return $ctx;
3432 } 3524 }
3433 3525
3434 sub new_socket { 3526 sub new_socket {
3435 my ($port, %extra) = @_; 3527 my ($port, %extra) = @_;
3436 my $npn = $extra{'npn'}; 3528 my $npn = $extra{'npn'};