diff --git a/src/mirall/propagator_qnam.cpp b/src/mirall/propagator_qnam.cpp index 6c0808c92..5b4cb067f 100644 --- a/src/mirall/propagator_qnam.cpp +++ b/src/mirall/propagator_qnam.cpp @@ -20,6 +20,7 @@ #include "utility.h" #include "filesystem.h" #include "propagatorjobs.h" +#include #include #include #include @@ -83,6 +84,14 @@ void PUTFileJob::slotTimeout() { reply()->abort(); } +void PollJob::start() +{ + setReply(davRequest("GET", path())); + connect(reply(), SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(resetTimeout())); + AbstractNetworkJob::start(); +} + + void PropagateUploadFileQNAM::start() { if (_propagator->_abortRequested.fetchAndAddRelaxed(0)) @@ -206,6 +215,7 @@ void PropagateUploadFileQNAM::startNextChunk() quint64 fileSize = _item._size; QMap headers; headers["OC-Total-Length"] = QByteArray::number(fileSize); + headers["OC-Async"] = "1"; headers["Content-Type"] = "application/octet-stream"; headers["X-OC-Mtime"] = QByteArray::number(qint64(_item._modtime)); if (!_item._etag.isEmpty() && _item._etag != "empty_etag") { @@ -292,6 +302,19 @@ void PropagateUploadFileQNAM::slotPutFinished() return; } + _item._httpErrorCode = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + // The server needs some time to process the request and provide with a poll URL + if (_item._httpErrorCode == 202) { + QString path = QString::fromUtf8(QByteArray::fromPercentEncoding(job->reply()->rawHeader("OC-Finish-Poll"))); + if (path.isEmpty()) { + _propagator->_activeJobs--; + done(SyncFileItem::NormalError, tr("Poll URL missing")); + return; + } + startPollJob(path); + return; + } + bool finished = job->reply()->hasRawHeader("ETag"); if (!finished) { @@ -383,6 +406,51 @@ void PropagateUploadFileQNAM::slotUploadProgress(qint64 sent, qint64) emit progress(_item, sent + _currentChunk * chunkSize()); } +void PropagateUploadFileQNAM::startPollJob(const QString& path) +{ + PollJob* job = new PollJob(AccountManager::instance()->account(), path, this); + job->setTimeout(_propagator->httpTimeout() * 10000); + connect(job, SIGNAL(finishedSignal(bool)), SLOT(slotPollFinished(bool))); +} + +void PropagateUploadFileQNAM::slotPollFinished(bool success) +{ + PollJob *job = qobject_cast(sender()); + Q_ASSERT(job); + qDebug() << Q_FUNC_INFO << job->reply()->request().url() << "FINISHED WITH STATUS" + << job->reply()->error() + << (job->reply()->error() == QNetworkReply::NoError ? QLatin1String("") : job->reply()->errorString()) + << job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute) + << job->reply()->attribute(QNetworkRequest::HttpReasonPhraseAttribute); + QNetworkReply::NetworkError err = job->reply()->error(); + if (!success || err != QNetworkReply::NoError) { + startPollJob(job->path()); + return; + } + + bool ok = false; + QVariantMap status = QtJson::parse(QString::fromUtf8(job->reply()->readAll()), ok).toMap(); + if (!ok || status.isEmpty()) { + _propagator->_activeJobs--; + done(SyncFileItem::NormalError, tr("Invalid json reply from the poll URL")); + // FIXME: retry? + return; + } + + // the following code only happens after all chunks were uploaded. + // the file id should only be empty for new files up- or downloaded + QByteArray fid = status["fileid"].toByteArray(); + if( !fid.isEmpty() ) { + if( !_item._fileId.isEmpty() && _item._fileId != fid ) { + qDebug() << "WARN: File ID changed!" << _item._fileId << fid; + } + _item._fileId = fid; + } + + _item._etag = status["etag"].toByteArray(); + _item._responseTimeStamp = job->responseTimestamp(); + finalize(_item); +} void PropagateUploadFileQNAM::abort() { diff --git a/src/mirall/propagator_qnam.h b/src/mirall/propagator_qnam.h index 3d87e2223..371ff40c3 100644 --- a/src/mirall/propagator_qnam.h +++ b/src/mirall/propagator_qnam.h @@ -80,6 +80,30 @@ signals: void uploadProgress(qint64,qint64); }; +class PollJob : public AbstractNetworkJob { + Q_OBJECT +public: + // Takes ownership of the device + explicit PollJob(Account* account, const QString &path, QObject *parent) + : AbstractNetworkJob(account, path, parent) {} + + virtual void start(); + + virtual bool finished() { + emit finishedSignal(true); + return true; + } + + virtual void slotTimeout() { +// emit finishedSignal(false); +// deleteLater(); + reply()->abort(); + } + +signals: + void finishedSignal(bool success); +}; + class PropagateUploadFileQNAM : public PropagateItemJob { Q_OBJECT @@ -96,10 +120,13 @@ public: void start(); private slots: void slotPutFinished(); + void slotPollFinished(bool success); void slotUploadProgress(qint64,qint64); void abort(); void startNextChunk(); void finalize(const SyncFileItem&); +private: + void startPollJob(const QString& path); };