Net::HTTP2::nghttp2
Perl XS bindings for the nghttp2 HTTP/2 library.
Synopsis
use Net::HTTP2::nghttp2;
use Net::HTTP2::nghttp2::Session;
# Check availability
die "nghttp2 not available" unless Net::HTTP2::nghttp2->available;
# Create an HTTP/2 server session
my $session = Net::HTTP2::nghttp2::Session->new_server(
callbacks => {
on_begin_headers => sub { ... },
on_header => sub { ... },
on_frame_recv => sub { ... },
on_data_chunk_recv => sub { ... },
on_stream_close => sub { ... },
},
);
# Or create a client session
my $session = Net::HTTP2::nghttp2::Session->new_client(
callbacks => { ... },
);
Description
This module provides Perl bindings for the nghttp2 C library, enabling HTTP/2 protocol support in Perl applications.
nghttp2 is one of the most mature HTTP/2 implementations, used by curl, Apache, and Firefox. It implements RFC 9113 (HTTP/2) and RFC 7541 (HPACK).
Features
- Full HTTP/2 support - DATA, HEADERS, PRIORITY, RST_STREAM, SETTINGS, PUSH_PROMISE, PING, GOAWAY, WINDOW_UPDATE, CONTINUATION frames
- HPACK header compression - Efficient header encoding/decoding
- Flow control - Automatic and manual flow control management
- Stream multiplexing - Multiple concurrent streams over a single connection
- Server and client modes - Build HTTP/2 servers or clients
- Streaming responses - Data provider callbacks for large/dynamic responses
- RFC 8441 support - Extended CONNECT for WebSocket bootstrapping over HTTP/2
Installation
cpanm Net::HTTP2::nghttp2
The module uses Alien::nghttp2 to automatically install the nghttp2 library if not present on your system.
Manual installation
perl Makefile.PL
make
make test
make install
Examples
HTTP/2 Server (h2c - cleartext)
use IO::Socket::INET;
use Net::HTTP2::nghttp2;
use Net::HTTP2::nghttp2::Session;
my $server = IO::Socket::INET->new(
LocalPort => 8080,
Listen => 128,
ReuseAddr => 1,
) or die "Cannot create server: $!";
while (my $client = $server->accept) {
my $session = Net::HTTP2::nghttp2::Session->new_server(
callbacks => {
on_begin_headers => sub {
my ($stream_id) = @_;
return 0;
},
on_header => sub {
my ($stream_id, $name, $value) = @_;
# Received header: :method, :path, :scheme, etc.
return 0;
},
on_frame_recv => sub {
my ($frame) = @_;
# HEADERS frame with END_STREAM = complete request
if ($frame->{type} == 1 && ($frame->{flags} & 0x1)) {
$session->submit_response($frame->{stream_id},
status => 200,
headers => [['content-type', 'text/plain']],
body => "Hello, HTTP/2!\n",
);
}
return 0;
},
},
);
$session->send_connection_preface();
while ($session->want_read || $session->want_write) {
if (my $out = $session->mem_send) {
$client->syswrite($out);
}
my $buf;
last unless $client->sysread($buf, 16384);
$session->mem_recv($buf);
}
$client->close;
}
HTTP/2 Client (h2 - TLS)
use IO::Socket::SSL;
use Net::HTTP2::nghttp2;
use Net::HTTP2::nghttp2::Session;
my $sock = IO::Socket::SSL->new(
PeerHost => 'nghttp2.org',
PeerPort => 443,
SSL_alpn_protocols => ['h2'],
) or die "Connection failed: $!";
die "ALPN failed" unless $sock->alpn_selected eq 'h2';
my %response;
my $session = Net::HTTP2::nghttp2::Session->new_client(
callbacks => {
on_header => sub {
my ($stream_id, $name, $value) = @_;
$response{headers}{$name} = $value;
return 0;
},
on_data_chunk_recv => sub {
my ($stream_id, $data) = @_;
$response{body} .= $data;
return 0;
},
on_stream_close => sub {
$response{done} = 1;
return 0;
},
},
);
$session->send_connection_preface();
my $stream_id = $session->submit_request(
method => 'GET',
path => '/',
scheme => 'https',
authority => 'nghttp2.org',
);
while (!$response{done} && ($session->want_read || $session->want_write)) {
if (my $out = $session->mem_send) {
$sock->syswrite($out);
}
my $buf;
last unless $sock->sysread($buf, 16384);
$session->mem_recv($buf);
}
print "Status: $response{headers}{':status'}\n";
Streaming Response
$session->submit_response($stream_id,
status => 200,
headers => [['content-type', 'application/octet-stream']],
body => sub {
my ($stream_id, $max_length) = @_;
my $chunk = get_next_chunk($max_length);
my $eof = is_last_chunk() ? 1 : 0;
return ($chunk, $eof);
},
);
RFC 8441 - WebSocket over HTTP/2
my $session = Net::HTTP2::nghttp2::Session->new_server(
callbacks => { ... },
settings => {
enable_connect_protocol => 1, # Advertise RFC 8441 support
},
);
Conformance Testing
This module has been tested against h2spec, the HTTP/2 conformance testing tool.
h2spec Results
146 tests, 137 passed, 1 skipped, 8 failed (94% pass rate)
The 8 failing tests are edge cases where nghttp2 intentionally chooses lenient behavior over strict RFC compliance for better interoperability. This matches the behavior of production implementations like curl, Apache, and nginx.
Running h2spec
# Start the test server
perl bin/h2spec-server --port 8080
# In another terminal
h2spec -h localhost -p 8080
Constants
Error Codes
NGHTTP2_ERR_WOULDBLOCK- Operation would blockNGHTTP2_ERR_CALLBACK_FAILURE- Callback returned an errorNGHTTP2_ERR_DEFERRED- Data production deferred
Frame Flags
NGHTTP2_FLAG_END_STREAM- End of streamNGHTTP2_FLAG_END_HEADERS- End of headersNGHTTP2_FLAG_ACK- AcknowledgmentNGHTTP2_FLAG_PADDED- Frame is paddedNGHTTP2_FLAG_PRIORITY- Priority information present
Settings
NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMSNGHTTP2_SETTINGS_INITIAL_WINDOW_SIZENGHTTP2_SETTINGS_MAX_FRAME_SIZENGHTTP2_SETTINGS_ENABLE_PUSHNGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL- RFC 8441
See Also
- Alien::nghttp2 - Alien module for nghttp2
- nghttp2.org - nghttp2 project homepage
- RFC 9113 - HTTP/2
- RFC 7541 - HPACK
- RFC 8441 - Bootstrapping WebSockets with HTTP/2
Author
John Napiorkowski jjnapiork@cpan.org
License
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.