Mercurial > hg > nginx-tests
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'}; |