CFAPI: Handle cancelation of hydration requests

Signed-off-by: Felix Weilbach <felix.weilbach@nextcloud.com>
This commit is contained in:
Felix Weilbach 2021-03-16 13:31:12 +01:00 committed by Felix Weilbach (Rebase PR Action)
parent 6abb0b2184
commit 9bf5b5c7ba
7 changed files with 94 additions and 1 deletions

View file

@ -342,6 +342,15 @@ void GETFileJob::slotReadyRead()
} }
} }
void GETFileJob::cancel()
{
if (reply()->isRunning()) {
reply()->abort();
}
emit canceled();
}
void GETFileJob::onTimedOut() void GETFileJob::onTimedOut()
{ {
qCWarning(lcGetJob) << "Timeout" << (reply() ? reply()->request().url() : path()); qCWarning(lcGetJob) << "Timeout" << (reply() ? reply()->request().url() : path());

View file

@ -83,6 +83,8 @@ public:
} }
} }
void cancel();
void newReplyHook(QNetworkReply *reply) override; void newReplyHook(QNetworkReply *reply) override;
void setBandwidthManager(BandwidthManager *bwm); void setBandwidthManager(BandwidthManager *bwm);
@ -108,6 +110,7 @@ public:
void setExpectedContentLength(qint64 size) { _expectedContentLength = size; } void setExpectedContentLength(qint64 size) { _expectedContentLength = size; }
signals: signals:
void canceled();
void finishedSignal(); void finishedSignal();
void downloadProgress(qint64, qint64); void downloadProgress(qint64, qint64);
private slots: private slots:

View file

@ -58,7 +58,6 @@ void cfApiSendTransferInfo(const CF_CONNECTION_KEY &connectionKey, const CF_TRAN
} }
} }
void CALLBACK cfApiFetchDataCallback(const CF_CALLBACK_INFO *callbackInfo, const CF_CALLBACK_PARAMETERS *callbackParameters) void CALLBACK cfApiFetchDataCallback(const CF_CALLBACK_INFO *callbackInfo, const CF_CALLBACK_PARAMETERS *callbackParameters)
{ {
qDebug(lcCfApiWrapper) << "Fetch data callback called. File size:" << callbackInfo->FileSize.QuadPart; qDebug(lcCfApiWrapper) << "Fetch data callback called. File size:" << callbackInfo->FileSize.QuadPart;
@ -181,8 +180,27 @@ void CALLBACK cfApiFetchDataCallback(const CF_CALLBACK_INFO *callbackInfo, const
} }
} }
void CALLBACK cfApiCancelFetchData(const CF_CALLBACK_INFO *callbackInfo, const CF_CALLBACK_PARAMETERS * /*callbackParameters*/)
{
const auto path = QString(QString::fromWCharArray(callbackInfo->VolumeDosName) + QString::fromWCharArray(callbackInfo->NormalizedPath));
qInfo(lcCfApiWrapper) << "Cancel fetch data of" << path;
auto vfs = reinterpret_cast<OCC::VfsCfApi *>(callbackInfo->CallbackContext);
Q_ASSERT(vfs->metaObject()->className() == QByteArrayLiteral("OCC::VfsCfApi"));
const auto requestId = QString::number(callbackInfo->TransferKey.QuadPart, 16);
const auto invokeResult = QMetaObject::invokeMethod(
vfs, [=] { vfs->cancelHydration(requestId, path); }, Qt::QueuedConnection);
if (!invokeResult) {
qCritical(lcCfApiWrapper) << "Failed to cancel hydration for" << path << requestId;
}
}
CF_CALLBACK_REGISTRATION cfApiCallbacks[] = { CF_CALLBACK_REGISTRATION cfApiCallbacks[] = {
{ CF_CALLBACK_TYPE_FETCH_DATA, cfApiFetchDataCallback }, { CF_CALLBACK_TYPE_FETCH_DATA, cfApiFetchDataCallback },
{ CF_CALLBACK_TYPE_CANCEL_FETCH_DATA, cfApiCancelFetchData },
CF_CALLBACK_REGISTRATION_END CF_CALLBACK_REGISTRATION_END
}; };

View file

