fix partial (206 and 416) support in https

As lengthy discussed in lp:1157943 partial https support was utterly
broken as a 206 response was handled as an (unhandled) error. This is
the first part of fixing it by supporting a 206 response and starting to
deal with 416.
This commit is contained in:
David Kalnischkies
2013-09-30 13:42:33 +02:00
parent 7330f4df8b
commit 85050e7644
+67 -33
View File
@@ -121,7 +121,6 @@ bool HttpsMethod::Fetch(FetchItem *Itm)
struct stat SBuf;
struct curl_slist *headers=NULL;
char curl_errorstr[CURL_ERROR_SIZE];
long curl_responsecode;
URI Uri = Itm->Uri;
string remotehost = Uri.Host;
@@ -277,7 +276,7 @@ bool HttpsMethod::Fetch(FetchItem *Itm)
if (stat(Itm->DestFile.c_str(),&SBuf) >= 0 && SBuf.st_size > 0)
{
char Buf[1000];
sprintf(Buf, "Range: bytes=%li-", (long) SBuf.st_size - 1);
sprintf(Buf, "Range: bytes=%li-", (long) SBuf.st_size);
headers = curl_slist_append(headers, Buf);
sprintf(Buf, "If-Range: %s", TimeRFC1123(SBuf.st_mtime).c_str());
headers = curl_slist_append(headers, Buf);
@@ -291,75 +290,110 @@ bool HttpsMethod::Fetch(FetchItem *Itm)
// go for it - if the file exists, append on it
File = new FileFd(Itm->DestFile, FileFd::WriteAny);
if (File->Size() > 0)
File->Seek(File->Size() - 1);
File->Seek(File->Size());
// keep apt updated
Res.Filename = Itm->DestFile;
// get it!
CURLcode success = curl_easy_perform(curl);
long curl_responsecode;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &curl_responsecode);
long curl_servdate;
curl_easy_getinfo(curl, CURLINFO_FILETIME, &curl_servdate);
// If the server returns 200 OK but the If-Modified-Since condition is not
// met, CURLINFO_CONDITION_UNMET will be set to 1
long curl_condition_unmet = 0;
curl_easy_getinfo(curl, CURLINFO_CONDITION_UNMET, &curl_condition_unmet);
File->Close();
curl_slist_free_all(headers);
// cleanup
if(success != 0 || (curl_responsecode != 200 && curl_responsecode != 304))
if (success != 0)
{
_error->Error("%s", curl_errorstr);
// unlink, no need keep 401/404 page content in partial/
unlink(File->Name().c_str());
Fail();
return false;
}
// server says file not modified
if (curl_responsecode == 304 || curl_condition_unmet == 1)
{
unlink(File->Name().c_str());
Res.IMSHit = true;
Res.LastModified = Itm->LastModified;
Res.Size = 0;
URIDone(Res);
return true;
}
// Timestamp
struct utimbuf UBuf;
if (curl_servdate != -1) {
UBuf.actime = curl_servdate;
UBuf.modtime = curl_servdate;
utime(File->Name().c_str(),&UBuf);
if (curl_responsecode != 200 && // OK
curl_responsecode != 206 && // Partial
curl_responsecode != 416) // invalid Range
{
char err[255];
snprintf(err, sizeof(err) - 1, "HttpError%ld", curl_responsecode);
SetFailReason(err);
_error->Error("%s", err);
// unlink, no need keep 401/404 page content in partial/
unlink(File->Name().c_str());
return false;
}
struct stat resultStat;
if (unlikely(stat(File->Name().c_str(), &resultStat) != 0))
{
_error->Errno("stat", "Unable to access file %s", File->Name().c_str());
return false;
}
Res.Size = resultStat.st_size;
// invalid range-request
if (curl_responsecode == 416)
{
unlink(File->Name().c_str());
Res.Size = 0;
delete File;
Redirect(Itm->Uri);
return true;
}
// check the downloaded result
struct stat Buf;
if (stat(File->Name().c_str(),&Buf) == 0)
if (curl_responsecode == 304 || curl_condition_unmet)
{
Res.Filename = File->Name();
Res.LastModified = Buf.st_mtime;
Res.IMSHit = false;
if (curl_responsecode == 304 || curl_condition_unmet)
{
unlink(File->Name().c_str());
Res.IMSHit = true;
Res.LastModified = Itm->LastModified;
Res.Size = 0;
URIDone(Res);
return true;
}
Res.Size = Buf.st_size;
unlink(File->Name().c_str());
Res.IMSHit = true;
Res.LastModified = Itm->LastModified;
Res.Size = 0;
URIDone(Res);
return true;
}
Res.IMSHit = false;
// Timestamp
curl_easy_getinfo(curl, CURLINFO_FILETIME, &Res.LastModified);
if (Res.LastModified != -1)
{
struct utimbuf UBuf;
UBuf.actime = Res.LastModified;
UBuf.modtime = Res.LastModified;
utime(File->Name().c_str(),&UBuf);
}
else
Res.LastModified = resultStat.st_mtime;
// take hashes
Hashes Hash;
FileFd Fd(Res.Filename, FileFd::ReadOnly);
Hash.AddFD(Fd);
Res.TakeHashes(Hash);
// keep apt updated
URIDone(Res);
// cleanup
Res.Size = 0;
delete File;
curl_slist_free_all(headers);
return true;
};