Skip to content

Commit

Permalink
Blacklist: remember the X-Request-ID
Browse files Browse the repository at this point in the history
Issue #6420
Store the X-Request-ID in the SyncFileItem and also in the blacklist.
Note that for consistency reason, the X-Request-ID is also in the
SyncFileItem if the request succeeds.

Currently there is no UI to access it, but it can be queried with sql
commands
  • Loading branch information
ogoffart committed Apr 4, 2018
1 parent f929982 commit 36d02bc
Show file tree
Hide file tree
Showing 14 changed files with 53 additions and 16 deletions.
20 changes: 15 additions & 5 deletions src/common/syncjournaldb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ bool SyncJournalDb::checkConnect()
return sqlFail("prepare _deleteUploadInfoQuery", _deleteUploadInfoQuery);
}

QByteArray sql("SELECT lastTryEtag, lastTryModtime, retrycount, errorstring, lastTryTime, ignoreDuration, renameTarget, errorCategory "
QByteArray sql("SELECT lastTryEtag, lastTryModtime, retrycount, errorstring, lastTryTime, ignoreDuration, renameTarget, errorCategory, requestId "
"FROM blacklist WHERE path=?1");
if (Utility::fsCasePreserving()) {
// if the file system is case preserving we have to check the blacklist
Expand Down Expand Up @@ -753,6 +753,16 @@ bool SyncJournalDb::updateErrorBlacklistTableStructure()
commitInternal("update database structure: add errorCategory col");
}

if (columns.indexOf("requestId") == -1) {
SqlQuery query(_db);
query.prepare("ALTER TABLE blacklist ADD COLUMN requestId VARCHAR(36);");
if (!query.exec()) {
sqlFail("updateBlacklistTableStructure: Add requestId", query);
re = false;
}
commitInternal("update database structure: add errorCategory col");
}

SqlQuery query(_db);
query.prepare("CREATE INDEX IF NOT EXISTS blacklist_index ON blacklist(path collate nocase);");
if (!query.exec()) {
Expand Down Expand Up @@ -1443,8 +1453,6 @@ SyncJournalErrorBlacklistRecord SyncJournalDb::errorBlacklistEntry(const QString
if (file.isEmpty())
return entry;

// SELECT lastTryEtag, lastTryModtime, retrycount, errorstring

if (checkConnect()) {
_getErrorBlacklistQuery.reset_and_clear_bindings();
_getErrorBlacklistQuery.bindValue(1, file);
Expand All @@ -1459,6 +1467,7 @@ SyncJournalErrorBlacklistRecord SyncJournalDb::errorBlacklistEntry(const QString
entry._renameTarget = _getErrorBlacklistQuery.stringValue(6);
entry._errorCategory = static_cast<SyncJournalErrorBlacklistRecord::Category>(
_getErrorBlacklistQuery.intValue(7));
entry._requestId = _getErrorBlacklistQuery.baValue(8);
entry._file = file;
}
}
Expand Down Expand Up @@ -1578,8 +1587,8 @@ void SyncJournalDb::setErrorBlacklistEntry(const SyncJournalErrorBlacklistRecord

if (!_setErrorBlacklistQuery.initOrReset(QByteArrayLiteral(
"INSERT OR REPLACE INTO blacklist "
"(path, lastTryEtag, lastTryModtime, retrycount, errorstring, lastTryTime, ignoreDuration, renameTarget, errorCategory) "
"VALUES ( ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)"), _db)) {
"(path, lastTryEtag, lastTryModtime, retrycount, errorstring, lastTryTime, ignoreDuration, renameTarget, errorCategory, requestId) "
"VALUES ( ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)"), _db)) {
return;
}

Expand All @@ -1592,6 +1601,7 @@ void SyncJournalDb::setErrorBlacklistEntry(const SyncJournalErrorBlacklistRecord
_setErrorBlacklistQuery.bindValue(7, item._ignoreDuration);
_setErrorBlacklistQuery.bindValue(8, item._renameTarget);
_setErrorBlacklistQuery.bindValue(9, item._errorCategory);
_setErrorBlacklistQuery.bindValue(10, item._requestId);
_setErrorBlacklistQuery.exec();
}

Expand Down
3 changes: 3 additions & 0 deletions src/common/syncjournalfilerecord.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ class OCSYNC_EXPORT SyncJournalErrorBlacklistRecord
QString _file;
QString _renameTarget;

/// The last X-Request-ID of the request that failled
QByteArray _requestId;

bool isValid() const;
};

Expand Down
5 changes: 5 additions & 0 deletions src/libsync/abstractnetworkjob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,11 @@ QByteArray AbstractNetworkJob::responseTimestamp()
return _responseTimestamp;
}

QByteArray AbstractNetworkJob::requestId()
{
return _reply ? _reply->request().rawHeader("X-Request-ID") : QByteArray();
}

