App-MHFS
view release on metacpan or search on metacpan
lib/MHFS/HTTP/Server/Client/Request.pm view on Meta::CPAN
package MHFS::HTTP::Server::Client::Request v0.7.0;
use 5.014;
use strict; use warnings;
use feature 'say';
use Time::HiRes qw( usleep clock_gettime CLOCK_REALTIME CLOCK_MONOTONIC);
use URI::Escape;
use Cwd qw(abs_path getcwd);
use Feature::Compat::Try;
use File::Basename;
use File::stat;
use IO::Poll qw(POLLIN POLLOUT POLLHUP);
use Data::Dumper;
use Scalar::Util qw(weaken);
use List::Util qw[min max];
use Symbol 'gensym';
use Devel::Peek;
use Encode qw(decode encode);
use constant {
MAX_REQUEST_SIZE => 8192,
};
use FindBin;
use File::Spec;
use MHFS::EventLoop::Poll;
use MHFS::Process;
use MHFS::Util qw(get_printable_utf8 LOCK_GET_LOCKDATA getMIME shell_escape escape_html_noquote parse_ipv4);
BEGIN {
if( ! (eval "use JSON; 1")) {
eval "use JSON::PP; 1" or die "No implementation of JSON available";
warn __PACKAGE__.": Using PurePerl version of JSON (JSON::PP)";
}
}
# Optional dependency, Alien::Tar::Size
BEGIN {
use constant HAS_Alien_Tar_Size => (eval "use Alien::Tar::Size; 1");
if(! HAS_Alien_Tar_Size) {
warn "Alien::Tar::Size is not available";
}
}
sub new {
my ($class, $client) = @_;
my %self = ( 'client' => $client);
bless \%self, $class;
weaken($self{'client'}); #don't allow Request to keep client alive
$self{'on_read_ready'} = \&want_request_line;
$self{'outheaders'}{'X-MHFS-CONN-ID'} = $client->{'outheaders'}{'X-MHFS-CONN-ID'};
$self{'rl'} = 0;
# we want the request
$client->SetEvents(POLLIN | MHFS::EventLoop::Poll->ALWAYSMASK );
$self{'recvrequesttimerid'} = $client->AddClientCloseTimer($client->{'server'}{'settings'}{'recvrequestimeout'}, $client->{'CONN-ID'}, 1);
return \%self;
}
# on ready ready handlers
sub want_request_line {
my ($self) = @_;
my $ipos = index($self->{'client'}{'inbuf'}, "\r\n");
if($ipos != -1) {
if(substr($self->{'client'}{'inbuf'}, 0, $ipos+2, '') =~ /^(([^\s]+)\s+([^\s]+)\s+(?:HTTP\/1\.([0-1])))\r\n/) {
my $rl = $1;
$self->{'method'} = $2;
$self->{'uri'} = $3;
$self->{'httpproto'} = $4;
my $rid = int(clock_gettime(CLOCK_MONOTONIC) * rand()); # insecure uid
$self->{'outheaders'}{'X-MHFS-REQUEST-ID'} = sprintf("%X", $rid);
say "X-MHFS-CONN-ID: " . $self->{'outheaders'}{'X-MHFS-CONN-ID'} . " X-MHFS-REQUEST-ID: " . $self->{'outheaders'}{'X-MHFS-REQUEST-ID'};
say "RECV: $rl";
if(($self->{'method'} ne 'GET') && ($self->{'method'} ne 'HEAD') && ($self->{'method'} ne 'PUT')) {
say "X-MHFS-CONN-ID: " . $self->{'outheaders'}{'X-MHFS-CONN-ID'} . 'Invalid method: ' . $self->{'method'}. ', closing conn';
return undef;
}
my ($path, $querystring) = ($self->{'uri'} =~ /^([^\?]+)(?:\?)?(.*)$/g);
say("raw path: $path\nraw querystring: $querystring");
# transformations
## Path
$path = uri_unescape($path);
my %pathStruct = ( 'unescapepath' => $path );
# collapse slashes
$path =~ s/\/{2,}/\//g;
say "collapsed: $path";
$pathStruct{'unsafecollapse'} = $path;
# without trailing slash
if(index($pathStruct{'unsafecollapse'}, '/', length($pathStruct{'unsafecollapse'})-1) != -1) {
chop($path);
say "no slash path: $path ";
}
$pathStruct{'unsafepath'} = $path;
## Querystring
my %qsStruct;
# In the querystring spaces are sometimes encoded as + for legacy reasons unfortunately
$querystring =~ s/\+/%20/g;
my @qsPairs = split('&', $querystring);
foreach my $pair (@qsPairs) {
my($key, $value) = split('=', $pair);
if(defined $value) {
if(!defined $qsStruct{$key}) {
$qsStruct{$key} = uri_unescape($value);
}
else {
( run in 1.199 second using v1.01-cache-2.11-cpan-39bf76dae61 )