Package Kit: re-use downloads from unfinished transactions
There are three parts to this change: - In FetchFileJob, if the request fails with a timeout or IO error (probably because of unstable connection) attempt to resume the download with a range request. No limit on number of retries currently, maybe we should add one. - In PackageManager, before downloading a file, look around in other transaction directories in case it's already there. Partial and complete downloads are differentiated by an attribute which the fetch file job maintains. For complete downloads, no fetch job is scheduled, for partial downloads, the fetch job will request the remainder of the file. - In BHttpRequest, the implementation of SetRangeStart() and SetRangeEnd() have been added, along with some refactoring to handle listener notifications consistently. This also fixed a bug where the final notification for download progress was not emitted for compressed data. Fixes #12414. Change-Id: I3e285741ed0e5651594a7c2e1c7170644a9d297d Reviewed-on: https://review.haiku-os.org/c/haiku/+/3404 Reviewed-by: Stephan Aßmus <[email protected]> Reviewed-by: Alex von Gluck IV <[email protected]>
This commit is contained in:
committed by
Stephan Aßmus
parent
096687ba71
commit
f15516ff92
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2013 Haiku Inc. All rights reserved.
|
* Copyright 2010-2021 Haiku Inc. All rights reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
#ifndef _B_URL_PROTOCOL_HTTP_H_
|
#ifndef _B_URL_PROTOCOL_HTTP_H_
|
||||||
@@ -95,6 +95,11 @@ private:
|
|||||||
// Utility methods
|
// Utility methods
|
||||||
bool _IsDefaultPort();
|
bool _IsDefaultPort();
|
||||||
|
|
||||||
|
// Listener notification
|
||||||
|
void _NotifyDataReceived(const char* data,
|
||||||
|
off_t pos, ssize_t length,
|
||||||
|
off_t bytesReceived, ssize_t bytesTotal);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool fSSL;
|
bool fSSL;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 Haiku Inc. All rights reserved.
|
* Copyright 2010-2021 Haiku Inc. All rights reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
* Christophe Huriaux, [email protected]
|
* Christophe Huriaux, [email protected]
|
||||||
* Niels Sascha Reedijk, [email protected]
|
* Niels Sascha Reedijk, [email protected]
|
||||||
* Adrien Destugues, [email protected]
|
* Adrien Destugues, [email protected]
|
||||||
|
* Stephan Aßmus, [email protected]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@@ -209,17 +210,36 @@ BHttpRequest::SetAutoReferrer(bool enable)
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
BHttpRequest::SetHeaders(const BHttpHeaders& headers)
|
BHttpRequest::SetUserName(const BString& name)
|
||||||
{
|
{
|
||||||
AdoptHeaders(new(std::nothrow) BHttpHeaders(headers));
|
fOptUsername = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
BHttpRequest::AdoptHeaders(BHttpHeaders* const headers)
|
BHttpRequest::SetPassword(const BString& password)
|
||||||
{
|
{
|
||||||
delete fOptHeaders;
|
fOptPassword = password;
|
||||||
fOptHeaders = headers;
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BHttpRequest::SetRangeStart(off_t position)
|
||||||
|
{
|
||||||
|
// This field is used within the transfer loop, so only
|
||||||
|
// allow setting it before sending the request.
|
||||||
|
if (fRequestStatus == kRequestInitialState)
|
||||||
|
fOptRangeStart = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BHttpRequest::SetRangeEnd(off_t position)
|
||||||
|
{
|
||||||
|
// This field could be used in the transfer loop, so only
|
||||||
|
// allow setting it before sending the request.
|
||||||
|
if (fRequestStatus == kRequestInitialState)
|
||||||
|
fOptRangeEnd = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -230,6 +250,13 @@ BHttpRequest::SetPostFields(const BHttpForm& fields)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BHttpRequest::SetHeaders(const BHttpHeaders& headers)
|
||||||
|
{
|
||||||
|
AdoptHeaders(new(std::nothrow) BHttpHeaders(headers));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
BHttpRequest::AdoptPostFields(BHttpForm* const fields)
|
BHttpRequest::AdoptPostFields(BHttpForm* const fields)
|
||||||
{
|
{
|
||||||
@@ -251,16 +278,10 @@ BHttpRequest::AdoptInputData(BDataIO* const data, const ssize_t size)
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
BHttpRequest::SetUserName(const BString& name)
|
BHttpRequest::AdoptHeaders(BHttpHeaders* const headers)
|
||||||
{
|
{
|
||||||
fOptUsername = name;
|
delete fOptHeaders;
|
||||||
}
|
fOptHeaders = headers;
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
BHttpRequest::SetPassword(const BString& password)
|
|
||||||
{
|
|
||||||
fOptPassword = password;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -740,17 +761,14 @@ BHttpRequest::_MakeRequest()
|
|||||||
ssize_t size = decompressorStorage.Size();
|
ssize_t size = decompressorStorage.Size();
|
||||||
BStackOrHeapArray<char, 4096> buffer(size);
|
BStackOrHeapArray<char, 4096> buffer(size);
|
||||||
size = decompressorStorage.Read(buffer, size);
|
size = decompressorStorage.Read(buffer, size);
|
||||||
if (size > 0) {
|
_NotifyDataReceived(buffer, bytesUnpacked, size,
|
||||||
fListener->DataReceived(this, buffer, bytesUnpacked,
|
bytesReceived, bytesTotal);
|
||||||
size);
|
|
||||||
bytesUnpacked += size;
|
bytesUnpacked += size;
|
||||||
}
|
|
||||||
} else if (bytesRead > 0) {
|
} else if (bytesRead > 0) {
|
||||||
fListener->DataReceived(this, inputTempBuffer,
|
_NotifyDataReceived(inputTempBuffer,
|
||||||
bytesReceived - bytesRead, bytesRead);
|
bytesReceived - bytesRead, bytesRead,
|
||||||
|
bytesReceived, bytesTotal);
|
||||||
}
|
}
|
||||||
fListener->DownloadProgress(this, bytesReceived,
|
|
||||||
std::max((off_t)0, bytesTotal));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytesTotal >= 0 && bytesReceived >= bytesTotal)
|
if (bytesTotal >= 0 && bytesReceived >= bytesTotal)
|
||||||
@@ -768,14 +786,12 @@ BHttpRequest::_MakeRequest()
|
|||||||
ssize_t size = decompressorStorage.Size();
|
ssize_t size = decompressorStorage.Size();
|
||||||
BStackOrHeapArray<char, 4096> buffer(size);
|
BStackOrHeapArray<char, 4096> buffer(size);
|
||||||
size = decompressorStorage.Read(buffer, size);
|
size = decompressorStorage.Read(buffer, size);
|
||||||
if (fListener != NULL && size > 0) {
|
_NotifyDataReceived(buffer, bytesUnpacked, size,
|
||||||
fListener->DataReceived(this, buffer,
|
bytesReceived, bytesTotal);
|
||||||
bytesUnpacked, size);
|
|
||||||
bytesUnpacked += size;
|
bytesUnpacked += size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
parseEnd = (fInputBuffer.Size() == 0);
|
parseEnd = (fInputBuffer.Size() == 0);
|
||||||
}
|
}
|
||||||
@@ -908,6 +924,20 @@ BHttpRequest::_SerializeHeaders()
|
|||||||
if (fOptReferer.CountChars() > 0)
|
if (fOptReferer.CountChars() > 0)
|
||||||
outputHeaders.AddHeader("Referer", fOptReferer.String());
|
outputHeaders.AddHeader("Referer", fOptReferer.String());
|
||||||
|
|
||||||
|
// Optional range requests headers
|
||||||
|
if (fOptRangeStart != -1 || fOptRangeEnd != -1) {
|
||||||
|
if (fOptRangeStart == -1)
|
||||||
|
fOptRangeStart = 0;
|
||||||
|
BString range;
|
||||||
|
if (fOptRangeEnd != -1) {
|
||||||
|
range.SetToFormat("bytes=%" B_PRIdOFF "-%" B_PRIdOFF,
|
||||||
|
fOptRangeStart, fOptRangeEnd);
|
||||||
|
} else {
|
||||||
|
range.SetToFormat("bytes=%" B_PRIdOFF "-", fOptRangeStart);
|
||||||
|
}
|
||||||
|
outputHeaders.AddHeader("Range", range.String());
|
||||||
|
}
|
||||||
|
|
||||||
// Authentication
|
// Authentication
|
||||||
if (fContext != NULL) {
|
if (fContext != NULL) {
|
||||||
BHttpAuthentication& authentication = fContext->GetAuthentication(fUrl);
|
BHttpAuthentication& authentication = fContext->GetAuthentication(fUrl);
|
||||||
@@ -1158,3 +1188,29 @@ BHttpRequest::_IsDefaultPort()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BHttpRequest::_NotifyDataReceived(const char* data, off_t pos, ssize_t size,
|
||||||
|
off_t bytesReceived, ssize_t bytesTotal)
|
||||||
|
{
|
||||||
|
if (fListener == NULL || size <= 0)
|
||||||
|
return;
|
||||||
|
if (fOptRangeStart > 0) {
|
||||||
|
pos += fOptRangeStart;
|
||||||
|
// bytesReceived and bytesTotal refer to the requested range,
|
||||||
|
// so that should technically not be adjusted for the range start.
|
||||||
|
// For displaying progress to the user, this is not ideal, though.
|
||||||
|
// But only for the case where we request the remainder of a file.
|
||||||
|
// Range requests can also be used to request any portion of a
|
||||||
|
// resource, so not modifying them is technically more correct.
|
||||||
|
// We can use a little trick, though: We know when the remainder
|
||||||
|
// is requested, because then fOptRangeEnd is -1.
|
||||||
|
if (fOptRangeEnd == -1) {
|
||||||
|
bytesReceived += fOptRangeStart;
|
||||||
|
if (bytesTotal > 0)
|
||||||
|
bytesTotal += fOptRangeStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fListener->DataReceived(this, data, pos, size);
|
||||||
|
fListener->DownloadProgress(this, bytesReceived,
|
||||||
|
std::max((off_t)0, bytesTotal));
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2011-2015, Haiku, Inc. All Rights Reserved.
|
* Copyright 2011-2021, Haiku, Inc. All Rights Reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
* Axel Dörfler <[email protected]>
|
* Axel Dörfler <[email protected]>
|
||||||
* Rene Gollent <[email protected]>
|
* Rene Gollent <[email protected]>
|
||||||
* Oliver Tappe <[email protected]>
|
* Oliver Tappe <[email protected]>
|
||||||
|
* Stephan Aßmus <[email protected]>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@@ -22,6 +23,8 @@
|
|||||||
# include <UrlProtocolRoster.h>
|
# include <UrlProtocolRoster.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "FetchUtils.h"
|
||||||
|
|
||||||
|
|
||||||
namespace BPackageKit {
|
namespace BPackageKit {
|
||||||
|
|
||||||
@@ -36,7 +39,7 @@ FetchFileJob::FetchFileJob(const BContext& context, const BString& title,
|
|||||||
inherited(context, title),
|
inherited(context, title),
|
||||||
fFileURL(fileURL),
|
fFileURL(fileURL),
|
||||||
fTargetEntry(targetEntry),
|
fTargetEntry(targetEntry),
|
||||||
fTargetFile(&targetEntry, B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY),
|
fTargetFile(&targetEntry, B_CREATE_FILE | B_WRITE_ONLY),
|
||||||
fError(B_ERROR),
|
fError(B_ERROR),
|
||||||
fDownloadProgress(0.0)
|
fDownloadProgress(0.0)
|
||||||
{
|
{
|
||||||
@@ -90,13 +93,40 @@ FetchFileJob::Execute()
|
|||||||
if (result != B_OK)
|
if (result != B_OK)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
BUrlRequest* request = BUrlProtocolRoster::MakeRequest(fFileURL.String(),
|
result = FetchUtils::SetFileType(fTargetFile,
|
||||||
|
"application/x-vnd.haiku-package");
|
||||||
|
if (result != B_OK) {
|
||||||
|
fprintf(stderr, "failed to set file type for '%s': %s\n",
|
||||||
|
DownloadFileName(), strerror(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
printf("downloading %s\n", DownloadURL());
|
||||||
|
BUrlRequest* request = BUrlProtocolRoster::MakeRequest(DownloadURL(),
|
||||||
this);
|
this);
|
||||||
if (request == NULL)
|
if (request == NULL)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
// Try to resume the download where we left off
|
||||||
|
off_t currentPosition;
|
||||||
|
BHttpRequest* http= dynamic_cast<BHttpRequest*>(request);
|
||||||
|
if (http != NULL && fTargetFile.GetSize(¤tPosition) == B_OK
|
||||||
|
&& currentPosition > 0) {
|
||||||
|
printf("requesting range start %" B_PRIdOFF "\n", currentPosition);
|
||||||
|
http->SetRangeStart(currentPosition);
|
||||||
|
}
|
||||||
|
|
||||||
thread_id thread = request->Run();
|
thread_id thread = request->Run();
|
||||||
wait_for_thread(thread, NULL);
|
wait_for_thread(thread, NULL);
|
||||||
|
} while (fError == B_IO_ERROR || fError == B_DEV_TIMEOUT);
|
||||||
|
|
||||||
|
if (fError == B_OK) {
|
||||||
|
result = FetchUtils::MarkDownloadComplete(fTargetFile);
|
||||||
|
if (result != B_OK) {
|
||||||
|
fprintf(stderr, "failed to mark download '%s' as complete: %s\n",
|
||||||
|
DownloadFileName(), strerror(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return fError;
|
return fError;
|
||||||
}
|
}
|
||||||
@@ -146,10 +176,8 @@ FetchFileJob::RequestCompleted(BUrlRequest* request, bool success)
|
|||||||
}
|
}
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case B_HTTP_STATUS_OK:
|
case B_HTTP_STATUS_OK:
|
||||||
fError = B_OK;
|
|
||||||
break;
|
|
||||||
case B_HTTP_STATUS_PARTIAL_CONTENT:
|
case B_HTTP_STATUS_PARTIAL_CONTENT:
|
||||||
fError = B_PARTIAL_READ;
|
fError = B_OK;
|
||||||
break;
|
break;
|
||||||
case B_HTTP_STATUS_REQUEST_TIMEOUT:
|
case B_HTTP_STATUS_REQUEST_TIMEOUT:
|
||||||
case B_HTTP_STATUS_GATEWAY_TIMEOUT:
|
case B_HTTP_STATUS_GATEWAY_TIMEOUT:
|
||||||
@@ -198,7 +226,7 @@ FetchFileJob::FetchFileJob(const BContext& context, const BString& title,
|
|||||||
inherited(context, title),
|
inherited(context, title),
|
||||||
fFileURL(fileURL),
|
fFileURL(fileURL),
|
||||||
fTargetEntry(targetEntry),
|
fTargetEntry(targetEntry),
|
||||||
fTargetFile(&targetEntry, B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY),
|
fTargetFile(&targetEntry, B_CREATE_FILE | B_WRITE_ONLY),
|
||||||
fDownloadProgress(0.0)
|
fDownloadProgress(0.0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020, Haiku, Inc. All Rights Reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stephan Aßmus <[email protected]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "FetchUtils.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
#include <Entry.h>
|
||||||
|
#include <Node.h>
|
||||||
|
#include <TypeConstants.h>
|
||||||
|
|
||||||
|
namespace BPackageKit {
|
||||||
|
|
||||||
|
namespace BPrivate {
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef HAIKU_TARGET_PLATFORM_HAIKU
|
||||||
|
|
||||||
|
#define DL_COMPLETE_ATTR "Meta:DownloadCompleted"
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ bool
|
||||||
|
FetchUtils::IsDownloadCompleted(const char* path)
|
||||||
|
{
|
||||||
|
BEntry entry(path, true);
|
||||||
|
BNode node(&entry);
|
||||||
|
return IsDownloadCompleted(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ bool
|
||||||
|
FetchUtils::IsDownloadCompleted(BNode& node)
|
||||||
|
{
|
||||||
|
bool isComplete;
|
||||||
|
status_t status = _GetAttribute(node, DL_COMPLETE_ATTR,
|
||||||
|
B_BOOL_TYPE, &isComplete, sizeof(isComplete));
|
||||||
|
if (status != B_OK) {
|
||||||
|
// Most likely cause is that the attribute was not written,
|
||||||
|
// for example by previous versions of the Package Kit.
|
||||||
|
// Worst outcome of assuming a partial download should be
|
||||||
|
// a no-op range request.
|
||||||
|
isComplete = false;
|
||||||
|
}
|
||||||
|
return isComplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ status_t
|
||||||
|
FetchUtils::MarkDownloadComplete(BNode& node)
|
||||||
|
{
|
||||||
|
bool isComplete = true;
|
||||||
|
return _SetAttribute(node, DL_COMPLETE_ATTR,
|
||||||
|
B_BOOL_TYPE, &isComplete, sizeof(isComplete));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ status_t
|
||||||
|
FetchUtils::SetFileType(BNode& node, const char* type)
|
||||||
|
{
|
||||||
|
return _SetAttribute(node, "BEOS:TYPE",
|
||||||
|
B_MIME_STRING_TYPE, type, strlen(type) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t
|
||||||
|
FetchUtils::_SetAttribute(BNode& node, const char* attrName,
|
||||||
|
type_code type, const void* data, size_t size)
|
||||||
|
{
|
||||||
|
if (node.InitCheck() != B_OK)
|
||||||
|
return node.InitCheck();
|
||||||
|
|
||||||
|
ssize_t written = node.WriteAttr(attrName, type, 0, data, size);
|
||||||
|
if (written != (ssize_t)size) {
|
||||||
|
if (written < 0)
|
||||||
|
return (status_t)written;
|
||||||
|
return B_IO_ERROR;
|
||||||
|
}
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
FetchUtils::_GetAttribute(BNode& node, const char* attrName,
|
||||||
|
type_code type, void* data, size_t size)
|
||||||
|
{
|
||||||
|
if (node.InitCheck() != B_OK)
|
||||||
|
return node.InitCheck();
|
||||||
|
|
||||||
|
ssize_t read = node.ReadAttr(attrName, type, 0, data, size);
|
||||||
|
if (read != (ssize_t)size) {
|
||||||
|
if (read < 0)
|
||||||
|
return (status_t)read;
|
||||||
|
return B_IO_ERROR;
|
||||||
|
}
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // HAIKU_TARGET_PLATFORM_HAIKU
|
||||||
|
|
||||||
|
} // namespace BPrivate
|
||||||
|
|
||||||
|
} // namespace BPackageKit
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020, Stephan Aßmus <[email protected]>
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef _PACKAGE__PRIVATE__FETCH_UTILS_H_
|
||||||
|
#define _PACKAGE__PRIVATE__FETCH_UTILS_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include "SupportDefs.h"
|
||||||
|
#include <Node.h>
|
||||||
|
|
||||||
|
namespace BPackageKit {
|
||||||
|
|
||||||
|
namespace BPrivate {
|
||||||
|
|
||||||
|
|
||||||
|
class FetchUtils {
|
||||||
|
public:
|
||||||
|
static bool IsDownloadCompleted(const char* path);
|
||||||
|
static bool IsDownloadCompleted(BNode& node);
|
||||||
|
|
||||||
|
static status_t MarkDownloadComplete(const char* path);
|
||||||
|
static status_t MarkDownloadComplete(BNode& node);
|
||||||
|
|
||||||
|
static status_t SetFileType(BNode& node, const char* type);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static status_t _SetAttribute(BNode& node,
|
||||||
|
const char* attrName,
|
||||||
|
type_code type, const void* data,
|
||||||
|
size_t size);
|
||||||
|
static status_t _GetAttribute(BNode& node,
|
||||||
|
const char* attrName,
|
||||||
|
type_code type, void* data,
|
||||||
|
size_t size);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace BPrivate
|
||||||
|
|
||||||
|
} // namespace BPackageKit
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _PACKAGE__PRIVATE__FETCH_UTILS_H_
|
||||||
@@ -84,6 +84,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
|
|||||||
DownloadFileRequest.cpp
|
DownloadFileRequest.cpp
|
||||||
DropRepositoryRequest.cpp
|
DropRepositoryRequest.cpp
|
||||||
FetchFileJob.cpp
|
FetchFileJob.cpp
|
||||||
|
FetchUtils.cpp
|
||||||
InstallationLocationInfo.cpp
|
InstallationLocationInfo.cpp
|
||||||
Job.cpp
|
Job.cpp
|
||||||
PackageInfo.cpp
|
PackageInfo.cpp
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2015, Haiku, Inc. All Rights Reserved.
|
* Copyright 2013-2020, Haiku, Inc. All Rights Reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
@@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#include <package/manager/PackageManager.h>
|
#include <package/manager/PackageManager.h>
|
||||||
|
|
||||||
|
#include <glob.h>
|
||||||
|
|
||||||
#include <Catalog.h>
|
#include <Catalog.h>
|
||||||
#include <Directory.h>
|
#include <Directory.h>
|
||||||
#include <package/CommitTransactionResult.h>
|
#include <package/CommitTransactionResult.h>
|
||||||
@@ -31,6 +33,7 @@
|
|||||||
#include <package/ValidateChecksumJob.h>
|
#include <package/ValidateChecksumJob.h>
|
||||||
|
|
||||||
#include "FetchFileJob.h"
|
#include "FetchFileJob.h"
|
||||||
|
#include "FetchUtils.h"
|
||||||
#include "PackageManagerUtils.h"
|
#include "PackageManagerUtils.h"
|
||||||
|
|
||||||
#undef B_TRANSLATION_CONTEXT
|
#undef B_TRANSLATION_CONTEXT
|
||||||
@@ -38,6 +41,7 @@
|
|||||||
|
|
||||||
|
|
||||||
using BPackageKit::BPrivate::FetchFileJob;
|
using BPackageKit::BPrivate::FetchFileJob;
|
||||||
|
using BPackageKit::BPrivate::FetchUtils;
|
||||||
using BPackageKit::BPrivate::ValidateChecksumJob;
|
using BPackageKit::BPrivate::ValidateChecksumJob;
|
||||||
|
|
||||||
|
|
||||||
@@ -560,15 +564,40 @@ BPackageManager::_PreparePackageChanges(
|
|||||||
RemoteRepository* remoteRepository
|
RemoteRepository* remoteRepository
|
||||||
= dynamic_cast<RemoteRepository*>(package->Repository());
|
= dynamic_cast<RemoteRepository*>(package->Repository());
|
||||||
if (remoteRepository != NULL) {
|
if (remoteRepository != NULL) {
|
||||||
|
// first check if the package already exists in a previous
|
||||||
|
// transaction
|
||||||
|
bool alreadyDownloaded = false;
|
||||||
|
BPath path(&transaction->TransactionDirectory());
|
||||||
|
BPath parent;
|
||||||
|
if (path.GetParent(&parent) == B_OK) {
|
||||||
|
BString globPath = parent.Path();
|
||||||
|
globPath << "/*/" << fileName;
|
||||||
|
glob_t globbuf;
|
||||||
|
if (glob(globPath.String(), 0, NULL, &globbuf) == 0) {
|
||||||
|
path.Append(fileName);
|
||||||
|
if (BCopyEngine().CopyEntry(globbuf.gl_pathv[0],
|
||||||
|
path.Path()) == B_OK) {
|
||||||
|
alreadyDownloaded = FetchUtils::IsDownloadCompleted(
|
||||||
|
path.Path());
|
||||||
|
printf("Re-using download '%s' from previous "
|
||||||
|
"transaction%s\n", globbuf.gl_pathv[0],
|
||||||
|
alreadyDownloaded ? "" : " (partial)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!alreadyDownloaded) {
|
||||||
// download the package
|
// download the package
|
||||||
BString url = remoteRepository->Config().PackagesURL();
|
BString url = remoteRepository->Config().PackagesURL();
|
||||||
url << '/' << fileName;
|
url << '/' << fileName;
|
||||||
|
|
||||||
status_t error = DownloadPackage(url, entry,
|
status_t error = DownloadPackage(url, entry,
|
||||||
package->Info().Checksum());
|
package->Info().Checksum());
|
||||||
if (error != B_OK)
|
if (error != B_OK) {
|
||||||
DIE(error, "Failed to download package %s",
|
DIE(error, "Failed to download package %s",
|
||||||
package->Info().Name().String());
|
package->Info().Name().String());
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (package->Repository() != &installationRepository) {
|
} else if (package->Repository() != &installationRepository) {
|
||||||
// clone the existing package
|
// clone the existing package
|
||||||
LocalRepository* localRepository
|
LocalRepository* localRepository
|
||||||
|
|||||||
Reference in New Issue
Block a user