QString AbstractNetworkJob::errorString() const
{
if (_timedout) {
Expand Down
2 changes: 2 additions & 0 deletions src/libsync/abstractnetworkjob.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ class OWNCLOUDSYNC_EXPORT AbstractNetworkJob : public QObject
bool followRedirects() const { return _followRedirects; }

QByteArray responseTimestamp();
/* Content of the X-Request-ID header. (Only set after the request is sent) */
QByteArray requestId();

qint64 timeoutMsec() const { return _timer.interval(); }
bool timedOut() const { return _timedout; }
Expand Down
1 change: 1 addition & 0 deletions src/libsync/owncloudpropagator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ static SyncJournalErrorBlacklistRecord createBlacklistEntry(
entry._lastTryTime = Utility::qDateTimeToTime_t(QDateTime::currentDateTimeUtc());
entry._renameTarget = item._renameTarget;
entry._retryCount = old._retryCount + 1;
entry._requestId = item._requestId;

static qint64 minBlacklistTime(getMinBlacklistTime());
static qint64 maxBlacklistTime(qMax(getMaxBlacklistTime(), minBlacklistTime));
Expand Down
6 changes: 4 additions & 2 deletions src/libsync/propagatedownload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -528,9 +528,12 @@ void PropagateDownloadFile::slotGetFinished()
GETFileJob *job = _job;
ASSERT(job);

_item->_httpErrorCode = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
_item->_responseTimeStamp = job->responseTimestamp();
_item->_requestId = job->requestId();

QNetworkReply::NetworkError err = job->reply()->error();
if (err != QNetworkReply::NoError) {
_item->_httpErrorCode = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();

// If we sent a 'Range' header and get 416 back, we want to retry
// without the header.
Expand Down Expand Up @@ -603,7 +606,6 @@ void PropagateDownloadFile::slotGetFinished()
// so make sure we have the up-to-date time
_item->_modtime = job->lastModified();
}
_item->_responseTimeStamp = job->responseTimestamp();

_tmpFile.close();
_tmpFile.flush();
Expand Down
4 changes: 2 additions & 2 deletions src/libsync/propagateremotedelete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ void PropagateRemoteDelete::slotDeleteJobFinished()
QNetworkReply::NetworkError err = _job->reply()->error();
const int httpStatus = _job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
_item->_httpErrorCode = httpStatus;
_item->_responseTimeStamp = _job->responseTimestamp();
_item->_requestId = _job->requestId();

if (err != QNetworkReply::NoError && err != QNetworkReply::ContentNotFoundError) {
SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode,
Expand All @@ -102,8 +104,6 @@ void PropagateRemoteDelete::slotDeleteJobFinished()
return;
}

_item->_responseTimeStamp = _job->responseTimestamp();

// A 404 reply is also considered a success here: We want to make sure
// a file is gone from the server. It not being there in the first place
// is ok. This will happen for files that are in the DB but not on
Expand Down
3 changes: 2 additions & 1 deletion src/libsync/propagateremotemkdir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ void PropagateRemoteMkdir::slotMkcolJobFinished()

QNetworkReply::NetworkError err = _job->reply()->error();
_item->_httpErrorCode = _job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
_item->_responseTimeStamp = _job->responseTimestamp();
_item->_requestId = _job->requestId();

if (_item->_httpErrorCode == 405) {
// This happens when the directory already exists. Nothing to do.
Expand All @@ -102,7 +104,6 @@ void PropagateRemoteMkdir::slotMkcolJobFinished()
return;
}

_item->_responseTimeStamp = _job->responseTimestamp();
_item->_fileId = _job->reply()->rawHeader("OC-FileId");

if (_item->_fileId.isEmpty()) {
Expand Down
4 changes: 2 additions & 2 deletions src/libsync/propagateremotemove.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ void PropagateRemoteMove::slotMoveJobFinished()

QNetworkReply::NetworkError err = _job->reply()->error();
_item->_httpErrorCode = _job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
_item->_responseTimeStamp = _job->responseTimestamp();
_item->_requestId = _job->requestId();

if (err != QNetworkReply::NoError) {
SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode,
Expand All @@ -125,8 +127,6 @@ void PropagateRemoteMove::slotMoveJobFinished()
return;
}

_item->_responseTimeStamp = _job->responseTimestamp();

if (_item->_httpErrorCode != 201) {
// Normally we expect "201 Created"
// If it is not the case, it might be because of a proxy or gateway intercepting the request, so we must
Expand Down
1 change: 1 addition & 0 deletions src/libsync/propagateupload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ bool PollJob::finished()
QNetworkReply::NetworkError err = reply()->error();
if (err != QNetworkReply::NoError) {
_item->_httpErrorCode = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
_item->_requestId = requestId();
_item->_status = classifyError(err, _item->_httpErrorCode);
_item->_errorString = errorString();

Expand Down
7 changes: 6 additions & 1 deletion src/libsync/propagateuploadng.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ void PropagateUploadFileNG::slotPropfindFinishedWithError()
auto httpErrorCode = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
auto status = classifyError(err, httpErrorCode, &propagator()->_anotherSyncNeeded);
if (status == SyncFileItem::FatalError) {
_item->_requestId = job->requestId();
propagator()->_activeJobList.removeOne(this);
abortWithError(status, job->errorStringParsingBody());
return;
Expand All @@ -196,6 +197,7 @@ void PropagateUploadFileNG::slotDeleteJobFinished()
const int httpStatus = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
SyncFileItem::Status status = classifyError(err, httpStatus);
if (status == SyncFileItem::FatalError) {
_item->_requestId = job->requestId();
abortWithError(status, job->errorString());
return;
} else {
Expand Down Expand Up @@ -252,6 +254,7 @@ void PropagateUploadFileNG::slotMkColFinished(QNetworkReply::NetworkError)
_item->_httpErrorCode = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();

if (err != QNetworkReply::NoError || _item->_httpErrorCode != 201) {
_item->_requestId = job->requestId();
SyncFileItem::Status status = classifyError(err, _item->_httpErrorCode,
&propagator()->_anotherSyncNeeded);
abortWithError(status, job->errorStringParsingBody());
Expand Down Expand Up @@ -355,6 +358,7 @@ void PropagateUploadFileNG::slotPutFinished()

if (err != QNetworkReply::NoError) {
_item->_httpErrorCode = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
_item->_requestId = job->requestId();
commonErrorHandling(job);
return;
}
Expand Down Expand Up @@ -435,6 +439,8 @@ void PropagateUploadFileNG::slotMoveJobFinished()
slotJobDestroyed(job); // remove it from the _jobs list
QNetworkReply::NetworkError err = job->reply()->error();
_item->_httpErrorCode = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
_item->_responseTimeStamp = job->responseTimestamp();
_item->_requestId = job->requestId();

if (err != QNetworkReply::NoError) {
commonErrorHandling(job);
Expand Down Expand Up @@ -465,7 +471,6 @@ void PropagateUploadFileNG::slotMoveJobFinished()
abortWithError(SyncFileItem::NormalError, tr("Missing ETag from server"));
return;
}
_item->_responseTimeStamp = job->responseTimestamp();
finalize();
}

Expand Down
4 changes: 2 additions & 2 deletions src/libsync/propagateuploadv1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ void PropagateUploadFileV1::slotPutFinished()
}

_item->_httpErrorCode = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
_item->_responseTimeStamp = job->responseTimestamp();
_item->_requestId = job->requestId();
QNetworkReply::NetworkError err = job->reply()->error();
if (err != QNetworkReply::NoError) {
commonErrorHandling(job);
Expand Down Expand Up @@ -299,8 +301,6 @@ void PropagateUploadFileV1::slotPutFinished()

_item->_etag = etag;

_item->_responseTimeStamp = job->responseTimestamp();

if (job->reply()->rawHeader("X-OC-MTime") != "accepted") {
// X-OC-MTime is supported since owncloud 5.0. But not when chunking.
// Normally Owncloud 6 always puts X-OC-MTime
Expand Down
1 change: 1 addition & 0 deletions src/libsync/syncfileitem.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ class SyncFileItem
RemotePermissions _remotePerm;
QString _errorString; // Contains a string only in case of error
QByteArray _responseTimeStamp;
QByteArray _requestId; // X-Request-Id of the failed request
quint32 _affectedItems; // the number of affected items by the operation on this item.
// usually this value is 1, but for removes on dirs, it might be much higher.

Expand Down
8 changes: 7 additions & 1 deletion test/testblacklist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ private slots:
auto &modifier = remote ? fakeFolder.remoteModifier() : fakeFolder.localModifier();

int counter = 0;
fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &, QIODevice *) -> QNetworkReply * {
QByteArray reqId;
fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *) -> QNetworkReply * {
reqId = req.rawHeader("X-Request-ID");
if (!remote && op == QNetworkAccessManager::PutOperation)
++counter;
if (remote && op == QNetworkAccessManager::GetOperation)
Expand Down Expand Up @@ -82,6 +84,7 @@ private slots:
QCOMPARE(entry._retryCount, 1);
QCOMPARE(counter, 1);
QVERIFY(entry._ignoreDuration > 0);
QCOMPARE(entry._requestId, reqId);

if (remote)
QCOMPARE(journalRecord(fakeFolder, "A")._etag, initialEtag);
Expand All @@ -102,6 +105,7 @@ private slots:
QCOMPARE(entry._retryCount, 1);
QCOMPARE(counter, 1);
QVERIFY(entry._ignoreDuration > 0);
QCOMPARE(entry._requestId, reqId);

if (remote)
QCOMPARE(journalRecord(fakeFolder, "A")._etag, initialEtag);
Expand All @@ -128,6 +132,7 @@ private slots:
QCOMPARE(entry._retryCount, 2);
QCOMPARE(counter, 2);
QVERIFY(entry._ignoreDuration > 0);
QCOMPARE(entry._requestId, reqId);

if (remote)
QCOMPARE(journalRecord(fakeFolder, "A")._etag, initialEtag);
Expand All @@ -149,6 +154,7 @@ private slots:
QCOMPARE(entry._retryCount, 3);
QCOMPARE(counter, 3);
QVERIFY(entry._ignoreDuration > 0);
QCOMPARE(entry._requestId, reqId);

if (remote)
QCOMPARE(journalRecord(fakeFolder, "A")._etag, initialEtag);
Expand Down

0 comments on commit 36d02bc

Please sign in to comment.