App-MHFS
view release on metacpan or search on metacpan
lib/MHFS/HTTP/Server/Client/Request.pm view on Meta::CPAN
elsif($code == 307) {
$msg = "307 Temporary Redirect";
}
$msg .= "\r\n<a href=\"$url\"></a>\r\n";
$self->SendHTML($msg, {'code' => $code});
}
# encodes path and querystring
# path and query string keys and values must be bytes not unicode string
sub SendRedirect {
my ($self, $code, $path, $qs) = @_;
my $url;
# encode the path component
while(length($path)) {
my $slash = index($path, '/');
my $len = ($slash != -1) ? $slash : length($path);
my $pathcomponent = substr($path, 0, $len, '');
$url .= uri_escape($pathcomponent);
if($slash != -1) {
substr($path, 0, 1, '');
$url .= '/';
}
}
# encode the querystring
if($qs) {
$url .= '?';
foreach my $key (keys %{$qs}) {
my @values;
if(ref($qs->{$key}) ne 'ARRAY') {
push @values, $qs->{$key};
}
else {
@values = @{$qs->{$key}};
}
foreach my $value (@values) {
$url .= uri_escape($key).'='.uri_escape($value) . '&';
}
}
chop $url;
}
@_ = ($self, $code, $url);
goto &SendRedirectRawURL;
}
sub SendLocalFile {
my ($self, $requestfile) = @_;
my $start = $self->{'header'}{'_RangeStart'};
my $client = $self->{'client'};
# open the file and get the size
my %fileitem = ('requestfile' => $requestfile);
my $currentsize;
if($self->{'method'} ne 'HEAD') {
my $FH;
if(! open($FH, "<", $requestfile)) {
say "SLF: open failed";
$self->Send404;
return;
}
binmode($FH);
my $st = stat($FH);
if(! $st) {
$self->Send404();
return;
}
$currentsize = $st->size;
$fileitem{'fh'} = $FH;
}
else {
$currentsize = (-s $requestfile);
}
# seek if a start is specified
if(defined $start) {
if($start >= $currentsize) {
$self->Send416($currentsize);
return;
}
elsif($fileitem{'fh'}) {
seek($fileitem{'fh'}, $start, 0);
}
}
# get the maximumly possible file size. 99999999999 signfies unknown
my $get_current_size = sub {
return $currentsize;
};
my $done;
my $ts;
my $get_max_size = sub {
if($done) {
return $ts;
}
my $locksz = LOCK_GET_LOCKDATA($requestfile);
if(defined($locksz)) {
$ts = ($locksz || 0);
}
else {
$done = 1;
$ts = ($get_current_size->() || 0);
}
};
my $filelength = $get_max_size->();
# truncate to the [potentially] satisfiable end
if(defined $self->{'header'}{'_RangeEnd'}) {
$self->{'header'}{'_RangeEnd'} = min($filelength-1, $self->{'header'}{'_RangeEnd'});
}
# setup callback for retrieving current file size if we are following the file
if($fileitem{'fh'}) {
if(! $done) {
$get_current_size = sub {
return stat($fileitem{'fh'})
};
}
my $get_read_filesize = sub {
my $maxsize = $get_max_size->();
if(defined $self->{'header'}{'_RangeEnd'}) {
my $rangesize = $self->{'header'}{'_RangeEnd'}+1;
return $rangesize if($rangesize <= $maxsize);
}
return $maxsize;
};
$fileitem{'get_current_length'} = $get_read_filesize;
}
# flag to add SharedArrayBuffer headers
my @SABwhitelist = ('static/music_worklet_inprogress/index.html');
my $allowSAB;
foreach my $allowed (@SABwhitelist) {
if(index($requestfile, $allowed, length($requestfile)-length($allowed)) != -1) {
$allowSAB = 1;
last;
}
}
# finally build headers and send
if($filelength == 99999999999) {
$filelength = undef;
}
my $mime = getMIME($requestfile);
my $opt = {
'size' => $filelength,
'mime' => $mime,
'allowSAB' => $allowSAB
};
if($self->{'responseopt'}{'cd_file'}) {
$opt->{$self->{'responseopt'}{'cd_file'}} = basename($requestfile);
}
$self->_SendDataItem(\%fileitem, $opt);
}
# currently only supports fixed filelength
sub SendPipe {
my ($self, $FH, $filename, $filelength, $mime) = @_;
if(! defined $filelength) {
$self->Send404();
}
$mime //= getMIME($filename);
binmode($FH);
my %fileitem;
$fileitem{'fh'} = $FH;
$fileitem{'get_current_length'} = sub {
my $tocheck = defined $self->{'header'}{'_RangeEnd'} ? $self->{'header'}{'_RangeEnd'}+1 : $filelength;
return min($filelength, $tocheck);
};
$self->_SendDataItem(\%fileitem, {
'size' => $filelength,
'mime' => $mime,
'filename' => $filename
});
}
# to do get rid of shell escape, launch ssh without blocking
sub SendFromSSH {
my ($self, $sshsource, $filename, $node) = @_;
my @sshcmd = ('ssh', $sshsource->{'userhost'}, '-p', $sshsource->{'port'});
my $fullescapedname = "'" . shell_escape($filename) . "'";
my $folder = $sshsource->{'folder'};
my $size = $node->[1];
my @cmd;
if(defined $self->{'header'}{'_RangeStart'}) {
my $start = $self->{'header'}{'_RangeStart'};
my $end = $self->{'header'}{'_RangeEnd'} // ($size - 1);
my $bytestoskip = $start;
my $count = $end - $start + 1;
@cmd = (@sshcmd, 'dd', 'skip='.$bytestoskip, 'count='.$count, 'bs=1', 'if='.$fullescapedname);
}
else{
@cmd = (@sshcmd, 'cat', $fullescapedname);
}
say "SendFromSSH (BLOCKING)";
open(my $cmdh, '-|', @cmd) or die("SendFromSSH $!");
$self->SendPipe($cmdh, basename($filename), $size);
return 1;
}
# ENOTIMPLEMENTED
sub Proxy {
my ($self, $proxy, $node) = @_;
die;
return 1;
}
# buf is a bytes scalar
sub SendBytes {
my ($self, $mime, $buf, $options) = @_;
# we want to sent in increments of bytes not characters
if(Encode::is_utf8($buf)) {
warn "SendBytes: UTF8 flag is set, turning off";
Encode::_utf8_off($buf);
}
my $bytesize = length($buf);
# only truncate buf if responding to a range request
if((!$options->{'code'}) || ($options->{'code'} == 206)) {
( run in 0.618 second using v1.01-cache-2.11-cpan-39bf76dae61 )