PSGI-Handy
view release on metacpan or search on metacpan
lib/PSGI/Handy.pm view on Meta::CPAN
$s =~ s/([^A-Za-z0-9_\-.~])/sprintf('%%%02X', ord($1))/eg;
return $s;
}
######################################################################
# PSGI::Handy::Request - tiny PSGI env wrapper
#
# Wraps a PSGI %env and exposes method, path, query/body parameters
# (merged), headers, cookies and the raw body. Query- and body-parameter
# parsing and percent-decoding are implemented here in pure Perl.
######################################################################
package PSGI::Handy::Request;
# --------------------------------------------------------------------
# new($env) - wrap a PSGI environment hash reference
# --------------------------------------------------------------------
sub new {
my ($class, $env) = @_;
ref($env) eq 'HASH' or Carp::croak "new: a PSGI env hash reference is required";
my $self = { env => $env };
return bless $self, $class;
}
# --- request line -----------------------------------------------------
sub method {
my $self = shift;
my $m = $self->{env}{REQUEST_METHOD};
return defined $m ? $m : '';
}
sub path {
my $self = shift;
my $p = $self->{env}{PATH_INFO};
return defined $p ? $p : '';
}
sub query_string {
my $self = shift;
my $q = $self->{env}{QUERY_STRING};
return defined $q ? $q : '';
}
sub content_type {
my $self = shift;
my $t = $self->{env}{CONTENT_TYPE};
return defined $t ? $t : '';
}
sub content_length {
my $self = shift;
my $l = $self->{env}{CONTENT_LENGTH};
return (defined $l && $l ne '') ? int($l) : 0;
}
sub env {
my $self = shift;
return $self->{env};
}
# --- headers ----------------------------------------------------------
# Accepts 'Content-Type', 'content_type', 'X-Forwarded-For', etc.
sub header {
my ($self, $name) = @_;
return undef unless defined $name;
my $key = uc($name);
$key =~ s/-/_/g;
if ($key eq 'CONTENT_TYPE' || $key eq 'CONTENT_LENGTH') {
return $self->{env}{$key};
}
return $self->{env}{'HTTP_' . $key};
}
# --- raw body (read once from psgi.input, then cached) ---------------
sub body {
my $self = shift;
return $self->{_body} if exists $self->{_body};
my $buf = '';
my $len = $self->content_length;
my $input = $self->{env}{'psgi.input'};
if ($len > 0 && $input) {
# psgi.input->read may return fewer bytes than requested, so loop
# until CONTENT_LENGTH bytes are read or the stream ends.
my $chunk;
my $got = 0;
while ($got < $len) {
my $n = $input->read($chunk, $len - $got);
last unless $n; # EOF or error: keep what we have
$buf .= $chunk;
$got += $n;
}
}
$self->{_body} = $buf;
return $buf;
}
# --- parameters (query string merged with urlencoded body) -----------
sub param {
my ($self, $name) = @_;
$self->_build_params;
return undef unless defined $name;
my $v = $self->{_params}{$name};
return undef unless $v;
return $v->[0];
}
sub param_all {
my ($self, $name) = @_;
$self->_build_params;
return () unless defined $name;
my $v = $self->{_params}{$name};
return () unless $v;
return @$v;
}
sub param_names {
my $self = shift;
$self->_build_params;
return keys %{ $self->{_params} };
}
# Flat hash reference: name => first value.
( run in 1.466 second using v1.01-cache-2.11-cpan-13bb782fe5a )