Initial commit
This commit is contained in:
commit
fb1ce8cbc1
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/vendor/
|
||||||
|
/composer.lock
|
||||||
|
/.phpunit.*
|
10
README.md
Normal file
10
README.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# HTTP/2 Support for ReactPHP
|
||||||
|
|
||||||
|
This is a project that is exploring the feasability and practicality of bringing HTTP/2 to ReactPHP while staying true to the ReactPHP philosophy. The main rationale for this is to eventually enable native support for gRPC.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Currently implemented:
|
||||||
|
|
||||||
|
* Huffman encoding (for compressed headers)
|
||||||
|
* Header parsing (including dictionary)
|
25
composer.json
Normal file
25
composer.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "noccylabs/react-http2",
|
||||||
|
"description": "Native ReactPHP HTTP2 implementation",
|
||||||
|
"type": "library",
|
||||||
|
"license": "GPL-3.0-or-later",
|
||||||
|
"keywords": [ "reactphp", "websockets" ],
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"NoccyLabs\\React\\Http2\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "NoccyLabs",
|
||||||
|
"email": "labs@noccy.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"react/http": "^1.9.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^11.0",
|
||||||
|
"phpstan/phpstan": "^1.10"
|
||||||
|
}
|
||||||
|
}
|
12
phpstan.neon
Normal file
12
phpstan.neon
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
parameters:
|
||||||
|
level: 5
|
||||||
|
|
||||||
|
excludePaths:
|
||||||
|
- doc
|
||||||
|
- vendor
|
||||||
|
- tests
|
||||||
|
|
||||||
|
# Paths to include in the analysis
|
||||||
|
paths:
|
||||||
|
- src
|
||||||
|
|
23
phpunit.xml
Normal file
23
phpunit.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.0/phpunit.xsd"
|
||||||
|
bootstrap="vendor/autoload.php"
|
||||||
|
cacheDirectory=".phpunit.cache"
|
||||||
|
executionOrder="depends,defects"
|
||||||
|
requireCoverageMetadata="true"
|
||||||
|
beStrictAboutCoverageMetadata="true"
|
||||||
|
beStrictAboutOutputDuringTests="true"
|
||||||
|
failOnRisky="true"
|
||||||
|
failOnWarning="true">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="default">
|
||||||
|
<directory>tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
|
||||||
|
<source restrictDeprecations="true" restrictNotices="true" restrictWarnings="true">
|
||||||
|
<include>
|
||||||
|
<directory>src</directory>
|
||||||
|
</include>
|
||||||
|
</source>
|
||||||
|
</phpunit>
|
27
src/Header/HeaderBag.php
Normal file
27
src/Header/HeaderBag.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NoccyLabs\React\Http2\Header;
|
||||||
|
|
||||||
|
use NoccyLabs\React\Http2\Huffman\Codec;
|
||||||
|
use NoccyLabs\React\Http2\Huffman\Dictionary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An ordered list of HTTP/2 headers
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class HeaderBag
|
||||||
|
{
|
||||||
|
|
||||||
|
private array $headers = [];
|
||||||
|
|
||||||
|
public function __construct(array $headers = [])
|
||||||
|
{
|
||||||
|
foreach ($headers as $name=>$value) $this->append($name, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function append(string $name, string $value)
|
||||||
|
{
|
||||||
|
$this->headers[] = [ $name, $value ];
|
||||||
|
}
|
||||||
|
}
|
155
src/Header/HeaderPacker.php
Normal file
155
src/Header/HeaderPacker.php
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NoccyLabs\React\Http2\Header;
|
||||||
|
|
||||||
|
use NoccyLabs\React\Http2\Huffman\Codec;
|
||||||
|
use NoccyLabs\React\Http2\Huffman\Dictionary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Per-connection header packer, implements HPACK for HTTP/2 as per RFC7541
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class HeaderPacker
|
||||||
|
{
|
||||||
|
/** @var Codec $huffman The codec and dictionary is static and shared */
|
||||||
|
private static ?Codec $huffman = null;
|
||||||
|
|
||||||
|
private static $staticTable = [
|
||||||
|
1 => [ ':authority', null],
|
||||||
|
2 => [ ':method', 'GET' ],
|
||||||
|
3 => [ ':method', 'POST'],
|
||||||
|
4 => [ ':path', '/'],
|
||||||
|
5 => [ ':path', '/index.html'],
|
||||||
|
6 => [ ':scheme', 'http'],
|
||||||
|
7 => [ ':scheme', 'https'],
|
||||||
|
8 => [ ':status', '200'],
|
||||||
|
9 => [ ':status', '204'],
|
||||||
|
10 => [ ':status', '206'],
|
||||||
|
11 => [ ':status', '304'],
|
||||||
|
12 => [ ':status', '400'],
|
||||||
|
13 => [ ':status', '404'],
|
||||||
|
14 => [ ':status', '500'],
|
||||||
|
15 => [ 'accept-charset', null],
|
||||||
|
16 => [ 'accept-encoding', 'gzip, deflate'],
|
||||||
|
17 => [ 'accept-language', null],
|
||||||
|
18 => [ 'accept-ranges', null],
|
||||||
|
19 => [ 'accept', null ],
|
||||||
|
20 => [ 'access-control-allow-origin', null],
|
||||||
|
21 => [ 'age', null],
|
||||||
|
22 => [ 'allow', null],
|
||||||
|
23 => [ 'authorization', null],
|
||||||
|
24 => [ 'cache-control', null],
|
||||||
|
25 => [ 'content-disposition', null],
|
||||||
|
26 => [ 'content-encoding', null],
|
||||||
|
27 => [ 'content-language', null],
|
||||||
|
28 => [ 'content-length', null],
|
||||||
|
29 => [ 'content-location', null],
|
||||||
|
30 => [ 'content-range', null],
|
||||||
|
31 => [ 'content-type', null],
|
||||||
|
32 => [ 'cookie', null],
|
||||||
|
33 => [ 'date', null],
|
||||||
|
34 => [ 'etag', null],
|
||||||
|
35 => [ 'expect', null],
|
||||||
|
36 => [ 'expires', null],
|
||||||
|
37 => [ 'from', null],
|
||||||
|
38 => [ 'host', null],
|
||||||
|
39 => [ 'if-match', null],
|
||||||
|
40 => [ 'if-modified-since', null],
|
||||||
|
41 => [ 'if-none-match', null],
|
||||||
|
42 => [ 'if-range', null],
|
||||||
|
43 => [ 'if-unmodified-since', null],
|
||||||
|
44 => [ 'last-modified', null],
|
||||||
|
45 => [ 'link', null],
|
||||||
|
46 => [ 'location', null],
|
||||||
|
47 => [ 'max-forwards', null],
|
||||||
|
48 => [ 'proxy-authenticate', null],
|
||||||
|
49 => [ 'proxy-authorization', null],
|
||||||
|
50 => [ 'range', null],
|
||||||
|
51 => [ 'referer', null],
|
||||||
|
52 => [ 'refresh', null],
|
||||||
|
53 => [ 'retry-after', null],
|
||||||
|
54 => [ 'server', null],
|
||||||
|
55 => [ 'set-cookie', null],
|
||||||
|
56 => [ 'strict-transport-security', null],
|
||||||
|
57 => [ 'transfer-encoding', null],
|
||||||
|
58 => [ 'user-agent', null],
|
||||||
|
59 => [ 'vary', null],
|
||||||
|
60 => [ 'via', null],
|
||||||
|
61 => [ 'www-authenticate', null],
|
||||||
|
];
|
||||||
|
private const STATIC_TABLE_SIZE = 62; // considering unused index 0
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
if (!self::$huffman) self::$huffman = new Codec(Dictionary::createRfc7541Dictionary());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function packHeaders(HeaderBag $headers): string
|
||||||
|
{
|
||||||
|
$packed = '';
|
||||||
|
|
||||||
|
|
||||||
|
return $packed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unpackHeaders(string $raw): HeaderBag
|
||||||
|
{
|
||||||
|
$headers = new HeaderBag();
|
||||||
|
$bytes = array_map("ord", str_split($raw));
|
||||||
|
|
||||||
|
while (count($bytes) > 0) {
|
||||||
|
$head = array_shift($bytes);
|
||||||
|
if ($head & 0x80) {
|
||||||
|
// Indexed field
|
||||||
|
[$header,$value] = $this->getIndexedHeader($head & 0x7F);
|
||||||
|
$headers->append($header,$value);
|
||||||
|
} elseif ($head & 0x40) {
|
||||||
|
// Indexed with literal
|
||||||
|
[$header,$value] = $this->getIndexedHeader($head & 0x3F);
|
||||||
|
$valueHead = array_shift($bytes);
|
||||||
|
$encoded = (bool)($valueHead & 0x80);
|
||||||
|
$length = ($valueHead & 0x7F);
|
||||||
|
while ($length-- > 0) {
|
||||||
|
$value .= chr(array_shift($bytes));
|
||||||
|
}
|
||||||
|
if ($encoded) {
|
||||||
|
$value = self::$huffman->decode($value);
|
||||||
|
}
|
||||||
|
$headers->append($header,$value);
|
||||||
|
} else {
|
||||||
|
// Literal field
|
||||||
|
$valueHead = array_shift($bytes);
|
||||||
|
$encoded = (bool)($valueHead & 0x80);
|
||||||
|
$length = ($valueHead & 0x7F);
|
||||||
|
$header = '';
|
||||||
|
while ($length-- > 0) {
|
||||||
|
$header .= chr(array_shift($bytes));
|
||||||
|
}
|
||||||
|
if ($encoded) {
|
||||||
|
$header = self::$huffman->decode($header);
|
||||||
|
}
|
||||||
|
|
||||||
|
$valueHead = array_shift($bytes);
|
||||||
|
$encoded = (bool)($valueHead & 0x80);
|
||||||
|
$length = ($valueHead & 0x7F);
|
||||||
|
$value = '';
|
||||||
|
while ($length-- > 0) {
|
||||||
|
$value .= chr(array_shift($bytes));
|
||||||
|
}
|
||||||
|
if ($encoded) {
|
||||||
|
$value = self::$huffman->decode($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$headers->append($header,$value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getIndexedHeader(int $index): array
|
||||||
|
{
|
||||||
|
return self::$staticTable[$index];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
50
src/Huffman/Codec.php
Normal file
50
src/Huffman/Codec.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NoccyLabs\React\Http2\Huffman;
|
||||||
|
|
||||||
|
class Codec
|
||||||
|
{
|
||||||
|
private Dictionary $dictionary;
|
||||||
|
|
||||||
|
const CODE_EOS = 256;
|
||||||
|
|
||||||
|
public function __construct(Dictionary $dictionary)
|
||||||
|
{
|
||||||
|
$this->dictionary = $dictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function decode(string $data): ?string
|
||||||
|
{
|
||||||
|
$in = array_map("ord", str_split($data));
|
||||||
|
$out = [];
|
||||||
|
$bits = '';
|
||||||
|
for ($ch = 0; $ch < count($in); $ch++) {
|
||||||
|
$bits .= str_pad(decbin($in[$ch]), 8, "0", STR_PAD_LEFT);
|
||||||
|
while (($code = $this->dictionary->match($bits)) !== null) {
|
||||||
|
if ($code === self::CODE_EOS) break(2);
|
||||||
|
$out[] = $code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return join("", array_map("chr", $out));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function encode(string $data): ?string
|
||||||
|
{
|
||||||
|
$in = array_map("ord", str_split($data));
|
||||||
|
//$in[] = self::CODE_EOS;
|
||||||
|
$out = [];
|
||||||
|
$bits = '';
|
||||||
|
for ($ch = 0; $ch < count($in); $ch++) {
|
||||||
|
$bits .= $this->dictionary->pattern($in[$ch]);
|
||||||
|
while (strlen($bits) >= 8) {
|
||||||
|
$out[] = bindec(substr($bits, 0, 8));
|
||||||
|
$bits = substr($bits, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (strlen($bits) > 0) {
|
||||||
|
$out[] = bindec(str_pad($bits, 8, "1", STR_PAD_RIGHT));
|
||||||
|
}
|
||||||
|
return join("", array_map("chr", $out));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
296
src/Huffman/Dictionary.php
Normal file
296
src/Huffman/Dictionary.php
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NoccyLabs\React\Http2\Huffman;
|
||||||
|
|
||||||
|
class Dictionary
|
||||||
|
{
|
||||||
|
private array $codes;
|
||||||
|
|
||||||
|
private array $patterns;
|
||||||
|
|
||||||
|
public function __construct(?array $codes = [])
|
||||||
|
{
|
||||||
|
$this->codes = $codes;
|
||||||
|
$this->patterns = array_flip($codes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function match(string &$pattern): ?int
|
||||||
|
{
|
||||||
|
for ($l = 1; $l <= strlen($pattern); $l++) {
|
||||||
|
$t = substr($pattern, 0, $l);
|
||||||
|
if (array_key_exists($t, $this->patterns)) {
|
||||||
|
$pattern = substr($pattern, strlen($t));
|
||||||
|
return $this->patterns[$t];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pattern(int $code): string
|
||||||
|
{
|
||||||
|
return $this->codes[$code];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function createRfc7541Dictionary(): Dictionary
|
||||||
|
{
|
||||||
|
return new Dictionary(array (
|
||||||
|
0 => '1111111111000',
|
||||||
|
1 => '11111111111111111011000',
|
||||||
|
2 => '1111111111111111111111100010',
|
||||||
|
3 => '1111111111111111111111100011',
|
||||||
|
4 => '1111111111111111111111100100',
|
||||||
|
5 => '1111111111111111111111100101',
|
||||||
|
6 => '1111111111111111111111100110',
|
||||||
|
7 => '1111111111111111111111100111',
|
||||||
|
8 => '1111111111111111111111101000',
|
||||||
|
9 => '111111111111111111101010',
|
||||||
|
10 => '111111111111111111111111111100',
|
||||||
|
11 => '1111111111111111111111101001',
|
||||||
|
12 => '1111111111111111111111101010',
|
||||||
|
13 => '111111111111111111111111111101',
|
||||||
|
14 => '1111111111111111111111101011',
|
||||||
|
15 => '1111111111111111111111101100',
|
||||||
|
16 => '1111111111111111111111101101',
|
||||||
|
17 => '1111111111111111111111101110',
|
||||||
|
18 => '1111111111111111111111101111',
|
||||||
|
19 => '1111111111111111111111110000',
|
||||||
|
20 => '1111111111111111111111110001',
|
||||||
|
21 => '1111111111111111111111110010',
|
||||||
|
22 => '111111111111111111111111111110',
|
||||||
|
23 => '1111111111111111111111110011',
|
||||||
|
24 => '1111111111111111111111110100',
|
||||||
|
25 => '1111111111111111111111110101',
|
||||||
|
26 => '1111111111111111111111110110',
|
||||||
|
27 => '1111111111111111111111110111',
|
||||||
|
28 => '1111111111111111111111111000',
|
||||||
|
29 => '1111111111111111111111111001',
|
||||||
|
30 => '1111111111111111111111111010',
|
||||||
|
31 => '1111111111111111111111111011',
|
||||||
|
32 => '010100',
|
||||||
|
33 => '1111111000',
|
||||||
|
34 => '1111111001',
|
||||||
|
35 => '111111111010',
|
||||||
|
36 => '1111111111001',
|
||||||
|
37 => '010101',
|
||||||
|
38 => '11111000',
|
||||||
|
39 => '11111111010',
|
||||||
|
40 => '1111111010',
|
||||||
|
41 => '1111111011',
|
||||||
|
42 => '11111001',
|
||||||
|
43 => '11111111011',
|
||||||
|
44 => '11111010',
|
||||||
|
45 => '010110',
|
||||||
|
46 => '010111',
|
||||||
|
47 => '011000',
|
||||||
|
48 => '00000',
|
||||||
|
49 => '00001',
|
||||||
|
50 => '00010',
|
||||||
|
51 => '011001',
|
||||||
|
52 => '011010',
|
||||||
|
53 => '011011',
|
||||||
|
54 => '011100',
|
||||||
|
55 => '011101',
|
||||||
|
56 => '011110',
|
||||||
|
57 => '011111',
|
||||||
|
58 => '1011100',
|
||||||
|
59 => '11111011',
|
||||||
|
60 => '111111111111100',
|
||||||
|
61 => '100000',
|
||||||
|
62 => '111111111011',
|
||||||
|
63 => '1111111100',
|
||||||
|
64 => '1111111111010',
|
||||||
|
65 => '100001',
|
||||||
|
66 => '1011101',
|
||||||
|
67 => '1011110',
|
||||||
|
68 => '1011111',
|
||||||
|
69 => '1100000',
|
||||||
|
70 => '1100001',
|
||||||
|
71 => '1100010',
|
||||||
|
72 => '1100011',
|
||||||
|
73 => '1100100',
|
||||||
|
74 => '1100101',
|
||||||
|
75 => '1100110',
|
||||||
|
76 => '1100111',
|
||||||
|
77 => '1101000',
|
||||||
|
78 => '1101001',
|
||||||
|
79 => '1101010',
|
||||||
|
80 => '1101011',
|
||||||
|
81 => '1101100',
|
||||||
|
82 => '1101101',
|
||||||
|
83 => '1101110',
|
||||||
|
84 => '1101111',
|
||||||
|
85 => '1110000',
|
||||||
|
86 => '1110001',
|
||||||
|
87 => '1110010',
|
||||||
|
88 => '11111100',
|
||||||
|
89 => '1110011',
|
||||||
|
90 => '11111101',
|
||||||
|
91 => '1111111111011',
|
||||||
|
92 => '1111111111111110000',
|
||||||
|
93 => '1111111111100',
|
||||||
|
94 => '11111111111100',
|
||||||
|
95 => '100010',
|
||||||
|
96 => '111111111111101',
|
||||||
|
97 => '00011',
|
||||||
|
98 => '100011',
|
||||||
|
99 => '00100',
|
||||||
|
100 => '100100',
|
||||||
|
101 => '00101',
|
||||||
|
102 => '100101',
|
||||||
|
103 => '100110',
|
||||||
|
104 => '100111',
|
||||||
|
105 => '00110',
|
||||||
|
106 => '1110100',
|
||||||
|
107 => '1110101',
|
||||||
|
108 => '101000',
|
||||||
|
109 => '101001',
|
||||||
|
110 => '101010',
|
||||||
|
111 => '00111',
|
||||||
|
112 => '101011',
|
||||||
|
113 => '1110110',
|
||||||
|
114 => '101100',
|
||||||
|
115 => '01000',
|
||||||
|
116 => '01001',
|
||||||
|
117 => '101101',
|
||||||
|
118 => '1110111',
|
||||||
|
119 => '1111000',
|
||||||
|
120 => '1111001',
|
||||||
|
121 => '1111010',
|
||||||
|
122 => '1111011',
|
||||||
|
123 => '111111111111110',
|
||||||
|
124 => '11111111100',
|
||||||
|
125 => '11111111111101',
|
||||||
|
126 => '1111111111101',
|
||||||
|
127 => '1111111111111111111111111100',
|
||||||
|
128 => '11111111111111100110',
|
||||||
|
129 => '1111111111111111010010',
|
||||||
|
130 => '11111111111111100111',
|
||||||
|
131 => '11111111111111101000',
|
||||||
|
132 => '1111111111111111010011',
|
||||||
|
133 => '1111111111111111010100',
|
||||||
|
134 => '1111111111111111010101',
|
||||||
|
135 => '11111111111111111011001',
|
||||||
|
136 => '1111111111111111010110',
|
||||||
|
137 => '11111111111111111011010',
|
||||||
|
138 => '11111111111111111011011',
|
||||||
|
139 => '11111111111111111011100',
|
||||||
|
140 => '11111111111111111011101',
|
||||||
|
141 => '11111111111111111011110',
|
||||||
|
142 => '111111111111111111101011',
|
||||||
|
143 => '11111111111111111011111',
|
||||||
|
144 => '111111111111111111101100',
|
||||||
|
145 => '111111111111111111101101',
|
||||||
|
146 => '1111111111111111010111',
|
||||||
|
147 => '11111111111111111100000',
|
||||||
|
148 => '111111111111111111101110',
|
||||||
|
149 => '11111111111111111100001',
|
||||||
|
150 => '11111111111111111100010',
|
||||||
|
151 => '11111111111111111100011',
|
||||||
|
152 => '11111111111111111100100',
|
||||||
|
153 => '111111111111111011100',
|
||||||
|
154 => '1111111111111111011000',
|
||||||
|
155 => '11111111111111111100101',
|
||||||
|
156 => '1111111111111111011001',
|
||||||
|
157 => '11111111111111111100110',
|
||||||
|
158 => '11111111111111111100111',
|
||||||
|
159 => '111111111111111111101111',
|
||||||
|
160 => '1111111111111111011010',
|
||||||
|
161 => '111111111111111011101',
|
||||||
|
162 => '11111111111111101001',
|
||||||
|
163 => '1111111111111111011011',
|
||||||
|
164 => '1111111111111111011100',
|
||||||
|
165 => '11111111111111111101000',
|
||||||
|
166 => '11111111111111111101001',
|
||||||
|
167 => '111111111111111011110',
|
||||||
|
168 => '11111111111111111101010',
|
||||||
|
169 => '1111111111111111011101',
|
||||||
|
170 => '1111111111111111011110',
|
||||||
|
171 => '111111111111111111110000',
|
||||||
|
172 => '111111111111111011111',
|
||||||
|
173 => '1111111111111111011111',
|
||||||
|
174 => '11111111111111111101011',
|
||||||
|
175 => '11111111111111111101100',
|
||||||
|
176 => '111111111111111100000',
|
||||||
|
177 => '111111111111111100001',
|
||||||
|
178 => '1111111111111111100000',
|
||||||
|
179 => '111111111111111100010',
|
||||||
|
180 => '11111111111111111101101',
|
||||||
|
181 => '1111111111111111100001',
|
||||||
|
182 => '11111111111111111101110',
|
||||||
|
183 => '11111111111111111101111',
|
||||||
|
184 => '11111111111111101010',
|
||||||
|
185 => '1111111111111111100010',
|
||||||
|
186 => '1111111111111111100011',
|
||||||
|
187 => '1111111111111111100100',
|
||||||
|
188 => '11111111111111111110000',
|
||||||
|
189 => '1111111111111111100101',
|
||||||
|
190 => '1111111111111111100110',
|
||||||
|
191 => '11111111111111111110001',
|
||||||
|
192 => '11111111111111111111100000',
|
||||||
|
193 => '11111111111111111111100001',
|
||||||
|
194 => '11111111111111101011',
|
||||||
|
195 => '1111111111111110001',
|
||||||
|
196 => '1111111111111111100111',
|
||||||
|
197 => '11111111111111111110010',
|
||||||
|
198 => '1111111111111111101000',
|
||||||
|
199 => '1111111111111111111101100',
|
||||||
|
200 => '11111111111111111111100010',
|
||||||
|
201 => '11111111111111111111100011',
|
||||||
|
202 => '11111111111111111111100100',
|
||||||
|
203 => '111111111111111111111011110',
|
||||||
|
204 => '111111111111111111111011111',
|
||||||
|
205 => '11111111111111111111100101',
|
||||||
|
206 => '111111111111111111110001',
|
||||||
|
207 => '1111111111111111111101101',
|
||||||
|
208 => '1111111111111110010',
|
||||||
|
209 => '111111111111111100011',
|
||||||
|
210 => '11111111111111111111100110',
|
||||||
|
211 => '111111111111111111111100000',
|
||||||
|
212 => '111111111111111111111100001',
|
||||||
|
213 => '11111111111111111111100111',
|
||||||
|
214 => '111111111111111111111100010',
|
||||||
|
215 => '111111111111111111110010',
|
||||||
|
216 => '111111111111111100100',
|
||||||
|
217 => '111111111111111100101',
|
||||||
|
218 => '11111111111111111111101000',
|
||||||
|
219 => '11111111111111111111101001',
|
||||||
|
220 => '1111111111111111111111111101',
|
||||||
|
221 => '111111111111111111111100011',
|
||||||
|
222 => '111111111111111111111100100',
|
||||||
|
223 => '111111111111111111111100101',
|
||||||
|
224 => '11111111111111101100',
|
||||||
|
225 => '111111111111111111110011',
|
||||||
|
226 => '11111111111111101101',
|
||||||
|
227 => '111111111111111100110',
|
||||||
|
228 => '1111111111111111101001',
|
||||||
|
229 => '111111111111111100111',
|
||||||
|
230 => '111111111111111101000',
|
||||||
|
231 => '11111111111111111110011',
|
||||||
|
232 => '1111111111111111101010',
|
||||||
|
233 => '1111111111111111101011',
|
||||||
|
234 => '1111111111111111111101110',
|
||||||
|
235 => '1111111111111111111101111',
|
||||||
|
236 => '111111111111111111110100',
|
||||||
|
237 => '111111111111111111110101',
|
||||||
|
238 => '11111111111111111111101010',
|
||||||
|
239 => '11111111111111111110100',
|
||||||
|
240 => '11111111111111111111101011',
|
||||||
|
241 => '111111111111111111111100110',
|
||||||
|
242 => '11111111111111111111101100',
|
||||||
|
243 => '11111111111111111111101101',
|
||||||
|
244 => '111111111111111111111100111',
|
||||||
|
245 => '111111111111111111111101000',
|
||||||
|
246 => '111111111111111111111101001',
|
||||||
|
247 => '111111111111111111111101010',
|
||||||
|
248 => '111111111111111111111101011',
|
||||||
|
249 => '1111111111111111111111111110',
|
||||||
|
250 => '111111111111111111111101100',
|
||||||
|
251 => '111111111111111111111101101',
|
||||||
|
252 => '111111111111111111111101110',
|
||||||
|
253 => '111111111111111111111101111',
|
||||||
|
254 => '111111111111111111111110000',
|
||||||
|
255 => '11111111111111111111101110',
|
||||||
|
256 => '111111111111111111111111111111',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
30
tests/Header/HeaderPackerTest.php
Normal file
30
tests/Header/HeaderPackerTest.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NoccyLabs\React\Http2\Header;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\Attributes\CoversClass;
|
||||||
|
|
||||||
|
#[CoversClass(HeaderBag::class)]
|
||||||
|
#[CoversClass(HeaderPacker::class)]
|
||||||
|
class HeaderPackerTest extends \PHPUnit\Framework\TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
public function testUnpackingExamplesFromRfc7541()
|
||||||
|
{
|
||||||
|
$packer = new HeaderPacker();
|
||||||
|
|
||||||
|
$in = "\x82\x86\x84\x41\x8c\xf1\xe3\xc2\xe5\xf2\x3a\x6b\xa0\xab\x90\xf4\xff";
|
||||||
|
$expect = new HeaderBag([
|
||||||
|
':method' => 'GET',
|
||||||
|
':scheme' => 'http',
|
||||||
|
':path' => '/',
|
||||||
|
':authority' => 'www.example.com'
|
||||||
|
]);
|
||||||
|
$out = $packer->unpackHeaders($in);
|
||||||
|
|
||||||
|
$this->assertEquals($expect, $out);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
38
tests/Huffman/CodecTest.php
Normal file
38
tests/Huffman/CodecTest.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NoccyLabs\React\Http2\Huffman;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\Attributes\CoversClass;
|
||||||
|
|
||||||
|
#[CoversClass(Codec::class)]
|
||||||
|
#[CoversClass(Dictionary::class)]
|
||||||
|
class CodecTest extends \PHPUnit\Framework\TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
public function testDecodingExamplesFromRfc7541()
|
||||||
|
{
|
||||||
|
$dict = Dictionary::createRfc7541Dictionary();
|
||||||
|
$huff = new Codec($dict);
|
||||||
|
|
||||||
|
$in = "\xf1\xe3\xc2\xe5\xf2\x3a\x6b\xa0\xab\x90\xf4\xff";
|
||||||
|
$expect = "www.example.com";
|
||||||
|
$out = $huff->decode($in);
|
||||||
|
|
||||||
|
$this->assertEquals($expect, $out);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEncodingExamplesFromRfc7541()
|
||||||
|
{
|
||||||
|
$dict = Dictionary::createRfc7541Dictionary();
|
||||||
|
$huff = new Codec($dict);
|
||||||
|
|
||||||
|
$in = "www.example.com";
|
||||||
|
$expect = "\xf1\xe3\xc2\xe5\xf2\x3a\x6b\xa0\xab\x90\xf4\xff";
|
||||||
|
$out = $huff->encode($in);
|
||||||
|
|
||||||
|
$this->assertEquals($expect, $out);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user