@ -116,6 +116,15 @@ void OCC::HydrationJob::start()
connect(_server, &QLocalServer::newConnection, this, &HydrationJob::onNewConnection); connect(_server, &QLocalServer::newConnection, this, &HydrationJob::onNewConnection);
} }
void OCC::HydrationJob::cancel()
{
if (!_job) {
return;
}
_job->cancel();
}
void OCC::HydrationJob::emitFinished(Status status) void OCC::HydrationJob::emitFinished(Status status)
{ {
_status = status; _status = status;
@ -131,6 +140,16 @@ void OCC::HydrationJob::emitFinished(Status status)
} }
} }
void OCC::HydrationJob::emitCanceled()
{
connect(_socket, &QLocalSocket::disconnected, this, [=] {
_socket->close();
});
_socket->disconnectFromServer();
emit canceled(this);
}
void OCC::HydrationJob::onNewConnection() void OCC::HydrationJob::onNewConnection()
{ {
Q_ASSERT(!_socket); Q_ASSERT(!_socket);
@ -140,9 +159,16 @@ void OCC::HydrationJob::onNewConnection()
_socket = _server->nextPendingConnection(); _socket = _server->nextPendingConnection();
_job = new GETFileJob(_account, _remotePath + _folderPath, _socket, {}, {}, 0, this); _job = new GETFileJob(_account, _remotePath + _folderPath, _socket, {}, {}, 0, this);
connect(_job, &GETFileJob::finishedSignal, this, &HydrationJob::onGetFinished); connect(_job, &GETFileJob::finishedSignal, this, &HydrationJob::onGetFinished);
connect(_job, &GETFileJob::canceled, this, &HydrationJob::onGetCanceled);
_job->start(); _job->start();
} }
void OCC::HydrationJob::onGetCanceled()
{
qCInfo(lcHydration) << "GETFileJob canceled" << _requestId << _folderPath << _job->reply()->error();
emitCanceled();
}
void OCC::HydrationJob::onGetFinished() void OCC::HydrationJob::onGetFinished()
{ {
qCInfo(lcHydration) << "GETFileJob finished" << _requestId << _folderPath << _job->reply()->error(); qCInfo(lcHydration) << "GETFileJob finished" << _requestId << _folderPath << _job->reply()->error();

View file

@ -57,15 +57,19 @@ public:
Status status() const; Status status() const;
void start(); void start();
void cancel();
signals: signals:
void finished(HydrationJob *job); void finished(HydrationJob *job);
void canceled(HydrationJob *job);
private: private:
void emitFinished(Status status); void emitFinished(Status status);
void emitCanceled();
void onNewConnection(); void onNewConnection();
void onGetFinished(); void onGetFinished();
void onGetCanceled();
AccountPtr _account; AccountPtr _account;
QString _remotePath; QString _remotePath;

View file

@ -264,6 +264,19 @@ Vfs::AvailabilityResult VfsCfApi::availability(const QString &folderPath)
return AvailabilityError::NoSuchItem; return AvailabilityError::NoSuchItem;
} }
void VfsCfApi::cancelHydration(const QString &requestId, const QString & /*path*/)
{
// Find matching hydration job for request id
const auto hydrationJobsIter = std::find_if(d->hydrationJobs.cbegin(), d->hydrationJobs.cend(), [&](const HydrationJob *job) {
return job->requestId() == requestId;
});
// If found, cancel it
if (hydrationJobsIter != d->hydrationJobs.cend()) {
(*hydrationJobsIter)->cancel();
}
}
void VfsCfApi::requestHydration(const QString &requestId, const QString &path) void VfsCfApi::requestHydration(const QString &requestId, const QString &path)
{ {
qCInfo(lcCfApi) << "Received request to hydrate" << path << requestId; qCInfo(lcCfApi) << "Received request to hydrate" << path << requestId;
@ -331,6 +344,7 @@ void VfsCfApi::scheduleHydrationJob(const QString &requestId, const QString &fol
job->setRequestId(requestId); job->setRequestId(requestId);
job->setFolderPath(folderPath); job->setFolderPath(folderPath);
connect(job, &HydrationJob::finished, this, &VfsCfApi::onHydrationJobFinished); connect(job, &HydrationJob::finished, this, &VfsCfApi::onHydrationJobFinished);
connect(job, &HydrationJob::canceled, this, &VfsCfApi::onHydrationJobCanceled);
d->hydrationJobs << job; d->hydrationJobs << job;
job->start(); job->start();
emit hydrationRequestReady(requestId); emit hydrationRequestReady(requestId);
@ -347,6 +361,22 @@ void VfsCfApi::onHydrationJobFinished(HydrationJob *job)
} }
} }
void VfsCfApi::onHydrationJobCanceled(HydrationJob *job)
{
const auto folderPath = job->localPath();
const auto folderRelativePath = job->folderPath();
// Remove placeholder file because there might be already pumped
// some data into it
QFile::remove(folderPath + folderRelativePath);
// Create a new placeholder file
SyncJournalFileRecord record;
params().journal->getFileRecord(folderRelativePath, &record);
const auto item = SyncFileItem::fromSyncJournalFileRecord(record);
createPlaceholder(*item);
}
VfsCfApi::HydratationAndPinStates VfsCfApi::computeRecursiveHydrationAndPinStates(const QString &folderPath, const Optional<PinState> &basePinState) VfsCfApi::HydratationAndPinStates VfsCfApi::computeRecursiveHydrationAndPinStates(const QString &folderPath, const Optional<PinState> &basePinState)
{ {
Q_ASSERT(!folderPath.endsWith('/')); Q_ASSERT(!folderPath.endsWith('/'));

View file

@ -53,6 +53,8 @@ public:
Optional<PinState> pinState(const QString &folderPath) override; Optional<PinState> pinState(const QString &folderPath) override;
AvailabilityResult availability(const QString &folderPath) override; AvailabilityResult availability(const QString &folderPath) override;
void cancelHydration(const QString &requestId, const QString &path);
public slots: public slots:
void requestHydration(const QString &requestId, const QString &path); void requestHydration(const QString &requestId, const QString &path);
void fileStatusChanged(const QString &systemFileName, SyncFileStatus fileStatus) override; void fileStatusChanged(const QString &systemFileName, SyncFileStatus fileStatus) override;
@ -68,6 +70,7 @@ protected:
private: private:
void scheduleHydrationJob(const QString &requestId, const QString &folderPath); void scheduleHydrationJob(const QString &requestId, const QString &folderPath);
void onHydrationJobFinished(HydrationJob *job); void onHydrationJobFinished(HydrationJob *job);
void onHydrationJobCanceled(HydrationJob *job);
struct HasHydratedDehydrated { struct HasHydratedDehydrated {
bool hasHydrated = false; bool hasHydrated = false;