NAME

Net::HTTP2::nghttp2::Session - HTTP/2 session management

SYNOPSIS

use Net::HTTP2::nghttp2::Session;

my $session = Net::HTTP2::nghttp2::Session->new_server(
    callbacks => {
        on_begin_headers => sub {
            my ($session, $stream_id) = @_;
            # New stream started
        },
        on_header => sub {
            my ($session, $stream_id, $name, $value, $flags) = @_;
            # Header received
        },
        on_frame_recv => sub {
            my ($session, $frame) = @_;
            # Frame received
        },
        on_stream_close => sub {
            my ($session, $stream_id, $error_code) = @_;
            # Stream closed
        },
        on_data_chunk_recv => sub {
            my ($session, $stream_id, $data, $flags) = @_;
            # Body data received
        },
    },
);

# Send connection preface
$session->send_connection_preface(
    max_concurrent_streams => 100,
);

# Process incoming data
$session->mem_recv($incoming_bytes);

# Get outgoing data to send
my $outgoing = $session->mem_send();

# Submit a response
$session->submit_response($stream_id,
    status  => 200,
    headers => [
        ['content-type', 'text/html'],
    ],
    body => '<html>...</html>',
);

METHODS

new_server

my $session = Net::HTTP2::nghttp2::Session->new_server(%args);

Create a new server-side HTTP/2 session.

Arguments:

callbacks

Hashref of callback handlers. Required callbacks: on_begin_headers, on_header, on_frame_recv. Optional: on_data_chunk_recv, on_stream_close.

user_data

Optional scalar passed to callbacks.

settings

Optional hashref of initial HTTP/2 settings.

stream_reset_burst / stream_reset_rate

