Net-HTTP2-nghttp2
view release on metacpan or search on metacpan
lib/Net/HTTP2/nghttp2.pm view on Meta::CPAN
},
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";
=head2 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);
=head2 Streaming Request
Client-side requests also support streaming body callbacks, using the same
callback signature as streaming responses. This is essential for bidirectional
protocols like WebSocket over HTTP/2 (RFC 8441), where the client stream must
remain open for ongoing data exchange.
my @send_queue;
my $eof = 0;
my $stream_id = $session->submit_request(
method => 'POST',
path => '/upload',
scheme => 'https',
authority => 'example.com',
body => sub {
my ($stream_id, $max_length) = @_;
if (@send_queue) {
my $chunk = shift @send_queue;
return ($chunk, $eof);
}
return undef; # Defer until data available
},
);
# Later, queue data and resume:
push @send_queue, $data;
$session->resume_stream($stream_id);
=head2 WebSocket over HTTP/2 (RFC 8441)
RFC 8441 defines how to bootstrap WebSocket connections over HTTP/2 using the
extended CONNECT method. The server advertises support via
C<SETTINGS_ENABLE_CONNECT_PROTOCOL>, and the client sends an extended CONNECT
request with a C<:protocol> pseudo-header set to C<websocket>.
B<Server side> - enable the setting and detect extended CONNECT:
$session->send_connection_preface(
enable_connect_protocol => 1,
);
# In on_header callback, look for :protocol pseudo-header:
on_header => sub {
my ($stream_id, $name, $value) = @_;
if ($name eq ':method' && $value eq 'CONNECT') {
# Possible extended CONNECT
}
if ($name eq ':protocol' && $value eq 'websocket') {
# This is a WebSocket upgrade request
}
return 0;
},
# Accept with 200 (not 101) and a streaming body for server-to-client:
$session->submit_response($stream_id,
status => 200,
headers => [['sec-websocket-protocol', $subprotocol]],
body => sub {
my ($stream_id, $max_length) = @_;
# Return WebSocket frames to send to client
return undef; # Defer until data ready
},
);
B<Client side> - send extended CONNECT with streaming body:
my $stream_id = $session->submit_request(
method => 'CONNECT',
path => '/chat',
scheme => 'https',
authority => 'example.com',
headers => [[':protocol', 'websocket']],
body => sub {
my ($stream_id, $max_length) = @_;
# Return WebSocket frames to send to server
return undef; # Defer until data ready
},
);
WebSocket frames (RFC 6455) are carried inside HTTP/2 DATA frames on the
stream. Both directions remain open until one side sends a DATA frame with
END_STREAM (by returning C<($data, 1)> from the callback).
=head1 CONFORMANCE TESTING
This module has been tested against h2spec (L<https://github.com/summerwind/h2spec>),
the HTTP/2 conformance testing tool.
=head2 h2spec Results
146 tests, 137 passed, 1 skipped, 8 failed (94% pass rate)
=head2 Passing Test Categories
=over 4
=item * Starting HTTP/2 - Connection preface handling
=item * Streams and Multiplexing - Stream state management
=item * Frame Definitions - DATA, HEADERS, PRIORITY, RST_STREAM, SETTINGS, PING, GOAWAY, WINDOW_UPDATE, CONTINUATION
=item * HTTP Message Exchanges - GET, HEAD, POST requests with trailers
=item * HPACK - All header compression variants (indexed, literal, Huffman)
=item * Server Push - PUSH_PROMISE handling
=back
=head2 Known Limitations
The 8 failing tests are edge cases where nghttp2 intentionally chooses lenient
behavior over strict RFC compliance for better interoperability:
=over 4
=item * Invalid connection preface - nghttp2 sends SETTINGS before validating
=item * DATA/HEADERS on closed streams - Silently ignored rather than erroring
=item * Out-of-order stream identifiers - Accepted (lenient parsing)
=item * PRIORITY self-dependency - Ignored rather than treated as error
( run in 1.211 second using v1.01-cache-2.11-cpan-140bd7fdf52 )