HTTP/2 Support
authorOlivier Goffart <ogoffart@woboq.com>
Mon, 22 May 2017 12:41:06 +0000 (14:41 +0200)
committerOlivier Goffart <olivier@woboq.com>
Mon, 17 Jul 2017 06:20:17 +0000 (08:20 +0200)
We need Qt 5.9 for HTTP2 because, even if Qt 5.8 already has support
for it, there is some critical bug in the HTTP2 implementation which
make it unusable [ https://codereview.qt-project.org/186050 and
https://codereview.qt-project.org/186066 ]

When using HTTP2, we can use many more parallel network request, this
is especially good for small file handling

Lower the priority of the GET and PUT propagation jobs, so the quota
or selective sync ui PROPFIND will not be blocked by them

src/libsync/accessmanager.cpp
src/libsync/account.h
src/libsync/connectionvalidator.cpp
src/libsync/owncloudpropagator.cpp
src/libsync/propagatedownload.cpp
src/libsync/propagateupload.cpp
src/libsync/syncengine.cpp

index 4c19c20a196de1effc2274b9eb03fe6a32678976..e9f485d93c830b6d4f63ae124681a6fdb37374c4 100644 (file)
@@ -93,6 +93,11 @@ QNetworkReply *AccessManager::createRequest(QNetworkAccessManager::Operation op,
     qInfo(lcAccessManager) << op << verb << newRequest.url().toString() << "has X-Request-ID" << requestId;
     newRequest.setRawHeader("X-Request-ID", requestId);
 
+#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
+    // only enable HTTP2 with Qt 5.9 because Qt 5.8.0 has too many bugs (only use one connection if the server does not support HTTP2)
+    newRequest.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
+#endif
+
     return QNetworkAccessManager::createRequest(op, newRequest, outgoingData);
 }
 
index b34342d4525b4180de42b3deb5bb219f7b5f667e..de1cd0293c9322f6f21c29e02052a54e0fe62445 100644 (file)
@@ -189,6 +189,10 @@ public:
     /** Detects a specific bug in older server versions */
     bool rootEtagChangesNotOnlySubFolderEtags();
 
+    /** True when the server supports HTTP2  */
+    bool isHttp2Supported() { return _http2Supported; }
+    void setHttp2Supported(bool value) { _http2Supported = value; };
+
     void clearCookieJar();
     void lendCookieJarTo(QNetworkAccessManager *guest);
     QString cookieJarPath();
@@ -247,6 +251,7 @@ private:
     QuotaInfo *_quotaInfo;
     QSharedPointer<QNetworkAccessManager> _am;
     QScopedPointer<AbstractCredentials> _credentials;
+    bool _http2Supported = false;
 
     /// Certificates that were explicitly rejected by the user
     QList<QSslCertificate> _rejectedCertificates;
index 0b5897f1ff688fb2a8981b90869ca6ede9565547..9eff065dee5572ffe63743dad245c315c9ff913d 100644 (file)
@@ -282,10 +282,18 @@ bool ConnectionValidator::setAndCheckServerVersion(const QString &version)
         reportResult(ServerVersionMismatch);
         return false;
     }
-
     // We attempt to work with servers >= 5.0.0 but warn users.
     // Check usages of Account::serverVersionUnsupported() for details.
 
+#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
+    // Record that the server supports HTTP/2
+    if (auto job = qobject_cast<AbstractNetworkJob *>(sender())) {
+        if (auto reply = job->reply()) {
+            _account->setHttp2Supported(
+                reply->attribute(QNetworkRequest::HTTP2WasUsedAttribute).toBool());
+        }
+    }
+#endif
     return true;
 }
 
index 016f0cdc84d7da110615a825dfc575ba8ba04ad8..e76e00e8e15035f426ecec1661626ecbb229e250 100644 (file)
@@ -84,18 +84,18 @@ int OwncloudPropagator::maximumActiveTransferJob()
         // disable parallelism when there is a network limit.
         return 1;
     }
-    return qCeil(hardMaximumActiveJob() / 2.);
+    return qMax(3, qCeil(hardMaximumActiveJob() / 2.));
 }
 
 /* The maximum number of active jobs in parallel  */
 int OwncloudPropagator::hardMaximumActiveJob()
 {
     static int max = qgetenv("OWNCLOUD_MAX_PARALLEL").toUInt();
-    if (!max) {
-        max = 6; //default (Qt cannot do more anyway)
-        // TODO: increase this number when using HTTP2
-    }
-    return max;
+    if (max)
+        return max;
+    if (_account->isHttp2Supported())
+        return 20;
+    return 6; // (Qt cannot do more anyway)
 }
 
 PropagateItemJob::~PropagateItemJob()
index b5d6ac00406a3fbbea47ff8c05d411cd07d1df8c..2a6ca14394dc0f9cc2073e7eb63568d2f5c8d6b2 100644 (file)
@@ -118,6 +118,8 @@ void GETFileJob::start()
         req.setRawHeader(it.key(), it.value());
     }
 
+    req.setPriority(QNetworkRequest::LowPriority); // Long downloads must not block non-propagation jobs.
+
     if (_directDownloadUrl.isEmpty()) {
         sendRequest("GET", makeDavUrl(path()), req);
     } else {
index 757aee1c4485078005c6bbb1215c8ed139fa584e..1cd0266277e0747559d5c6876ce92be6d99a60db 100644 (file)
@@ -79,6 +79,8 @@ void PUTFileJob::start()
         req.setRawHeader(it.key(), it.value());
     }
 
+    req.setPriority(QNetworkRequest::LowPriority); // Long uploads must not block non-propagation jobs.
+
     if (_url.isValid()) {
         sendRequest("PUT", _url, req, _device);
     } else {
index 8fa6eb7b726152476257ef7c9b8cb2f083dcec12..967f5ca6a002826c727d734ac4fa07ccc69f4f8f 100644 (file)
@@ -852,7 +852,7 @@ void SyncEngine::startSync()
     _discoveryMainThread->setParent(this);
     connect(this, SIGNAL(finished(bool)), _discoveryMainThread, SLOT(deleteLater()));
     qCInfo(lcEngine) << "Server" << account()->serverVersion()
-                     << QString("rootEtagChangesNotOnlySubFolderEtags=%1").arg(account()->rootEtagChangesNotOnlySubFolderEtags());
+                     << (account()->isHttp2Supported() ? "Using HTTP/2" : "");
     if (account()->rootEtagChangesNotOnlySubFolderEtags()) {
         connect(_discoveryMainThread, SIGNAL(etag(QString)), this, SLOT(slotRootEtagReceived(QString)));
     } else {