Configure the incoming-RST_STREAM rate limit (nghttp2's HTTP/2 Rapid Reset, CVE-2023-44487, mitigation). Both must be given together. They map to nghttp2_option_set_stream_reset_rate_limit(option, burst, rate): a token bucket of burst tokens refilling at rate tokens/second, one token per incoming RST_STREAM. When the bucket is empty nghttp2 sends GOAWAY and tears down the connection. Omit both to use nghttp2's own defaults (burst 1000, rate 33). Requires nghttp2 >= 1.57.

new_client

my $session = Net::HTTP2::nghttp2::Session->new_client(%args);

Create a new client-side HTTP/2 session.

Arguments:

callbacks

Hashref of callback handlers. Recommended: on_header, on_data_chunk_recv, on_stream_close.

user_data

Optional scalar passed to callbacks.

send_connection_preface

$session->send_connection_preface(%settings);

Send HTTP/2 connection preface (SETTINGS frame). Default settings: max_concurrent_streams => 100, initial_window_size => 65535.

Additional settings:

enable_connect_protocol

Set to 1 to advertise RFC 8441 extended CONNECT support (SETTINGS_ENABLE_CONNECT_PROTOCOL). Required for WebSocket over HTTP/2.

mem_recv

my $consumed = $session->mem_recv($data);

Feed incoming data to the session. Returns number of bytes consumed. Triggers registered callbacks as frames are parsed.

mem_send

my $data = $session->mem_send();

Get outgoing data from the session. Returns bytes to send to peer (empty string if nothing pending).

submit_request

my $stream_id = $session->submit_request(%args);

Submit an HTTP/2 request (client-side). Returns the stream ID.

Arguments:

method

HTTP method. Default: 'GET'.

path

Request path. Default: '/'.

scheme

URL scheme. Default: 'https'.

authority

Host authority (e.g. 'example.com').

headers

Arrayref of [$name, $value] pairs for additional headers (including pseudo-headers like :protocol for RFC 8441 extended CONNECT).

body

Request body. Can be:

undef (or omitted)

No body. HEADERS frame sent with END_STREAM.

String

Static body. Sent as DATA frame(s) with END_STREAM after the last frame.

CODE ref

Streaming callback for bidirectional streams. The callback receives ($stream_id, $max_length) and must return one of:

($data, $eof_flag)

Send data. A true EOF ends the stream, preserving pre-0.009 behavior.

($data, $eof_flag, $no_end_stream)

Send data. When both flags are true, content production is complete but DATA does not carry END_STREAM, allowing submit_trailer() to queue the terminal HEADERS block. The third value has no effect unless EOF is true.

undef or an empty list

Defer production until the stream is resumed or submit_data() is called.

This is required for protocols that keep the stream open for bidirectional exchange, such as WebSocket over HTTP/2 (RFC 8441 extended CONNECT).

submit_response

$session->submit_response($stream_id, %args);

Submit an HTTP/2 response on the given stream.

Arguments:

status

HTTP status code. Default: 200.

headers

Arrayref of [$name, $value] pairs.

body

Response body. Same types as submit_request: undef (no body), string (static body), or CODE ref (streaming callback).

undef (or omitted)

No body. HEADERS frame sent with END_STREAM.

String

Static body. Sent as DATA frame(s) with END_STREAM after the last frame.

CODE ref

Streaming callback. It receives ($stream_id, $max_length), or ($stream_id, $max_length, $user_data) when callback_data is defined, and must return one of:

($data, $eof_flag)

Send data. A true EOF ends the stream, preserving pre-0.009 behavior.

($data, $eof_flag, $no_end_stream)

Send data. When both flags are true, content production is complete but DATA does not carry END_STREAM, allowing submit_trailer() to queue the terminal HEADERS block. The third value has no effect unless EOF is true.

undef or an empty list

Defer production until the stream is resumed or submit_data() is called.

data_callback

Alternative to passing a CODE ref as body. Callback with the same streaming signature.

callback_data

Optional user data passed as third argument to the streaming callback.

submit_trailer

$session->submit_trailer(
    $stream_id,
    headers => [
        ['x-checksum', 'abc'],
        ['set-cookie', 'a=1'],
        ['set-cookie', 'b=2'],
    ],
);

Queue a trailing HEADERS block that ends the stream. headers defaults to an empty array reference; order and duplicate names are preserved. Trailer names must be ordinary field names, not pseudo-header names beginning with :.

When trailers follow, the data provider must ultimately report EOF together with NO_END_STREAM, using ($data, 1, 1) or submit_data($stream_id, $data, 1, 1), so the final DATA does not consume END_STREAM. submit_trailer may be called inside the data callback or after that callback returns. Callers that do not send trailers should use the legacy two-value ($data, $eof_flag) callback or three-argument submit_data($stream_id, $data, $eof) form, where a true EOF retains the normal DATA END_STREAM behavior. An empty headers list queues an empty terminal HEADERS block.

A zero return means nghttp2 accepted the trailer block into its outbound queue. It does not mean the peer has received it. Invalid Perl input and immediate nghttp2 submission errors throw exceptions.

submit_push_promise

my $promised_stream_id = $session->submit_push_promise($stream_id, %args);

Submit a server push promise.

submit_data

$session->submit_data($stream_id, $data, $eof, $no_end_stream);

Push data directly onto an existing stream. The stream must already have a data provider (established by submit_request or submit_response with a CODE ref or data_callback). This replaces the streaming callback with a one-shot static body, then resumes the stream.

Arguments:

$stream_id

The stream to send data on.

$data

The data to send. Can be undef for an empty DATA frame.

$eof

If true, the DATA frame will include END_STREAM, closing the stream.

$no_end_stream

Optional and false by default. When true together with a true $eof, content production is complete but DATA does not include END_STREAM, allowing submit_trailer() to queue the terminal HEADERS block. It has no effect unless $eof is true. The three-argument form retains its existing behavior: a true $eof ends the stream on DATA.

This is useful when you have data available outside the streaming callback context and want to push it directly, such as forwarding WebSocket frames received from another source.

resume_stream

$session->resume_stream($stream_id);

Resume data production for a deferred stream. Call this after a streaming body callback has returned undef and new data is available. Works for both request and response streams.

terminate_session

$session->terminate_session($error_code);

Send a GOAWAY frame and terminate the session. The $error_code should be an HTTP/2 wire error code (0 for NGHTTP2_NO_ERROR).

submit_rst_stream

$session->submit_rst_stream($stream_id, $error_code);

Send a RST_STREAM frame to abnormally terminate a stream. The $error_code should be an HTTP/2 error code (e.g. 0 for NO_ERROR, 8 for CANCEL).

submit_ping

$session->submit_ping($ack, $opaque_data);

Send a PING frame. Set $ack to 1 for a PING ACK response, 0 for an unsolicited PING. $opaque_data must be exactly 8 bytes, or undef for default.

submit_window_update

$session->submit_window_update($stream_id, $window_size_increment);

Send a WINDOW_UPDATE frame to increase the flow control window. Use $stream_id = 0 for connection-level flow control, or a specific stream ID for stream-level.

get_stream_user_data

my $data = $session->get_stream_user_data($stream_id);

Retrieve user data associated with a stream. Returns undef if no data is set.

set_stream_user_data

$session->set_stream_user_data($stream_id, $data);

Associate arbitrary user data with a stream. Useful for storing per-stream application state.

is_stream_deferred

my $bool = $session->is_stream_deferred($stream_id);

Returns true if the stream's data provider has been deferred (i.e. the streaming callback returned undef). The stream can be resumed with resume_stream().

want_read

my $bool = $session->want_read();

Returns true if the session wants to read more data.

want_write

my $bool = $session->want_write();

Returns true if the session has data to write.

resume_data

$session->resume_data($stream_id);

Low-level resume for deferred data production. Prefer resume_stream() which also clears the internal deferred flag.

CALLBACKS

All callbacks receive positional arguments and should return 0 on success.

on_begin_headers

sub { my ($stream_id, $frame_type, $flags) = @_; return 0; }

Called when a new headers block begins (new stream or trailers).

on_header

sub { my ($stream_id, $name, $value, $flags) = @_; return 0; }

Called for each header. Pseudo-headers (:method, :path, :scheme, :authority, :status, :protocol) are delivered before regular headers.

on_frame_recv

sub { my ($frame_hashref) = @_; return 0; }

Called when a complete frame is received. The hashref contains: type, flags, stream_id, length. A HEADERS frame additionally contains headers_category, one of:

NGHTTP2_HCAT_REQUEST

Initial request headers.

NGHTTP2_HCAT_RESPONSE

Initial response headers.

NGHTTP2_HCAT_PUSH_RESPONSE

Pushed response headers.

NGHTTP2_HCAT_HEADERS

A later ordinary HEADERS block on an open stream.

NGHTTP2_HCAT_HEADERS is not a universal is_trailer boolean: informational responses and message direction/state remain relevant when interpreting a later headers block.

on_data_chunk_recv

sub { my ($stream_id, $data, $flags) = @_; return 0; }

Called when body data is received on a stream.

on_stream_close

sub { my ($stream_id, $error_code) = @_; return 0; }

Called when a stream is closed.