Implemented header packing logic
This commit is contained in:
parent
ff824cdaba
commit
b706afaf67
@ -6,9 +6,9 @@ This is a project that is exploring the feasability and practicality of bringing
|
|||||||
|
|
||||||
Currently implemented:
|
Currently implemented:
|
||||||
|
|
||||||
* Huffman encoding (for compressed headers)
|
* Huffman encoding and decoding (for compressed headers)
|
||||||
* Header parsing (including dictionary)
|
* Header parsing and packing (only static dictionary, no dynamic dictionary)
|
||||||
* Frame parsing (not all frame types)
|
* Frame parsing core (not all frame types)
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
@ -2,15 +2,18 @@
|
|||||||
|
|
||||||
namespace NoccyLabs\React\Http2\Header;
|
namespace NoccyLabs\React\Http2\Header;
|
||||||
|
|
||||||
|
use ArrayIterator;
|
||||||
|
use IteratorAggregate;
|
||||||
use NoccyLabs\React\Http2\Huffman\Codec;
|
use NoccyLabs\React\Http2\Huffman\Codec;
|
||||||
use NoccyLabs\React\Http2\Huffman\Dictionary;
|
use NoccyLabs\React\Http2\Huffman\Dictionary;
|
||||||
|
use Traversable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An ordered list of HTTP/2 headers
|
* An ordered list of HTTP/2 headers
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class HeaderBag
|
class HeaderBag implements IteratorAggregate
|
||||||
{
|
{
|
||||||
|
|
||||||
private array $headers = [];
|
private array $headers = [];
|
||||||
@ -24,4 +27,9 @@ class HeaderBag
|
|||||||
{
|
{
|
||||||
$this->headers[] = [ $name, $value ];
|
$this->headers[] = [ $name, $value ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getIterator(): Traversable
|
||||||
|
{
|
||||||
|
return new ArrayIterator($this->headers);
|
||||||
|
}
|
||||||
}
|
}
|
@ -79,6 +79,8 @@ class HeaderPacker
|
|||||||
];
|
];
|
||||||
private const STATIC_TABLE_SIZE = 62; // considering unused index 0
|
private const STATIC_TABLE_SIZE = 62; // considering unused index 0
|
||||||
|
|
||||||
|
private array $dynamicTable = [];
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
if (!self::$huffman) self::$huffman = new Codec(Dictionary::createRfc7541Dictionary());
|
if (!self::$huffman) self::$huffman = new Codec(Dictionary::createRfc7541Dictionary());
|
||||||
@ -88,6 +90,20 @@ class HeaderPacker
|
|||||||
{
|
{
|
||||||
$packed = '';
|
$packed = '';
|
||||||
|
|
||||||
|
foreach ($headers as [$header,$value]) {
|
||||||
|
if ($index = $this->lookupIndexForHeader($header,$value)) {
|
||||||
|
// Indexed
|
||||||
|
$packed .= chr(0x80 | ($index & 0x7F));
|
||||||
|
} elseif ($index = $this->lookupIndexForHeader($header,null)) {
|
||||||
|
// Indexed literal
|
||||||
|
$packed .= chr(0x40 | ($index & 0x3F));
|
||||||
|
$value = self::$huffman->encode($value);
|
||||||
|
$packed .= chr(0x80 | strlen($value) & 0x7F);
|
||||||
|
$packed .= $value;
|
||||||
|
} else {
|
||||||
|
// Literal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $packed;
|
return $packed;
|
||||||
}
|
}
|
||||||
@ -149,7 +165,21 @@ class HeaderPacker
|
|||||||
|
|
||||||
private function getIndexedHeader(int $index): array
|
private function getIndexedHeader(int $index): array
|
||||||
{
|
{
|
||||||
return self::$staticTable[$index];
|
if ($index < self::STATIC_TABLE_SIZE)
|
||||||
|
return self::$staticTable[$index];
|
||||||
|
// TODO bounds check
|
||||||
|
return $this->dynamicTable[$index - self::STATIC_TABLE_SIZE];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function lookupIndexForHeader(string $header, ?string $value): ?int
|
||||||
|
{
|
||||||
|
foreach (self::$staticTable as $index=>[$iName, $iValue]) {
|
||||||
|
if ($header === $iName && $value === $iValue) return $index;
|
||||||
|
}
|
||||||
|
foreach ($this->dynamicTable as $index=>[$iName, $iValue]) {
|
||||||
|
if ($header === $iName && $value === $iValue) return $index + self::STATIC_TABLE_SIZE;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -26,5 +26,22 @@ class HeaderPackerTest extends \PHPUnit\Framework\TestCase
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testPackingExamplesFromRfc7541()
|
||||||
|
{
|
||||||
|
$packer = new HeaderPacker();
|
||||||
|
|
||||||
|
$expect = "\x82\x86\x84\x41\x8c\xf1\xe3\xc2\xe5\xf2\x3a\x6b\xa0\xab\x90\xf4\xff";
|
||||||
|
$in = new HeaderBag([
|
||||||
|
':method' => 'GET',
|
||||||
|
':scheme' => 'http',
|
||||||
|
':path' => '/',
|
||||||
|
':authority' => 'www.example.com'
|
||||||
|
]);
|
||||||
|
$out = $packer->packHeaders($in);
|
||||||
|
|
||||||
|
$this->assertEquals($expect, $out);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user