Commit 895173ba authored by Leigh Stoller's avatar Leigh Stoller

Add support the HTTP_RANGE header so that wget/curl can restart

an interrupted download instead of starting from the beginning.
parent b7a2e967
#!/usr/bin/perl -wT
#!/usr/bin/perl -w
#
# Copyright (c) 2000-2016, 2019 University of Utah and the Flux Group.
#
......@@ -38,11 +38,13 @@ sub usage()
"Spew an image file to a (widearea) node.\n";
exit(-1);
}
my $optlist = "t:k:hsde";
my $optlist = "t:k:hsder:";
my $debug = 0;
my $headonly = 0;
my $sigfile = 0;
my $delta = 0;
my $rangestart;
my $rangeend;
my $access_key;
my $timestamp; # GM Time.
......@@ -76,6 +78,7 @@ use OSImage;
# Locals
my $bytelen = 0;
my $foffset = 0;
# Protos
sub SpewImage();
......@@ -129,6 +132,20 @@ if (defined($options{"t"})) {
die("*** Bad data in timestamp: $timestamp\n");
}
}
if (defined($options{"r"})) {
my $range = $options{"r"};
if ($range =~ /^(\d+)(\-)?$/) {
$rangestart = $1;
}
elsif ($range =~ /^(\d+)\-(\d+)$/) {
$rangestart = $1;
$rangeend = $2;
}
else {
die("*** Bad data in range: $range\n");
}
}
if (@ARGV != 1 || !defined($access_key)) {
usage();
}
......@@ -163,7 +180,6 @@ sub SpewImage()
#
# Deal with NFS read failures
#
my $foffset = 0;
my $retries = 5;
my $buf;
......@@ -248,16 +264,9 @@ sub VerifyImage()
#
# Stat the file to get the bytelen for spewing.
#
my (undef,undef,undef,undef,undef,undef,undef,$length,
my (undef,undef,undef,undef,undef,undef,undef,$filelength,
undef,$mtime) = stat($file);
if ($headonly) {
print "Content-Length: $length\n";
print "Last-Modified: " .
strftime("%a, %d %b %Y %H:%M:%S GMT", gmtime($mtime)) . "\n";
exit(0);
}
#
# Check timestamp if supplied. Remember, we get GM timestamps, so
# must convert the local stamp.
......@@ -267,7 +276,35 @@ sub VerifyImage()
return 2
if ($timestamp >= $mtime);
}
my $length = $filelength;
if (defined($rangestart)) {
if ($rangeend) {
$length = $rangeend - $rangestart;
$length += 1;
}
else {
$length = $filelength - $rangestart;
$rangeend = $filelength - 1;
}
print "Content-Range: bytes ${rangestart}-${rangeend}/$filelength\n";
}
else {
print "Accept-Ranges: bytes\n";
}
$bytelen = $length;
$foffset = ($rangestart ? $rangestart : 0);
print "Content-Length: $length\n";
print "Last-Modified: " .
strftime("%a, %d %b %Y %H:%M:%S GMT", gmtime($mtime)) . "\n";
exit(0)
if ($headonly);
# End of headers.
print "\n";
return 0;
}
......
......@@ -91,6 +91,7 @@ $pid = $group->pid();
$unix_gid = $group->unix_gid();
$project = $image->Project();
$unix_pid = $project->unix_gid();
$rangearg = "";
if ($image->noexport()) {
SPITERROR(403, "This image is marked as export restricted");
......@@ -100,7 +101,25 @@ if (!$image->isglobal()) {
}
#
# We want to support HEAD requests to avoid send the file.
# Check for RANGE header. We support a singlw range, there is no
# reason for the client to ask for multiple ranges for an image.
#
if (isset($_SERVER['HTTP_RANGE'])) {
// Delimiters are case insensitive
if (preg_match('/bytes=(\d*)\-$/i', $_SERVER['HTTP_RANGE'], $matches) ||
preg_match('/bytes=(\d*)\-(\d*)$/i', $_SERVER['HTTP_RANGE'], $matches)){
$rangearg = "-r " . $matches[1] . "-";
if (count($matches) == 3) {
$rangearg .= $matches[2];
}
}
else {
SPITERROR(416, "Client requested invalid Range.");
}
}
#
# We want to support HEAD requests to avoid sending the file.
#
$ishead = 0;
$headarg = "";
......@@ -110,7 +129,8 @@ if ($_SERVER['REQUEST_METHOD'] == "HEAD") {
}
if ($fp = popen("$TBSUEXEC_PATH nobody $unix_pid,$unix_gid ".
"webspewimage $arg $headarg -k $access_key $versid", "r")) {
"webspewimage $arg $headarg $rangearg -k $access_key $versid",
"r")) {
header("Content-Type: application/octet-stream");
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
......@@ -135,13 +155,29 @@ if ($fp = popen("$TBSUEXEC_PATH nobody $unix_pid,$unix_gid ".
# The first read will come back with no output, which means nothing is
# going to be sent except the headers.
#
$string = fread($fp, 1024);
$string = fgets($fp);
if ($string) {
print($string);
while (!feof($fp) && connection_status() == 0) {
print(fread($fp, 1024*32));
flush();
}
# We know the first line is a header.
$string = rtrim($string);
header($string);
# Look for end of headers.
$found_headers = false;
while (!$found_headers) {
$string = fgets($fp);
if ($string == "\n") {
$found_headers = true;
}
else {
$string = rtrim($string);
header($string);
}
}
while (!feof($fp) && connection_status() == 0) {
print(fread($fp, 1024*32));
flush();
}
}
}
$retval = pclose($fp);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment