NAME
Punk::Request - a lazy wrapper over the PSGI environment
DESCRIPTION
Nothing is parsed until asked for, and everything parsed is cached on the object: query pairs, form bodies, cookies, the raw body. Reached through "req" in Punk::Context.
The object is a plain blessed array with a fixed slot layout and an all-C implementation - percent-decoding, pair splitting, multi-value promotion and header lookup without the regex engine; the body read runs through PerlIO and JSON decodes through File::Raw::JSON's C ABI. Load this module through Punk, which loads the compiled core first.
METHODS
new($env)
Constructed by the dispatcher; wraps the PSGI environment.
env
method
path
The raw environment, request method and path (/ when empty).
address
The client's address: REMOTE_ADDR. On a directly-exposed application that is the socket peer. Behind a reverse proxy with the proxy keyword in force it is the resolved client, because Punk rewrites the env key rather than adding a second one - see "proxy" in Punk. The connecting address is then available as $c->env->{'punk.peer_addr'}.
header($name)
A request header by name, case-insensitively (Content-Type and Content-Length included).
headers
Every request header as a hashref, keyed lowercase and dash-separated - x-forwarded-for, content-type, content-length - which is how HTTP/2 spells them and the same shape the OpenAPI validation path builds.
Reach for "header" when you know the name: it folds case, and a plain hash cannot, so $req->headers->{'X-Foo'} misses where $req->header('X-Foo') hits. This is for the cases where you want them all - logging, proxying, signing.
The hash is built fresh each call, so keeping or changing it is safe.
param($name)
Query parameter first, then form body parameter. A repeated parameter yields an arrayref.
params
params(@names)
With no names, one merged hashref, query winning over form.
With names, only those, looked up the way "param" looks one up. The return follows context: a list of values in the order asked for, undef for a name neither table has -
my ($page, $size) = $req->params(qw(page size));
or, in scalar context, a hashref holding only the names that were there, which is the shape to build a filter from -
my %filter = %{ $req->params(qw(state queue task)) };
A dereference block puts what it wraps in scalar context, so the %{ } above gets the hashref. Somewhere already in list context - an argument list, a hash constructor - it takes the slice instead, so ask for the hashref explicitly there with scalar.
Passing a list that happens to be empty is passing no names at all, and so gives everything: guard the call where the names are built at runtime.
query
form
The parsed body: application/x-www-form-urlencoded pairs, or the field parts of a multipart/form-data submission (whose file parts become uploads).
upload($name)
uploads
The Punk::Upload for a multipart file field - the first if several - and the { name => upload | [uploads] } hash of all of them. Both parse the body once.
body
The raw request body bytes (undef when there is none), read once and rewound.
json
The body decoded as JSON through File::Raw::JSON's C ABI.
cookies
cookie($name)
The request cookie jar (first value wins) / one cookie value.
AUTHOR
LNATION <email@lnation.org>
LICENSE AND COPYRIGHT
This software is Copyright (c) 2026 by LNATION <email@lnation.org>.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)