amphp/hpack
Fast HPACK (HTTP/2 header compression) implementation for PHP by amphp. Provides efficient encoding/decoding of header blocks with dynamic tables, Huffman coding, and compliance-focused behavior, suitable for high-performance HTTP/2 clients and servers.
Installation:
composer require amphp/hpack
Ensure PHP ≥ 7.4 (PHP 8.x recommended).
First Use Case:
Encode/decode HTTP/2 headers for a custom HTTP/2 server or client (e.g., using amphp/http-server).
use Amp\Hpack\Encoder;
use Amp\Hpack\Decoder;
$encoder = new Encoder();
$decoder = new Decoder();
// Encode headers (e.g., from Laravel's request)
$headers = [
[':method', 'GET'],
[':path', '/api/users'],
['content-type', 'application/json'],
];
$encoded = $encoder->encode($headers); // Binary string
// Decode headers (e.g., for a custom response)
$decoded = $decoder->decode($encoded); // Array of [name, value] pairs
Where to Look First:
Encoder (compression) and Decoder (decompression) in src/Encoder.php and src/Decoder.php.tableSizeLimit (default: 4096 bytes).examples/ directory for basic usage (e.g., roundtrip.php).tests/ for edge cases (e.g., RfcTest.php for RFC 7541 compliance).Per-Connection State:
Reuse Encoder/Decoder instances for the lifetime of an HTTP/2 connection (HPACK’s dynamic table is stateful).
// In a custom HTTP/2 server loop
$encoder = new Encoder();
$decoder = new Decoder();
foreach ($requests as $request) {
$encodedHeaders = $encoder->encode($request->headers);
// Send $encodedHeaders via HTTP/2 frame...
}
Integration with Async I/O:
Use with amphp/socket or ReactPHP for async HTTP/2 servers/clients. Example with amphp/http-server:
use Amp\Http\Server\Request;
use Amp\Http\Server\Response;
$server = new Server('127.0.0.1', 8080);
$encoder = new Encoder();
$server->on('request', function (Request $request) use ($encoder) {
$response = new Response();
$response->setHeader(':status', '200');
$response->setHeader('content-type', 'text/plain');
$encodedHeaders = $encoder->encode($response->getHeaders());
// Write $encodedHeaders to HTTP/2 frame...
});
Dynamic Table Management:
new Decoder(64 * 1024) for 64KB limit).SETTINGS_HEADER_TABLE_SIZE frames by calling decoder->setTableSizeLimit().
$decoder->setTableSizeLimit(1024); // Update after receiving SETTINGS frame
Header Normalization:
HPACK requires headers to be lowercase and canonicalized (e.g., :method instead of :Method). Use Laravel’s Str::lower() or HeaderUtils for preprocessing:
$normalizedHeaders = array_map(
fn(array $header) => [strtolower($header[0]), $header[1]],
$headers
);
Testing:
Validate against RFC 7541 test vectors (included in tests/RfcTest.php). Example:
$this->assertEquals(
$decoder->decode($encoder->encode($headers)),
$headers
);
amphp/http-client), integrate HPACK after Laravel’s default stack:
// In a custom HTTP client (not Laravel's Guzzle)
$client = new AmpHttpClient();
$encoder = new Encoder();
$response = $client->request('GET', 'https://api.example.com', [
'headers' => $encoder->encode([[':authority', 'api.example.com']]),
]);
Stateful Dynamic Table:
Decoder’s table size after receiving a SETTINGS_HEADER_TABLE_SIZE frame causes corruption.decoder->setTableSizeLimit() when processing settings frames.
// Pseudocode for HTTP/2 frame handler
if ($frame->type === SETTINGS) {
$decoder->setTableSizeLimit($frame->headerTableSize);
}
Header Name Mismatches:
:Method vs :method). Laravel’s Request headers may not be normalized.$hpackHeaders = array_map(
fn(array $header) => [strtolower($header[0]), $header[1]],
$request->header->all()
);
Binary Safety:
decode() throws Amp\Http\Server\HttpException on malformed input (e.g., truncated Huffman codes).try {
$decoded = $decoder->decode($binaryData);
} catch (HttpException $e) {
\Log::error("HPACK decode failed", ['binary' => bin2hex($binaryData)]);
throw $e;
}
Performance Overhead:
compressionThreshold):
$encoder = new Encoder(compressionThreshold: 2048); // Only compress >2KB headers
FFI Dependencies:
HPackNghttp2 class (optional) requires libnghttp2 and FFI. Missing dependencies cause failures.libnghttp2 via system package manager (e.g., apt-get install libnghttp2-dev) or avoid FFI by using the pure-PHP implementation.Compare with Wireshark: Capture HTTP/2 traffic and compare HPACK-encoded headers with Wireshark’s "HPACK" dissector to spot mismatches.
Enable Debug Logging:
Use HPACK_DEBUG (if available) or log raw binary:
\Log::debug('Encoded headers', ['hex' => bin2hex($encodedHeaders)]);
Test Edge Cases:
[] → Should encode to empty binary.compressionThreshold.content-type headers become one).Custom Header Handling:
Extend Encoder/Decoder to modify headers before/after compression:
class CustomEncoder extends Encoder {
public function encode(array $headers): string {
// Redact sensitive headers
$headers = array_filter($headers, fn(array $h) => $h[0] !== 'authorization');
return parent::encode($headers);
}
}
Static Table Overrides: Replace the default static table (RFC 7541 § 4.2) for custom headers:
$customTable = new StaticTable([
[':custom-header', 'default-value'],
]);
$encoder = new Encoder(staticTable: $customTable);
Huffman Encoding:
Override Huffman logic (e.g., for custom Huffman tables) by extending HuffmanEncoder/HuffmanDecoder.
amphp/ReactPHP). Use only in custom async HTTP servers.How can I help you explore Laravel packages today?