NAME
Net::HTTP2::nghttp2 - Perl XS bindings for 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;
# See EXAMPLES below for complete server and client examples
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).
CLASS METHODS
available
my $bool = Net::HTTP2::nghttp2->available;
Returns true if nghttp2 is available and properly linked.
CONSTANTS
Error Codes
- NGHTTP2_ERR_WOULDBLOCK
-
Operation would block (non-fatal).
- NGHTTP2_ERR_CALLBACK_FAILURE
-
Callback returned an error.
- NGHTTP2_ERR_DEFERRED
-
Data production deferred (for flow control).
Frame Flags
- NGHTTP2_FLAG_END_STREAM
-
End of stream flag.
- NGHTTP2_FLAG_END_HEADERS
-
End of headers flag.
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) = @_;
# New request starting
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;
},
on_stream_close => sub {
my ($stream_id, $error_code) = @_;
return 0;
},
},
);
$session->send_connection_preface();
# I/O loop
while ($session->want_read || $session->want_write) {
# Send pending data
if (my $out = $session->mem_send) {
$client->syswrite($out);
}
# Read incoming data
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;
# Connect with ALPN to negotiate HTTP/2
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 {
my ($stream_id, $error_code) = @_;
$response{done} = 1;
return 0;
},
},
);
$session->send_connection_preface();
# Submit GET request
my $stream_id = $session->submit_request(
method => 'GET',
path => '/',
scheme => 'https',
authority => 'nghttp2.org',
headers => [['user-agent', 'perl-nghttp2/0.001']],
);
# I/O loop
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";
print "Body: " . length($response{body}) . " bytes\n";
Streaming Response
# In on_frame_recv callback, use a data provider for large responses:
$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);
},
);
# Return undef to defer, then call resume_stream() when data ready:
body => sub {
my ($stream_id, $max_length) = @_;
return undef if !data_available(); # Defers
return (get_data(), $eof);
},
# Later, when data becomes available:
$session->resume_stream($stream_id);
CONFORMANCE TESTING
This module has been tested against h2spec (https://github.com/summerwind/h2spec), the HTTP/2 conformance testing tool.
h2spec Results
146 tests, 137 passed, 1 skipped, 8 failed (94% pass rate)
Passing Test Categories
Starting HTTP/2 - Connection preface handling
Streams and Multiplexing - Stream state management
Frame Definitions - DATA, HEADERS, PRIORITY, RST_STREAM, SETTINGS, PING, GOAWAY, WINDOW_UPDATE, CONTINUATION
HTTP Message Exchanges - GET, HEAD, POST requests with trailers
HPACK - All header compression variants (indexed, literal, Huffman)
Server Push - PUSH_PROMISE handling
Known Limitations
The 8 failing tests are edge cases where nghttp2 intentionally chooses lenient behavior over strict RFC compliance for better interoperability:
Invalid connection preface - nghttp2 sends SETTINGS before validating
DATA/HEADERS on closed streams - Silently ignored rather than erroring
Out-of-order stream identifiers - Accepted (lenient parsing)
PRIORITY self-dependency - Ignored rather than treated as error
PRIORITY on stream 0 - Silently ignored
This lenient behavior is intentional in nghttp2 and matches the behavior of production HTTP/2 implementations like curl, Apache, and nginx.
SEE ALSO
Alien::nghttp2 - Alien module for automatic nghttp2 installation
https://nghttp2.org/ - nghttp2 project homepage
https://datatracker.ietf.org/doc/html/rfc9113 - HTTP/2 RFC
https://github.com/summerwind/h2spec - HTTP/2 conformance testing tool
AUTHOR
Your Name <your@email.com>
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.