Protoc...:Client(3pm)

πŸ’– Protocol::HTTP2::Client β€” the manual, but make it cute πŸ’–

okay bestie so this is Protocol::HTTP2::Client(3pm), from the User Contributed Perl Documentation section of the manual. It's a whole HTTP/2 client library written in Perl and it is lowkey iconic. Let's get into it. πŸ¦‹


✨ NAME ✨

Protocol::HTTP2::Client β€” the HTTP/2 client. yes. that's the whole name. slay. πŸ’…


🌸 SYNOPSIS 🌸

here's the whole vibe in one code block, copied exactly because I would never mess with your code, bestie:

use Protocol::HTTP2::Client;

# Create client object
my $client = Protocol::HTTP2::Client->new;

# Prepare first request
$client->request(

    # HTTP/2 headers
    ':scheme'    => 'http',
    ':authority' => 'localhost:8000',
    ':path'      => '/',
    ':method'    => 'GET',

    # HTTP/1.1 headers
    headers      => [
        'accept'     => '*/*',
        'user-agent' => 'perl-Protocol-HTTP2/0.13',
    ],

    # Callback when receive server's response
    on_done => sub {
        my ( $headers, $data ) = @_;
        ...
    },
);

# Protocol::HTTP2 is just HTTP/2 protocol decoder/encoder
# so you must create connection yourself

use AnyEvent;
use AnyEvent::Socket;
use AnyEvent::Handle;
my $w = AnyEvent->condvar;

# Plain-text HTTP/2 connection
tcp_connect 'localhost', 8000, sub {
    my ($fh) = @_ or die "connection failed: $!\n";

    my $handle;
    $handle = AnyEvent::Handle->new(
        fh       => $fh,
        autocork => 1,
        on_error => sub {
            $_[0]->destroy;
            print "connection error\n";
            $w->send;
        },
        on_eof => sub {
            $handle->destroy;
            $w->send;
        }
    );

    # First write preface to peer
    while ( my $frame = $client->next_frame ) {
        $handle->push_write($frame);
    }

    # Receive servers frames
    # Reply to server
    $handle->on_read(
        sub {
            my $handle = shift;

            $client->feed( $handle->{rbuf} );

            $handle->{rbuf} = undef;
            while ( my $frame = $client->next_frame ) {
                $handle->push_write($frame);
            }

            # Terminate connection if all done
            $handle->push_shutdown if $client->shutdown;
        }
    );
};

$w->recv;

πŸŽ€ DESCRIPTION πŸŽ€

Protocol::HTTP2::Client is an HTTP/2 client library, periodt. The whole point of it is to let you build http2-client implementations on top of your favorite event-loop. your loop, your rules. main character energy fr fr. πŸ‘‘

🧚 METHODS 🧚

new

Initialize a brand new client object:

my $client = Protocol::HTTP2::Client->new( %options );

Available options, and bestie I need you to read all of them:

  • on_push => sub {...} β€” if the server sends a push promise, this callback gets invoked. it's giving ✨unsolicited gifts✨. The shape of it:
on_push => sub {
    # received PUSH PROMISE headers
    my $pp_header = shift;
    ...

    # if we want reject this push
    # return undef

    # if we want to accept pushed resource
    # return callback to receive data
    return sub {
        my ( $headers, $data ) = @_;
        ...
    }
},
  • upgrade => 0|1 β€” use HTTP/1.1 Upgrade to upgrade the protocol from HTTP/1.1 to HTTP/2. Heads up: upgrade is possible only on a plain (non-tls) connection. Default value is 0.

See Starting HTTP/2 for "http" URIs <https://tools.ietf.org/html/rfc7540#section-3.2>

  • keepalive => 0|1 β€” keep the connection alive after requests. Default value is 0. Don't forget to explicitly call the close method if you set this to true. yes it's a whole commitment, treat it like one. πŸ’–
  • on_error => sub {...} β€” callback invoked on protocol errors:
on_error => sub {
    my $error = shift;
    ...
},
  • on_change_state => sub {...} β€” callback invoked every time http/2 streams change their state. the way it fires on every single transition… obsessed.

See Stream States <https://tools.ietf.org/html/rfc7540#section-5.1>

on_change_state => sub {
    my ( $stream_id, $previous_state, $current_state ) = @_;
    ...
},

request

Prepare an HTTP/2 request. here's what it looks like:

$client->request(

    # HTTP/2 headers
    ':scheme'    => 'http',
    ':authority' => 'localhost:8000',
    ':path'      => '/items',
    ':method'    => 'POST',

    # HTTP/1.1 headers
    headers      => [
        'content-type' => 'application/x-www-form-urlencoded',
        'user-agent' => 'perl-Protocol-HTTP2/0.06',
    ],

    # Callback when receive server's response
    on_done => sub {
        my ( $headers, $data ) = @_;
        ...
    },

    # Callback when receive stream reset
    on_error => sub {
        my $error_code = shift;
    },

    # Body of POST request
    data => "hello=world&test=done",
);

and yes, you can chain requests one by one, look at her go:

$client->request( 1-st request )->request( 2-nd request );

Available callbacks:

  • on_done => sub {...} β€” invoked when the full server's response is available. the grand finale 🌈
on_done => sub {
    my ( $headers, $data ) = @_;
    ...
},
  • on_headers => sub {...} β€” invoked as soon as headers have been successfully received from the server:
on_headers => sub {
    my $headers = shift;
    ...

    # if we want reject any data
    # return undef

    # continue
    return 1
}
  • on_data => sub {...} β€” if specified, all data will be passed to this callback instead of to on_done. on_done will receive an empty string. (yes it's handing over the aux, but politely. 🎧)
on_data => sub {
    my ( $partial_data, $headers ) = @_;
    ...

    # if we want cancel download
    # return undef

    # continue downloading
    return 1
}
  • on_error => sub {...} β€” callback invoked on stream errors:
on_error => sub {
    my $error = shift;
    ...
}

keepalive

Keep the connection alive after requests:

my $bool = $client->keepalive;
$client = $client->keepalive($bool);

shutdown

Get the connection status:

  • 0 β€” active
  • 1 β€” closed (you can terminate connection)

close

Explicitly close the connection (sends a GOAWAY frame). This is required if the client has the keepalive option enabled. like, mandatory mandatory. πŸ™ˆ

next_frame

get the next frame to send over the connection to the server. Returns:

  • undef β€” on error
  • 0 β€” nothing to send
  • binary string β€” encoded frame
# Example
while ( my $frame = $client->next_frame ) {
    syswrite $fh, $frame;
}

feed

Feed the decoder with chunks of the server's response:

sysread $fh, $binary_data, 4096;
$client->feed($binary_data);

ping

Send a ping frame to the server (to keep the connection alive). just checking in on her 🫢

$client->ping

or

$client->ping($payload);

The payload can be an arbitrary binary string and must contain 8 octets. If the payload argument is omitted, the client will send random data.


and that's the whole page, bestie, no notes. this one shipped in perl v5.42.3, dated 2026-09-04, and it lives at Protoc...:Client(3pm) in your man pages. go build something iconic with it. πŸ’œβœ¨πŸŽ€

πŸ“¦ where this page comes from