Modules · Web · 0.29

Fetch

An HTTP user agent for HTTP/3, HTTP/2 and HTTP/1.1, whose every request is a future.

Sockets, TLS, QUIC and the HTTP framing are vendored C. Every request returns a future, and ->get on it pumps whichever loop is running, its own if none is: one line for a single call, or a thousand requests multiplexed on one loop, in the same code.

use Fetch;

my $base = $ENV{BASE} || 'http://127.0.0.1:8080';   # the Hyperman beside it

# ->get awaits the future: there is no event loop to set up
my $res = Fetch->new(timeout => 5)->get("$base/")->get;

print $res->status, ' ', $res->content;              # 200 hello
print $res->header('Content-Type'), "\n";            # text/plain

What you get

  • A future for every request

    get, post, request and websocket all return a Fetch::Future, and needs_all waits for many at once.

  • Any loop, or none

    Fetch has its own loop and uses it when you give it nothing. Hand it an IO::Async loop, AnyEvent or Hyperman's and its requests share that loop without blocking it.

  • Three protocols

    HTTP/2 by ALPN over TLS, HTTP/3 over QUIC when a server offers it with Alt-Svc, and HTTP/1.1 otherwise, each with a keep-alive pool.

  • What a client needs

    Redirects, per-request timeouts, a cookie jar, streaming bodies, json => in and $res->json out, and WebSockets.

  • Watch every request

    Fetch->on_request sees each request the process makes, even ones it did not write, and can add a header to it: a tracing layer's traceparent, for one.

In use

use Fetch;
use Time::HiRes qw(time);

my $base = $ENV{BASE} || 'http://127.0.0.1:8080';
my $ua   = Fetch->new(timeout => 5);

# eight requests in flight at once, on one loop
my $t0   = time;
my @futs = map { $ua->get("$base/slow") } 1 .. 8;
Fetch::Future->needs_all(@futs)->get;

print sort map { $_->get->content } @futs;           # slow request 1 .. 8
printf "8 requests in %.1fs\n", time - $t0;          # about 0.2s, not 1.6s

For C

fetch_abi.h, vendored at a pinned version and resolved at boot through Fetch::_abi_ptr: another XS module builds and sends requests entirely in C, as Reverse::Proxy builds its upstream requests, and can observe every request the process makes with no Perl on the path.

Get it

cpanm Fetch