From: Daniel Molkentin Date: Wed, 25 Nov 2015 10:48:43 +0000 (+0100) Subject: Ensure patches apply cleanly, drop patch 14 X-Git-Tag: archive/raspbian/3.16.7-1_deb13u1+rpi1~1^2~1390^2~19 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=127c107094bedddaa89ae6cd780494294c50207f;p=nextcloud-desktop.git Ensure patches apply cleanly, drop patch 14 --- diff --git a/admin/qt/patches/0004-Cocoa-Fix-systray-SVG-icons.patch b/admin/qt/patches/0004-Cocoa-Fix-systray-SVG-icons.patch new file mode 100644 index 000000000..011b9a0f8 --- /dev/null +++ b/admin/qt/patches/0004-Cocoa-Fix-systray-SVG-icons.patch @@ -0,0 +1,32 @@ +From 22f3d359350fd65e4bbe2e9420fcc4460e8a590a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= +Date: Tue, 10 Mar 2015 22:37:39 +0100 +Subject: [PATCH] Cocoa: Fix systray SVG icons. + +Regression caused by f3699510. + +Task-number: QTBUG-44686 +Change-Id: I546422a67d4da29fac196025b09bddcb45c1b641 +Reviewed-by: Timur Pocheptsov +--- + src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm +index e449fd3..8a35705 100755 +--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm ++++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm +@@ -234,6 +234,10 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon) + } + } + ++ // Handle SVG icons, which do not return anything for availableSizes(). ++ if (!selectedSize.isValid()) ++ selectedSize = icon.actualSize(QSize(maxPixmapHeight, maxPixmapHeight), mode); ++ + QPixmap pixmap = icon.pixmap(selectedSize, mode); + + // Draw a low-resolution icon if there is not enough pixels for a retina +-- +1.9.1 + diff --git a/admin/qt/patches/0004-OSX-Fix-disapearing-tray-icon.patch b/admin/qt/patches/0004-OSX-Fix-disapearing-tray-icon.patch deleted file mode 100644 index df6b4eff3..000000000 --- a/admin/qt/patches/0004-OSX-Fix-disapearing-tray-icon.patch +++ /dev/null @@ -1,115 +0,0 @@ -From ee7fea33383726f0bb72e8082a357820e3ee3675 Mon Sep 17 00:00:00 2001 -From: Jocelyn Turcotte -Date: Tue, 24 Feb 2015 17:02:02 +0100 -Subject: [PATCH] OSX Fix disapearing tray icon -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -It would happen together with an error: - QPainter::begin: Paint device returned engine == 0 -and would be caused by the size provided to QIcon::pixmap being empty, -itself caused by the availableSizes list being empty for the Selected -mode. - -This bug was most often hidden by the fact that the Selected icon mode -was not triggered properly since we usually only set menuVisible after -calling updateIcon, and most of the time when it did, we would overwrite -it right after with a Normal mode icon. - -Fix the issue by disabling the broken feature completely since the -default Selected icon is grayed out while tray icons are now usually -black (or white when selected). To support the dark menu bar mode on -10.10 we'll need to use NSImage's setTemplate anyway and that -knowing in advance if we can invert the colors ourselves would also -better solve the menuVisible usecase. - -Task-number: QTBUG-42910 -Change-Id: If9ec9659af28ecceb841bfc2f11721e6029fe891 -Reviewed-by: Morten Johan Sørvig ---- - src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm | 17 +++-------------- - 1 file changed, 3 insertions(+), 14 deletions(-) - -diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm -index 8a35705..d366a3c 100755 ---- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm -+++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm -@@ -102,7 +102,6 @@ QT_USE_NAMESPACE - QCocoaSystemTrayIcon *systray; - NSStatusItem *item; - QCocoaMenu *menu; -- bool menuVisible; - QIcon icon; - QT_MANGLE_NAMESPACE(QNSImageView) *imageCell; - } -@@ -202,8 +201,6 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon) - - m_sys->item->icon = icon; - -- const bool menuVisible = m_sys->item->menu && m_sys->item->menuVisible; -- - // The reccomended maximum title bar icon height is 18 points - // (device independent pixels). The menu height on past and - // current OS X versions is 22 points. Provide some future-proofing -@@ -218,9 +215,8 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon) - // devicePixelRatio for the "best" screen on the system. - qreal devicePixelRatio = qApp->devicePixelRatio(); - const int maxPixmapHeight = maxImageHeight * devicePixelRatio; -- const QIcon::Mode mode = menuVisible ? QIcon::Selected : QIcon::Normal; - QSize selectedSize; -- Q_FOREACH (const QSize& size, sortByHeight(icon.availableSizes(mode))) { -+ Q_FOREACH (const QSize& size, sortByHeight(icon.availableSizes())) { - // Select a pixmap based on the height. We want the largest pixmap - // with a height smaller or equal to maxPixmapHeight. The pixmap - // may rectangular; assume it has a reasonable size. If there is -@@ -236,9 +232,9 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon) - - // Handle SVG icons, which do not return anything for availableSizes(). - if (!selectedSize.isValid()) -- selectedSize = icon.actualSize(QSize(maxPixmapHeight, maxPixmapHeight), mode); -+ selectedSize = icon.actualSize(QSize(maxPixmapHeight, maxPixmapHeight)); - -- QPixmap pixmap = icon.pixmap(selectedSize, mode); -+ QPixmap pixmap = icon.pixmap(selectedSize); - - // Draw a low-resolution icon if there is not enough pixels for a retina - // icon. This prevents showing a small icon on retina displays. -@@ -385,9 +381,6 @@ QT_END_NAMESPACE - Q_UNUSED(notification); - down = NO; - -- parent->systray->updateIcon(parent->icon); -- parent->menuVisible = false; -- - [self setNeedsDisplay:YES]; - } - -@@ -397,8 +390,6 @@ QT_END_NAMESPACE - int clickCount = [mouseEvent clickCount]; - [self setNeedsDisplay:YES]; - -- parent->systray->updateIcon(parent->icon); -- - if (clickCount == 2) { - [self menuTrackingDone:nil]; - [parent doubleClickSelector:self]; -@@ -454,7 +445,6 @@ QT_END_NAMESPACE - if (self) { - item = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain]; - menu = 0; -- menuVisible = false; - systray = sys; - imageCell = [[QNSImageView alloc] initWithParent:self]; - [item setView: imageCell]; -@@ -498,7 +488,6 @@ QT_END_NAMESPACE - selector:@selector(menuTrackingDone:) - name:NSMenuDidEndTrackingNotification - object:m]; -- menuVisible = true; - [item popUpStatusItemMenu: m]; - } - } --- -1.9.1 - diff --git a/admin/qt/patches/0005-Fix-force-debug-info-with-macx-clang_NOUPSTREAM.patch b/admin/qt/patches/0005-Fix-force-debug-info-with-macx-clang_NOUPSTREAM.patch deleted file mode 100644 index ea9c8b875..000000000 --- a/admin/qt/patches/0005-Fix-force-debug-info-with-macx-clang_NOUPSTREAM.patch +++ /dev/null @@ -1,30 +0,0 @@ -From f3cd07c11e0b7327ffc629f48a89c8c457cdba75 Mon Sep 17 00:00:00 2001 -From: Jocelyn Turcotte -Date: Fri, 6 Mar 2015 16:12:37 +0100 -Subject: [PATCH] Fix -force-debug-info with macx-clang - ---- - mkspecs/common/clang.conf | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/mkspecs/common/clang.conf b/mkspecs/common/clang.conf -index 2c29bb8..110d380 100644 ---- a/mkspecs/common/clang.conf -+++ b/mkspecs/common/clang.conf -@@ -20,11 +20,13 @@ QMAKE_CFLAGS_ISYSTEM = -isystem - QMAKE_CFLAGS_PRECOMPILE = -x c-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT} - QMAKE_CFLAGS_USE_PRECOMPILE = -Xclang -include-pch -Xclang ${QMAKE_PCH_OUTPUT} - QMAKE_CFLAGS_LTCG = -flto -+QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO = $$QMAKE_CFLAGS_OPTIMIZE -g - - QMAKE_CXXFLAGS_PRECOMPILE = -x c++-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT} - QMAKE_CXXFLAGS_USE_PRECOMPILE = $$QMAKE_CFLAGS_USE_PRECOMPILE - QMAKE_CXXFLAGS_LTCG = $$QMAKE_CFLAGS_LTCG - QMAKE_CXXFLAGS_CXX11 = -std=c++11 -+QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO = $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO - - QMAKE_LFLAGS_CXX11 = - QMAKE_LFLAGS_LTCG = $$QMAKE_CFLAGS_LTCG --- -2.2.0 - diff --git a/admin/qt/patches/0005-OSX-Fix-disapearing-tray-icon.patch b/admin/qt/patches/0005-OSX-Fix-disapearing-tray-icon.patch new file mode 100644 index 000000000..df6b4eff3 --- /dev/null +++ b/admin/qt/patches/0005-OSX-Fix-disapearing-tray-icon.patch @@ -0,0 +1,115 @@ +From ee7fea33383726f0bb72e8082a357820e3ee3675 Mon Sep 17 00:00:00 2001 +From: Jocelyn Turcotte +Date: Tue, 24 Feb 2015 17:02:02 +0100 +Subject: [PATCH] OSX Fix disapearing tray icon +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It would happen together with an error: + QPainter::begin: Paint device returned engine == 0 +and would be caused by the size provided to QIcon::pixmap being empty, +itself caused by the availableSizes list being empty for the Selected +mode. + +This bug was most often hidden by the fact that the Selected icon mode +was not triggered properly since we usually only set menuVisible after +calling updateIcon, and most of the time when it did, we would overwrite +it right after with a Normal mode icon. + +Fix the issue by disabling the broken feature completely since the +default Selected icon is grayed out while tray icons are now usually +black (or white when selected). To support the dark menu bar mode on +10.10 we'll need to use NSImage's setTemplate anyway and that +knowing in advance if we can invert the colors ourselves would also +better solve the menuVisible usecase. + +Task-number: QTBUG-42910 +Change-Id: If9ec9659af28ecceb841bfc2f11721e6029fe891 +Reviewed-by: Morten Johan Sørvig +--- + src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm | 17 +++-------------- + 1 file changed, 3 insertions(+), 14 deletions(-) + +diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm +index 8a35705..d366a3c 100755 +--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm ++++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm +@@ -102,7 +102,6 @@ QT_USE_NAMESPACE + QCocoaSystemTrayIcon *systray; + NSStatusItem *item; + QCocoaMenu *menu; +- bool menuVisible; + QIcon icon; + QT_MANGLE_NAMESPACE(QNSImageView) *imageCell; + } +@@ -202,8 +201,6 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon) + + m_sys->item->icon = icon; + +- const bool menuVisible = m_sys->item->menu && m_sys->item->menuVisible; +- + // The reccomended maximum title bar icon height is 18 points + // (device independent pixels). The menu height on past and + // current OS X versions is 22 points. Provide some future-proofing +@@ -218,9 +215,8 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon) + // devicePixelRatio for the "best" screen on the system. + qreal devicePixelRatio = qApp->devicePixelRatio(); + const int maxPixmapHeight = maxImageHeight * devicePixelRatio; +- const QIcon::Mode mode = menuVisible ? QIcon::Selected : QIcon::Normal; + QSize selectedSize; +- Q_FOREACH (const QSize& size, sortByHeight(icon.availableSizes(mode))) { ++ Q_FOREACH (const QSize& size, sortByHeight(icon.availableSizes())) { + // Select a pixmap based on the height. We want the largest pixmap + // with a height smaller or equal to maxPixmapHeight. The pixmap + // may rectangular; assume it has a reasonable size. If there is +@@ -236,9 +232,9 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon) + + // Handle SVG icons, which do not return anything for availableSizes(). + if (!selectedSize.isValid()) +- selectedSize = icon.actualSize(QSize(maxPixmapHeight, maxPixmapHeight), mode); ++ selectedSize = icon.actualSize(QSize(maxPixmapHeight, maxPixmapHeight)); + +- QPixmap pixmap = icon.pixmap(selectedSize, mode); ++ QPixmap pixmap = icon.pixmap(selectedSize); + + // Draw a low-resolution icon if there is not enough pixels for a retina + // icon. This prevents showing a small icon on retina displays. +@@ -385,9 +381,6 @@ QT_END_NAMESPACE + Q_UNUSED(notification); + down = NO; + +- parent->systray->updateIcon(parent->icon); +- parent->menuVisible = false; +- + [self setNeedsDisplay:YES]; + } + +@@ -397,8 +390,6 @@ QT_END_NAMESPACE + int clickCount = [mouseEvent clickCount]; + [self setNeedsDisplay:YES]; + +- parent->systray->updateIcon(parent->icon); +- + if (clickCount == 2) { + [self menuTrackingDone:nil]; + [parent doubleClickSelector:self]; +@@ -454,7 +445,6 @@ QT_END_NAMESPACE + if (self) { + item = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain]; + menu = 0; +- menuVisible = false; + systray = sys; + imageCell = [[QNSImageView alloc] initWithParent:self]; + [item setView: imageCell]; +@@ -498,7 +488,6 @@ QT_END_NAMESPACE + selector:@selector(menuTrackingDone:) + name:NSMenuDidEndTrackingNotification + object:m]; +- menuVisible = true; + [item popUpStatusItemMenu: m]; + } + } +-- +1.9.1 + diff --git a/admin/qt/patches/0006-Fix-force-debug-info-with-macx-clang_NOUPSTREAM.patch b/admin/qt/patches/0006-Fix-force-debug-info-with-macx-clang_NOUPSTREAM.patch new file mode 100644 index 000000000..ea9c8b875 --- /dev/null +++ b/admin/qt/patches/0006-Fix-force-debug-info-with-macx-clang_NOUPSTREAM.patch @@ -0,0 +1,30 @@ +From f3cd07c11e0b7327ffc629f48a89c8c457cdba75 Mon Sep 17 00:00:00 2001 +From: Jocelyn Turcotte +Date: Fri, 6 Mar 2015 16:12:37 +0100 +Subject: [PATCH] Fix -force-debug-info with macx-clang + +--- + mkspecs/common/clang.conf | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/mkspecs/common/clang.conf b/mkspecs/common/clang.conf +index 2c29bb8..110d380 100644 +--- a/mkspecs/common/clang.conf ++++ b/mkspecs/common/clang.conf +@@ -20,11 +20,13 @@ QMAKE_CFLAGS_ISYSTEM = -isystem + QMAKE_CFLAGS_PRECOMPILE = -x c-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT} + QMAKE_CFLAGS_USE_PRECOMPILE = -Xclang -include-pch -Xclang ${QMAKE_PCH_OUTPUT} + QMAKE_CFLAGS_LTCG = -flto ++QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO = $$QMAKE_CFLAGS_OPTIMIZE -g + + QMAKE_CXXFLAGS_PRECOMPILE = -x c++-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT} + QMAKE_CXXFLAGS_USE_PRECOMPILE = $$QMAKE_CFLAGS_USE_PRECOMPILE + QMAKE_CXXFLAGS_LTCG = $$QMAKE_CFLAGS_LTCG + QMAKE_CXXFLAGS_CXX11 = -std=c++11 ++QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO = $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO + + QMAKE_LFLAGS_CXX11 = + QMAKE_LFLAGS_LTCG = $$QMAKE_CFLAGS_LTCG +-- +2.2.0 + diff --git a/admin/qt/patches/0006-QNAM-Fix-upload-corruptions-when-server-closes-conne.patch b/admin/qt/patches/0006-QNAM-Fix-upload-corruptions-when-server-closes-conne.patch deleted file mode 100644 index edd46eb7e..000000000 --- a/admin/qt/patches/0006-QNAM-Fix-upload-corruptions-when-server-closes-conne.patch +++ /dev/null @@ -1,691 +0,0 @@ -From cff39fba10ffc10ee4dcfdc66ff6528eb26462d3 Mon Sep 17 00:00:00 2001 -From: Markus Goetz -Date: Fri, 10 Apr 2015 14:09:53 +0200 -Subject: [PATCH] QNAM: Fix upload corruptions when server closes connection - -This patch fixes several upload corruptions if the server closes the connection -while/before we send data into it. They happen inside multiple places in the HTTP -layer and are explained in the comments. -Corruptions are: -* The upload byte device has an in-flight signal with pending upload data, if -it gets reset (because server closes the connection) then the re-send of the -request was sometimes taking this stale in-flight pending upload data. -* Because some signals were DirectConnection and some were QueuedConnection, there -was a chance that a direct signal overtakes a queued signal. The state machine -then sent data down the socket which was buffered there (and sent later) although -it did not match the current state of the state machine when it was actually sent. -* A socket was seen as being able to have requests sent even though it was not -encrypted yet. This relates to the previous corruption where data is stored inside -the socket's buffer and then sent later. - -The included auto test produces all fixed corruptions, I detected no regressions -via the other tests. -This code also adds a bit of sanity checking to protect from possible further -problems. - -[ChangeLog][QtNetwork] Fix HTTP(s) upload corruption when server closes connection - -Change-Id: I54c883925ec897050941498f139c4b523030432e -Reviewed-by: Peter Hartmann ---- - src/corelib/io/qnoncontiguousbytedevice.cpp | 18 +++ - src/corelib/io/qnoncontiguousbytedevice_p.h | 4 + - .../access/qhttpnetworkconnectionchannel.cpp | 35 ++++- - .../access/qhttpnetworkconnectionchannel_p.h | 2 + - src/network/access/qhttpprotocolhandler.cpp | 7 + - src/network/access/qhttpthreaddelegate_p.h | 36 ++++- - src/network/access/qnetworkreplyhttpimpl.cpp | 25 ++- - src/network/access/qnetworkreplyhttpimpl_p.h | 7 +- - .../access/qnetworkreply/tst_qnetworkreply.cpp | 175 ++++++++++++++++++++- - 9 files changed, 279 insertions(+), 30 deletions(-) - -diff --git a/src/corelib/io/qnoncontiguousbytedevice.cpp b/src/corelib/io/qnoncontiguousbytedevice.cpp -index 11510a8..760ca3d 100644 ---- a/src/corelib/io/qnoncontiguousbytedevice.cpp -+++ b/src/corelib/io/qnoncontiguousbytedevice.cpp -@@ -236,6 +236,11 @@ qint64 QNonContiguousByteDeviceByteArrayImpl::size() - return byteArray->size(); - } - -+qint64 QNonContiguousByteDeviceByteArrayImpl::pos() -+{ -+ return currentPosition; -+} -+ - QNonContiguousByteDeviceRingBufferImpl::QNonContiguousByteDeviceRingBufferImpl(QSharedPointer rb) - : QNonContiguousByteDevice(), currentPosition(0) - { -@@ -273,6 +278,11 @@ bool QNonContiguousByteDeviceRingBufferImpl::atEnd() - return currentPosition >= size(); - } - -+qint64 QNonContiguousByteDeviceRingBufferImpl::pos() -+{ -+ return currentPosition; -+} -+ - bool QNonContiguousByteDeviceRingBufferImpl::reset() - { - if (resetDisabled) -@@ -406,6 +416,14 @@ qint64 QNonContiguousByteDeviceIoDeviceImpl::size() - return device->size() - initialPosition; - } - -+qint64 QNonContiguousByteDeviceIoDeviceImpl::pos() -+{ -+ if (device->isSequential()) -+ return -1; -+ -+ return device->pos(); -+} -+ - QByteDeviceWrappingIoDevice::QByteDeviceWrappingIoDevice(QNonContiguousByteDevice *bd) : QIODevice((QObject*)0) - { - byteDevice = bd; -diff --git a/src/corelib/io/qnoncontiguousbytedevice_p.h b/src/corelib/io/qnoncontiguousbytedevice_p.h -index c05ae11..4d7b7b0 100644 ---- a/src/corelib/io/qnoncontiguousbytedevice_p.h -+++ b/src/corelib/io/qnoncontiguousbytedevice_p.h -@@ -61,6 +61,7 @@ public: - virtual const char* readPointer(qint64 maximumLength, qint64 &len) = 0; - virtual bool advanceReadPointer(qint64 amount) = 0; - virtual bool atEnd() = 0; -+ virtual qint64 pos() { return -1; } - virtual bool reset() = 0; - void disableReset(); - bool isResetDisabled() { return resetDisabled; } -@@ -106,6 +107,7 @@ public: - bool atEnd(); - bool reset(); - qint64 size(); -+ qint64 pos() Q_DECL_OVERRIDE; - protected: - QByteArray* byteArray; - qint64 currentPosition; -@@ -121,6 +123,7 @@ public: - bool atEnd(); - bool reset(); - qint64 size(); -+ qint64 pos() Q_DECL_OVERRIDE; - protected: - QSharedPointer ringBuffer; - qint64 currentPosition; -@@ -138,6 +141,7 @@ public: - bool atEnd(); - bool reset(); - qint64 size(); -+ qint64 pos() Q_DECL_OVERRIDE; - protected: - QIODevice* device; - QByteArray* currentReadBuffer; -diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp -index 9f63280..49c6793 100644 ---- a/src/network/access/qhttpnetworkconnectionchannel.cpp -+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp -@@ -106,15 +106,19 @@ void QHttpNetworkConnectionChannel::init() - socket->setProxy(QNetworkProxy::NoProxy); - #endif - -+ // We want all signals (except the interactive ones) be connected as QueuedConnection -+ // because else we're falling into cases where we recurse back into the socket code -+ // and mess up the state. Always going to the event loop (and expecting that when reading/writing) -+ // is safer. - QObject::connect(socket, SIGNAL(bytesWritten(qint64)), - this, SLOT(_q_bytesWritten(qint64)), -- Qt::DirectConnection); -+ Qt::QueuedConnection); - QObject::connect(socket, SIGNAL(connected()), - this, SLOT(_q_connected()), -- Qt::DirectConnection); -+ Qt::QueuedConnection); - QObject::connect(socket, SIGNAL(readyRead()), - this, SLOT(_q_readyRead()), -- Qt::DirectConnection); -+ Qt::QueuedConnection); - - // The disconnected() and error() signals may already come - // while calling connectToHost(). -@@ -143,13 +147,13 @@ void QHttpNetworkConnectionChannel::init() - // won't be a sslSocket if encrypt is false - QObject::connect(sslSocket, SIGNAL(encrypted()), - this, SLOT(_q_encrypted()), -- Qt::DirectConnection); -+ Qt::QueuedConnection); - QObject::connect(sslSocket, SIGNAL(sslErrors(QList)), - this, SLOT(_q_sslErrors(QList)), - Qt::DirectConnection); - QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)), - this, SLOT(_q_encryptedBytesWritten(qint64)), -- Qt::DirectConnection); -+ Qt::QueuedConnection); - - if (ignoreAllSslErrors) - sslSocket->ignoreSslErrors(); -@@ -186,8 +190,11 @@ void QHttpNetworkConnectionChannel::close() - // pendingEncrypt must only be true in between connected and encrypted states - pendingEncrypt = false; - -- if (socket) -+ if (socket) { -+ // socket can be 0 since the host lookup is done from qhttpnetworkconnection.cpp while -+ // there is no socket yet. - socket->close(); -+ } - } - - -@@ -353,6 +360,14 @@ bool QHttpNetworkConnectionChannel::ensureConnection() - } - return false; - } -+ -+ // This code path for ConnectedState -+ if (pendingEncrypt) { -+ // Let's only be really connected when we have received the encrypted() signal. Else the state machine seems to mess up -+ // and corrupt the things sent to the server. -+ return false; -+ } -+ - return true; - } - -@@ -659,6 +674,12 @@ bool QHttpNetworkConnectionChannel::isSocketReading() const - void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes) - { - Q_UNUSED(bytes); -+ if (ssl) { -+ // In the SSL case we want to send data from encryptedBytesWritten signal since that one -+ // is the one going down to the actual network, not only into some SSL buffer. -+ return; -+ } -+ - // bytes have been written to the socket. write even more of them :) - if (isSocketWriting()) - sendRequest(); -@@ -734,7 +755,7 @@ void QHttpNetworkConnectionChannel::_q_connected() - - // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again! - //channels[i].reconnectAttempts = 2; -- if (pendingEncrypt) { -+ if (ssl || pendingEncrypt) { // FIXME: Didn't work properly with pendingEncrypt only, we should refactor this into an EncrypingState - #ifndef QT_NO_SSL - if (connection->sslContext().isNull()) { - // this socket is making the 1st handshake for this connection, -diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h -index 692c0e6..231fe11 100644 ---- a/src/network/access/qhttpnetworkconnectionchannel_p.h -+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h -@@ -83,6 +83,8 @@ typedef QPair HttpMessagePair; - class QHttpNetworkConnectionChannel : public QObject { - Q_OBJECT - public: -+ // TODO: Refactor this to add an EncryptingState (and remove pendingEncrypt). -+ // Also add an Unconnected state so IdleState does not have double meaning. - enum ChannelState { - IdleState = 0, // ready to send request - ConnectingState = 1, // connecting to host -diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp -index 28e10f7..3357948 100644 ---- a/src/network/access/qhttpprotocolhandler.cpp -+++ b/src/network/access/qhttpprotocolhandler.cpp -@@ -368,6 +368,13 @@ bool QHttpProtocolHandler::sendRequest() - // nothing to read currently, break the loop - break; - } else { -+ if (m_channel->written != uploadByteDevice->pos()) { -+ // Sanity check. This was useful in tracking down an upload corruption. -+ qWarning() << "QHttpProtocolHandler: Internal error in sendRequest. Expected to write at position" << m_channel->written << "but read device is at" << uploadByteDevice->pos(); -+ Q_ASSERT(m_channel->written == uploadByteDevice->pos()); -+ m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::ProtocolFailure); -+ return false; -+ } - qint64 currentWriteSize = m_socket->write(readPointer, currentReadSize); - if (currentWriteSize == -1 || currentWriteSize != currentReadSize) { - // socket broke down -diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h -index 1661082..b553409 100644 ---- a/src/network/access/qhttpthreaddelegate_p.h -+++ b/src/network/access/qhttpthreaddelegate_p.h -@@ -187,6 +187,7 @@ protected: - QByteArray m_dataArray; - bool m_atEnd; - qint64 m_size; -+ qint64 m_pos; // to match calls of haveDataSlot with the expected position - public: - QNonContiguousByteDeviceThreadForwardImpl(bool aE, qint64 s) - : QNonContiguousByteDevice(), -@@ -194,7 +195,8 @@ public: - m_amount(0), - m_data(0), - m_atEnd(aE), -- m_size(s) -+ m_size(s), -+ m_pos(0) - { - } - -@@ -202,6 +204,11 @@ public: - { - } - -+ qint64 pos() Q_DECL_OVERRIDE -+ { -+ return m_pos; -+ } -+ - const char* readPointer(qint64 maximumLength, qint64 &len) - { - if (m_amount > 0) { -@@ -229,11 +236,10 @@ public: - - m_amount -= a; - m_data += a; -+ m_pos += a; - -- // To main thread to inform about our state -- emit processedData(a); -- -- // FIXME possible optimization, already ask user thread for some data -+ // To main thread to inform about our state. The m_pos will be sent as a sanity check. -+ emit processedData(m_pos, a); - - return true; - } -@@ -250,10 +256,21 @@ public: - { - m_amount = 0; - m_data = 0; -+ m_dataArray.clear(); -+ -+ if (wantDataPending) { -+ // had requested the user thread to send some data (only 1 in-flight at any moment) -+ wantDataPending = false; -+ } - - // Communicate as BlockingQueuedConnection - bool b = false; - emit resetData(&b); -+ if (b) { -+ // the reset succeeded, we're at pos 0 again -+ m_pos = 0; -+ // the HTTP code will anyway abort the request if !b. -+ } - return b; - } - -@@ -264,8 +281,13 @@ public: - - public slots: - // From user thread: -- void haveDataSlot(QByteArray dataArray, bool dataAtEnd, qint64 dataSize) -+ void haveDataSlot(qint64 pos, QByteArray dataArray, bool dataAtEnd, qint64 dataSize) - { -+ if (pos != m_pos) { -+ // Sometimes when re-sending a request in the qhttpnetwork* layer there is a pending haveData from the -+ // user thread on the way to us. We need to ignore it since it is the data for the wrong(later) chunk. -+ return; -+ } - wantDataPending = false; - - m_dataArray = dataArray; -@@ -285,7 +307,7 @@ signals: - - // to main thread: - void wantData(qint64); -- void processedData(qint64); -+ void processedData(qint64 pos, qint64 amount); - void resetData(bool *b); - }; - -diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp -index 4ce7303..974a101 100644 ---- a/src/network/access/qnetworkreplyhttpimpl.cpp -+++ b/src/network/access/qnetworkreplyhttpimpl.cpp -@@ -424,6 +424,7 @@ QNetworkReplyHttpImplPrivate::QNetworkReplyHttpImplPrivate() - , synchronous(false) - , state(Idle) - , statusCode(0) -+ , uploadByteDevicePosition(false) - , uploadDeviceChoking(false) - , outgoingData(0) - , bytesUploaded(-1) -@@ -863,9 +864,9 @@ void QNetworkReplyHttpImplPrivate::postRequest() - q, SLOT(uploadByteDeviceReadyReadSlot()), - Qt::QueuedConnection); - -- // From main thread to user thread: -- QObject::connect(q, SIGNAL(haveUploadData(QByteArray,bool,qint64)), -- forwardUploadDevice, SLOT(haveDataSlot(QByteArray,bool,qint64)), Qt::QueuedConnection); -+ // From user thread to http thread: -+ QObject::connect(q, SIGNAL(haveUploadData(qint64,QByteArray,bool,qint64)), -+ forwardUploadDevice, SLOT(haveDataSlot(qint64,QByteArray,bool,qint64)), Qt::QueuedConnection); - QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()), - forwardUploadDevice, SIGNAL(readyRead()), - Qt::QueuedConnection); -@@ -873,8 +874,8 @@ void QNetworkReplyHttpImplPrivate::postRequest() - // From http thread to user thread: - QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)), - q, SLOT(wantUploadDataSlot(qint64))); -- QObject::connect(forwardUploadDevice, SIGNAL(processedData(qint64)), -- q, SLOT(sentUploadDataSlot(qint64))); -+ QObject::connect(forwardUploadDevice,SIGNAL(processedData(qint64, qint64)), -+ q, SLOT(sentUploadDataSlot(qint64,qint64))); - QObject::connect(forwardUploadDevice, SIGNAL(resetData(bool*)), - q, SLOT(resetUploadDataSlot(bool*)), - Qt::BlockingQueuedConnection); // this is the only one with BlockingQueued! -@@ -1268,12 +1269,22 @@ void QNetworkReplyHttpImplPrivate::replySslConfigurationChanged(const QSslConfig - void QNetworkReplyHttpImplPrivate::resetUploadDataSlot(bool *r) - { - *r = uploadByteDevice->reset(); -+ if (*r) { -+ // reset our own position which is used for the inter-thread communication -+ uploadByteDevicePosition = 0; -+ } - } - - // Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread --void QNetworkReplyHttpImplPrivate::sentUploadDataSlot(qint64 amount) -+void QNetworkReplyHttpImplPrivate::sentUploadDataSlot(qint64 pos, qint64 amount) - { -+ if (uploadByteDevicePosition + amount != pos) { -+ // Sanity check, should not happen. -+ error(QNetworkReply::UnknownNetworkError, ""); -+ return; -+ } - uploadByteDevice->advanceReadPointer(amount); -+ uploadByteDevicePosition += amount; - } - - // Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread -@@ -1298,7 +1309,7 @@ void QNetworkReplyHttpImplPrivate::wantUploadDataSlot(qint64 maxSize) - QByteArray dataArray(data, currentUploadDataLength); - - // Communicate back to HTTP thread -- emit q->haveUploadData(dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size()); -+ emit q->haveUploadData(uploadByteDevicePosition, dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size()); - } - - void QNetworkReplyHttpImplPrivate::uploadByteDeviceReadyReadSlot() -diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h -index 77d9c5a..1940922 100644 ---- a/src/network/access/qnetworkreplyhttpimpl_p.h -+++ b/src/network/access/qnetworkreplyhttpimpl_p.h -@@ -120,7 +120,7 @@ public: - - Q_PRIVATE_SLOT(d_func(), void resetUploadDataSlot(bool *r)) - Q_PRIVATE_SLOT(d_func(), void wantUploadDataSlot(qint64)) -- Q_PRIVATE_SLOT(d_func(), void sentUploadDataSlot(qint64)) -+ Q_PRIVATE_SLOT(d_func(), void sentUploadDataSlot(qint64,qint64)) - Q_PRIVATE_SLOT(d_func(), void uploadByteDeviceReadyReadSlot()) - Q_PRIVATE_SLOT(d_func(), void emitReplyUploadProgress(qint64, qint64)) - Q_PRIVATE_SLOT(d_func(), void _q_cacheSaveDeviceAboutToClose()) -@@ -144,7 +144,7 @@ signals: - - void startHttpRequestSynchronously(); - -- void haveUploadData(QByteArray dataArray, bool dataAtEnd, qint64 dataSize); -+ void haveUploadData(const qint64 pos, QByteArray dataArray, bool dataAtEnd, qint64 dataSize); - }; - - class QNetworkReplyHttpImplPrivate: public QNetworkReplyPrivate -@@ -195,6 +195,7 @@ public: - // upload - QNonContiguousByteDevice* createUploadByteDevice(); - QSharedPointer uploadByteDevice; -+ qint64 uploadByteDevicePosition; - bool uploadDeviceChoking; // if we couldn't readPointer() any data at the moment - QIODevice *outgoingData; - QSharedPointer outgoingDataBuffer; -@@ -283,7 +284,7 @@ public: - // From QNonContiguousByteDeviceThreadForwardImpl in HTTP thread: - void resetUploadDataSlot(bool *r); - void wantUploadDataSlot(qint64); -- void sentUploadDataSlot(qint64); -+ void sentUploadDataSlot(qint64, qint64); - - // From user's QNonContiguousByteDevice - void uploadByteDeviceReadyReadSlot(); -diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp -index 3ccedf6..d2edf67 100644 ---- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp -+++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp -@@ -457,6 +457,10 @@ private Q_SLOTS: - - void putWithRateLimiting(); - -+#ifndef QT_NO_SSL -+ void putWithServerClosingConnectionImmediately(); -+#endif -+ - // NOTE: This test must be last! - void parentingRepliesToTheApp(); - private: -@@ -4718,18 +4722,22 @@ void tst_QNetworkReply::ioPostToHttpNoBufferFlag() - class SslServer : public QTcpServer { - Q_OBJECT - public: -- SslServer() : socket(0) {}; -+ SslServer() : socket(0), m_ssl(true) {} - void incomingConnection(qintptr socketDescriptor) { - QSslSocket *serverSocket = new QSslSocket; - serverSocket->setParent(this); - - if (serverSocket->setSocketDescriptor(socketDescriptor)) { -+ connect(serverSocket, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); -+ if (!m_ssl) { -+ emit newPlainConnection(serverSocket); -+ return; -+ } - QString testDataDir = QFileInfo(QFINDTESTDATA("rfc3252.txt")).absolutePath(); - if (testDataDir.isEmpty()) - testDataDir = QCoreApplication::applicationDirPath(); - - connect(serverSocket, SIGNAL(encrypted()), this, SLOT(encryptedSlot())); -- connect(serverSocket, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); - serverSocket->setProtocol(QSsl::AnyProtocol); - connect(serverSocket, SIGNAL(sslErrors(QList)), serverSocket, SLOT(ignoreSslErrors())); - serverSocket->setLocalCertificate(testDataDir + "/certs/server.pem"); -@@ -4740,11 +4748,12 @@ public: - } - } - signals: -- void newEncryptedConnection(); -+ void newEncryptedConnection(QSslSocket *s); -+ void newPlainConnection(QSslSocket *s); - public slots: - void encryptedSlot() { - socket = (QSslSocket*) sender(); -- emit newEncryptedConnection(); -+ emit newEncryptedConnection(socket); - } - void readyReadSlot() { - // for the incoming sockets, not the server socket -@@ -4753,6 +4762,7 @@ public slots: - - public: - QSslSocket *socket; -+ bool m_ssl; - }; - - // very similar to ioPostToHttpUploadProgress but for SSL -@@ -4780,7 +4790,7 @@ void tst_QNetworkReply::ioPostToHttpsUploadProgress() - QNetworkReplyPtr reply(manager.post(request, sourceFile)); - - QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64))); -- connect(&server, SIGNAL(newEncryptedConnection()), &QTestEventLoop::instance(), SLOT(exitLoop())); -+ connect(&server, SIGNAL(newEncryptedConnection(QSslSocket*)), &QTestEventLoop::instance(), SLOT(exitLoop())); - connect(reply, SIGNAL(sslErrors(QList)), reply.data(), SLOT(ignoreSslErrors())); - - // get the request started and the incoming socket connected -@@ -4788,7 +4798,7 @@ void tst_QNetworkReply::ioPostToHttpsUploadProgress() - QVERIFY(!QTestEventLoop::instance().timeout()); - QTcpSocket *incomingSocket = server.socket; - QVERIFY(incomingSocket); -- disconnect(&server, SIGNAL(newEncryptedConnection()), &QTestEventLoop::instance(), SLOT(exitLoop())); -+ disconnect(&server, SIGNAL(newEncryptedConnection(QSslSocket*)), &QTestEventLoop::instance(), SLOT(exitLoop())); - - - incomingSocket->setReadBufferSize(1*1024); -@@ -7905,6 +7915,159 @@ void tst_QNetworkReply::putWithRateLimiting() - } - - -+#ifndef QT_NO_SSL -+ -+class PutWithServerClosingConnectionImmediatelyHandler: public QObject -+{ -+ Q_OBJECT -+public: -+ bool m_parsedHeaders; -+ QByteArray m_receivedData; -+ QByteArray m_expectedData; -+ QSslSocket *m_socket; -+ PutWithServerClosingConnectionImmediatelyHandler(QSslSocket *s, QByteArray expected) :m_parsedHeaders(false), m_expectedData(expected), m_socket(s) -+ { -+ m_socket->setParent(this); -+ connect(m_socket, SIGNAL(readyRead()), SLOT(readyReadSlot())); -+ connect(m_socket, SIGNAL(disconnected()), SLOT(disconnectedSlot())); -+ } -+signals: -+ void correctFileUploadReceived(); -+ void corruptFileUploadReceived(); -+ -+public slots: -+ void closeDelayed() { -+ m_socket->close(); -+ } -+ -+ void readyReadSlot() -+ { -+ QByteArray data = m_socket->readAll(); -+ m_receivedData += data; -+ if (!m_parsedHeaders && m_receivedData.contains("\r\n\r\n")) { -+ m_parsedHeaders = true; -+ QTimer::singleShot(qrand()%10, this, SLOT(closeDelayed())); // simulate random network latency -+ // This server simulates a web server connection closing, e.g. because of Apaches MaxKeepAliveRequests or KeepAliveTimeout -+ // In this case QNAM needs to re-send the upload data but it had a bug which then corrupts the upload -+ // This test catches that. -+ } -+ -+ } -+ void disconnectedSlot() -+ { -+ if (m_parsedHeaders) { -+ //qDebug() << m_receivedData.left(m_receivedData.indexOf("\r\n\r\n")); -+ m_receivedData = m_receivedData.mid(m_receivedData.indexOf("\r\n\r\n")+4); // check only actual data -+ } -+ if (m_receivedData.length() > 0 && !m_expectedData.startsWith(m_receivedData)) { -+ // We had received some data but it is corrupt! -+ qDebug() << "CORRUPT" << m_receivedData.count(); -+ -+ // Use this to track down the pattern of the corruption and conclude the source -+// QFile a("/tmp/corrupt"); -+// a.open(QIODevice::WriteOnly); -+// a.write(m_receivedData); -+// a.close(); -+ -+// QFile b("/tmp/correct"); -+// b.open(QIODevice::WriteOnly); -+// b.write(m_expectedData); -+// b.close(); -+ //exit(1); -+ emit corruptFileUploadReceived(); -+ } else { -+ emit correctFileUploadReceived(); -+ } -+ } -+}; -+ -+class PutWithServerClosingConnectionImmediatelyServer: public SslServer -+{ -+ Q_OBJECT -+public: -+ int m_correctUploads; -+ int m_corruptUploads; -+ int m_repliesFinished; -+ int m_expectedReplies; -+ QByteArray m_expectedData; -+ PutWithServerClosingConnectionImmediatelyServer() : SslServer(), m_correctUploads(0), m_corruptUploads(0), m_repliesFinished(0), m_expectedReplies(0) -+ { -+ QObject::connect(this, SIGNAL(newEncryptedConnection(QSslSocket*)), this, SLOT(createHandlerForConnection(QSslSocket*))); -+ QObject::connect(this, SIGNAL(newPlainConnection(QSslSocket*)), this, SLOT(createHandlerForConnection(QSslSocket*))); -+ } -+ -+public slots: -+ void createHandlerForConnection(QSslSocket* s) { -+ PutWithServerClosingConnectionImmediatelyHandler *handler = new PutWithServerClosingConnectionImmediatelyHandler(s, m_expectedData); -+ handler->setParent(this); -+ QObject::connect(handler, SIGNAL(correctFileUploadReceived()), this, SLOT(increaseCorrect())); -+ QObject::connect(handler, SIGNAL(corruptFileUploadReceived()), this, SLOT(increaseCorrupt())); -+ } -+ void increaseCorrect() { -+ m_correctUploads++; -+ } -+ void increaseCorrupt() { -+ m_corruptUploads++; -+ } -+ void replyFinished() { -+ m_repliesFinished++; -+ if (m_repliesFinished == m_expectedReplies) { -+ QTestEventLoop::instance().exitLoop(); -+ } -+ } -+}; -+ -+ -+ -+void tst_QNetworkReply::putWithServerClosingConnectionImmediately() -+{ -+ const int numUploads = 40; -+ qint64 wantedSize = 512*1024; // 512 kB -+ QByteArray sourceFile; -+ for (int i = 0; i < wantedSize; ++i) { -+ sourceFile += (char)'a' +(i%26); -+ } -+ bool withSsl = false; -+ -+ for (int s = 0; s <= 1; s++) { -+ withSsl = (s == 1); -+ // Test also needs to run several times because of 9c2ecf89 -+ for (int j = 0; j < 20; j++) { -+ // emulate a minimal https server -+ PutWithServerClosingConnectionImmediatelyServer server; -+ server.m_ssl = withSsl; -+ server.m_expectedData = sourceFile; -+ server.m_expectedReplies = numUploads; -+ server.listen(QHostAddress(QHostAddress::LocalHost), 0); -+ -+ for (int i = 0; i < numUploads; i++) { -+ // create the request -+ QUrl url = QUrl(QString("http%1://127.0.0.1:%2/file=%3").arg(withSsl ? "s" : "").arg(server.serverPort()).arg(i)); -+ QNetworkRequest request(url); -+ QNetworkReply *reply = manager.put(request, sourceFile); -+ connect(reply, SIGNAL(sslErrors(QList)), reply, SLOT(ignoreSslErrors())); -+ connect(reply, SIGNAL(finished()), &server, SLOT(replyFinished())); -+ reply->setParent(&server); -+ } -+ -+ // get the request started and the incoming socket connected -+ QTestEventLoop::instance().enterLoop(10); -+ -+ //qDebug() << "correct=" << server.m_correctUploads << "corrupt=" << server.m_corruptUploads << "expected=" < 5); -+ // Because actually important is that we don't get any corruption: -+ QCOMPARE(server.m_corruptUploads, 0); -+ -+ server.close(); -+ } -+ } -+ -+ -+} -+ -+#endif - - // NOTE: This test must be last testcase in tst_qnetworkreply! - void tst_QNetworkReply::parentingRepliesToTheApp() --- -1.9.1 - diff --git a/admin/qt/patches/0007-QNAM-Fix-upload-corruptions-when-server-closes-conne.patch b/admin/qt/patches/0007-QNAM-Fix-upload-corruptions-when-server-closes-conne.patch new file mode 100644 index 000000000..edd46eb7e --- /dev/null +++ b/admin/qt/patches/0007-QNAM-Fix-upload-corruptions-when-server-closes-conne.patch @@ -0,0 +1,691 @@ +From cff39fba10ffc10ee4dcfdc66ff6528eb26462d3 Mon Sep 17 00:00:00 2001 +From: Markus Goetz +Date: Fri, 10 Apr 2015 14:09:53 +0200 +Subject: [PATCH] QNAM: Fix upload corruptions when server closes connection + +This patch fixes several upload corruptions if the server closes the connection +while/before we send data into it. They happen inside multiple places in the HTTP +layer and are explained in the comments. +Corruptions are: +* The upload byte device has an in-flight signal with pending upload data, if +it gets reset (because server closes the connection) then the re-send of the +request was sometimes taking this stale in-flight pending upload data. +* Because some signals were DirectConnection and some were QueuedConnection, there +was a chance that a direct signal overtakes a queued signal. The state machine +then sent data down the socket which was buffered there (and sent later) although +it did not match the current state of the state machine when it was actually sent. +* A socket was seen as being able to have requests sent even though it was not +encrypted yet. This relates to the previous corruption where data is stored inside +the socket's buffer and then sent later. + +The included auto test produces all fixed corruptions, I detected no regressions +via the other tests. +This code also adds a bit of sanity checking to protect from possible further +problems. + +[ChangeLog][QtNetwork] Fix HTTP(s) upload corruption when server closes connection + +Change-Id: I54c883925ec897050941498f139c4b523030432e +Reviewed-by: Peter Hartmann +--- + src/corelib/io/qnoncontiguousbytedevice.cpp | 18 +++ + src/corelib/io/qnoncontiguousbytedevice_p.h | 4 + + .../access/qhttpnetworkconnectionchannel.cpp | 35 ++++- + .../access/qhttpnetworkconnectionchannel_p.h | 2 + + src/network/access/qhttpprotocolhandler.cpp | 7 + + src/network/access/qhttpthreaddelegate_p.h | 36 ++++- + src/network/access/qnetworkreplyhttpimpl.cpp | 25 ++- + src/network/access/qnetworkreplyhttpimpl_p.h | 7 +- + .../access/qnetworkreply/tst_qnetworkreply.cpp | 175 ++++++++++++++++++++- + 9 files changed, 279 insertions(+), 30 deletions(-) + +diff --git a/src/corelib/io/qnoncontiguousbytedevice.cpp b/src/corelib/io/qnoncontiguousbytedevice.cpp +index 11510a8..760ca3d 100644 +--- a/src/corelib/io/qnoncontiguousbytedevice.cpp ++++ b/src/corelib/io/qnoncontiguousbytedevice.cpp +@@ -236,6 +236,11 @@ qint64 QNonContiguousByteDeviceByteArrayImpl::size() + return byteArray->size(); + } + ++qint64 QNonContiguousByteDeviceByteArrayImpl::pos() ++{ ++ return currentPosition; ++} ++ + QNonContiguousByteDeviceRingBufferImpl::QNonContiguousByteDeviceRingBufferImpl(QSharedPointer rb) + : QNonContiguousByteDevice(), currentPosition(0) + { +@@ -273,6 +278,11 @@ bool QNonContiguousByteDeviceRingBufferImpl::atEnd() + return currentPosition >= size(); + } + ++qint64 QNonContiguousByteDeviceRingBufferImpl::pos() ++{ ++ return currentPosition; ++} ++ + bool QNonContiguousByteDeviceRingBufferImpl::reset() + { + if (resetDisabled) +@@ -406,6 +416,14 @@ qint64 QNonContiguousByteDeviceIoDeviceImpl::size() + return device->size() - initialPosition; + } + ++qint64 QNonContiguousByteDeviceIoDeviceImpl::pos() ++{ ++ if (device->isSequential()) ++ return -1; ++ ++ return device->pos(); ++} ++ + QByteDeviceWrappingIoDevice::QByteDeviceWrappingIoDevice(QNonContiguousByteDevice *bd) : QIODevice((QObject*)0) + { + byteDevice = bd; +diff --git a/src/corelib/io/qnoncontiguousbytedevice_p.h b/src/corelib/io/qnoncontiguousbytedevice_p.h +index c05ae11..4d7b7b0 100644 +--- a/src/corelib/io/qnoncontiguousbytedevice_p.h ++++ b/src/corelib/io/qnoncontiguousbytedevice_p.h +@@ -61,6 +61,7 @@ public: + virtual const char* readPointer(qint64 maximumLength, qint64 &len) = 0; + virtual bool advanceReadPointer(qint64 amount) = 0; + virtual bool atEnd() = 0; ++ virtual qint64 pos() { return -1; } + virtual bool reset() = 0; + void disableReset(); + bool isResetDisabled() { return resetDisabled; } +@@ -106,6 +107,7 @@ public: + bool atEnd(); + bool reset(); + qint64 size(); ++ qint64 pos() Q_DECL_OVERRIDE; + protected: + QByteArray* byteArray; + qint64 currentPosition; +@@ -121,6 +123,7 @@ public: + bool atEnd(); + bool reset(); + qint64 size(); ++ qint64 pos() Q_DECL_OVERRIDE; + protected: + QSharedPointer ringBuffer; + qint64 currentPosition; +@@ -138,6 +141,7 @@ public: + bool atEnd(); + bool reset(); + qint64 size(); ++ qint64 pos() Q_DECL_OVERRIDE; + protected: + QIODevice* device; + QByteArray* currentReadBuffer; +diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp +index 9f63280..49c6793 100644 +--- a/src/network/access/qhttpnetworkconnectionchannel.cpp ++++ b/src/network/access/qhttpnetworkconnectionchannel.cpp +@@ -106,15 +106,19 @@ void QHttpNetworkConnectionChannel::init() + socket->setProxy(QNetworkProxy::NoProxy); + #endif + ++ // We want all signals (except the interactive ones) be connected as QueuedConnection ++ // because else we're falling into cases where we recurse back into the socket code ++ // and mess up the state. Always going to the event loop (and expecting that when reading/writing) ++ // is safer. + QObject::connect(socket, SIGNAL(bytesWritten(qint64)), + this, SLOT(_q_bytesWritten(qint64)), +- Qt::DirectConnection); ++ Qt::QueuedConnection); + QObject::connect(socket, SIGNAL(connected()), + this, SLOT(_q_connected()), +- Qt::DirectConnection); ++ Qt::QueuedConnection); + QObject::connect(socket, SIGNAL(readyRead()), + this, SLOT(_q_readyRead()), +- Qt::DirectConnection); ++ Qt::QueuedConnection); + + // The disconnected() and error() signals may already come + // while calling connectToHost(). +@@ -143,13 +147,13 @@ void QHttpNetworkConnectionChannel::init() + // won't be a sslSocket if encrypt is false + QObject::connect(sslSocket, SIGNAL(encrypted()), + this, SLOT(_q_encrypted()), +- Qt::DirectConnection); ++ Qt::QueuedConnection); + QObject::connect(sslSocket, SIGNAL(sslErrors(QList)), + this, SLOT(_q_sslErrors(QList)), + Qt::DirectConnection); + QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)), + this, SLOT(_q_encryptedBytesWritten(qint64)), +- Qt::DirectConnection); ++ Qt::QueuedConnection); + + if (ignoreAllSslErrors) + sslSocket->ignoreSslErrors(); +@@ -186,8 +190,11 @@ void QHttpNetworkConnectionChannel::close() + // pendingEncrypt must only be true in between connected and encrypted states + pendingEncrypt = false; + +- if (socket) ++ if (socket) { ++ // socket can be 0 since the host lookup is done from qhttpnetworkconnection.cpp while ++ // there is no socket yet. + socket->close(); ++ } + } + + +@@ -353,6 +360,14 @@ bool QHttpNetworkConnectionChannel::ensureConnection() + } + return false; + } ++ ++ // This code path for ConnectedState ++ if (pendingEncrypt) { ++ // Let's only be really connected when we have received the encrypted() signal. Else the state machine seems to mess up ++ // and corrupt the things sent to the server. ++ return false; ++ } ++ + return true; + } + +@@ -659,6 +674,12 @@ bool QHttpNetworkConnectionChannel::isSocketReading() const + void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes) + { + Q_UNUSED(bytes); ++ if (ssl) { ++ // In the SSL case we want to send data from encryptedBytesWritten signal since that one ++ // is the one going down to the actual network, not only into some SSL buffer. ++ return; ++ } ++ + // bytes have been written to the socket. write even more of them :) + if (isSocketWriting()) + sendRequest(); +@@ -734,7 +755,7 @@ void QHttpNetworkConnectionChannel::_q_connected() + + // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again! + //channels[i].reconnectAttempts = 2; +- if (pendingEncrypt) { ++ if (ssl || pendingEncrypt) { // FIXME: Didn't work properly with pendingEncrypt only, we should refactor this into an EncrypingState + #ifndef QT_NO_SSL + if (connection->sslContext().isNull()) { + // this socket is making the 1st handshake for this connection, +diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h +index 692c0e6..231fe11 100644 +--- a/src/network/access/qhttpnetworkconnectionchannel_p.h ++++ b/src/network/access/qhttpnetworkconnectionchannel_p.h +@@ -83,6 +83,8 @@ typedef QPair HttpMessagePair; + class QHttpNetworkConnectionChannel : public QObject { + Q_OBJECT + public: ++ // TODO: Refactor this to add an EncryptingState (and remove pendingEncrypt). ++ // Also add an Unconnected state so IdleState does not have double meaning. + enum ChannelState { + IdleState = 0, // ready to send request + ConnectingState = 1, // connecting to host +diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp +index 28e10f7..3357948 100644 +--- a/src/network/access/qhttpprotocolhandler.cpp ++++ b/src/network/access/qhttpprotocolhandler.cpp +@@ -368,6 +368,13 @@ bool QHttpProtocolHandler::sendRequest() + // nothing to read currently, break the loop + break; + } else { ++ if (m_channel->written != uploadByteDevice->pos()) { ++ // Sanity check. This was useful in tracking down an upload corruption. ++ qWarning() << "QHttpProtocolHandler: Internal error in sendRequest. Expected to write at position" << m_channel->written << "but read device is at" << uploadByteDevice->pos(); ++ Q_ASSERT(m_channel->written == uploadByteDevice->pos()); ++ m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::ProtocolFailure); ++ return false; ++ } + qint64 currentWriteSize = m_socket->write(readPointer, currentReadSize); + if (currentWriteSize == -1 || currentWriteSize != currentReadSize) { + // socket broke down +diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h +index 1661082..b553409 100644 +--- a/src/network/access/qhttpthreaddelegate_p.h ++++ b/src/network/access/qhttpthreaddelegate_p.h +@@ -187,6 +187,7 @@ protected: + QByteArray m_dataArray; + bool m_atEnd; + qint64 m_size; ++ qint64 m_pos; // to match calls of haveDataSlot with the expected position + public: + QNonContiguousByteDeviceThreadForwardImpl(bool aE, qint64 s) + : QNonContiguousByteDevice(), +@@ -194,7 +195,8 @@ public: + m_amount(0), + m_data(0), + m_atEnd(aE), +- m_size(s) ++ m_size(s), ++ m_pos(0) + { + } + +@@ -202,6 +204,11 @@ public: + { + } + ++ qint64 pos() Q_DECL_OVERRIDE ++ { ++ return m_pos; ++ } ++ + const char* readPointer(qint64 maximumLength, qint64 &len) + { + if (m_amount > 0) { +@@ -229,11 +236,10 @@ public: + + m_amount -= a; + m_data += a; ++ m_pos += a; + +- // To main thread to inform about our state +- emit processedData(a); +- +- // FIXME possible optimization, already ask user thread for some data ++ // To main thread to inform about our state. The m_pos will be sent as a sanity check. ++ emit processedData(m_pos, a); + + return true; + } +@@ -250,10 +256,21 @@ public: + { + m_amount = 0; + m_data = 0; ++ m_dataArray.clear(); ++ ++ if (wantDataPending) { ++ // had requested the user thread to send some data (only 1 in-flight at any moment) ++ wantDataPending = false; ++ } + + // Communicate as BlockingQueuedConnection + bool b = false; + emit resetData(&b); ++ if (b) { ++ // the reset succeeded, we're at pos 0 again ++ m_pos = 0; ++ // the HTTP code will anyway abort the request if !b. ++ } + return b; + } + +@@ -264,8 +281,13 @@ public: + + public slots: + // From user thread: +- void haveDataSlot(QByteArray dataArray, bool dataAtEnd, qint64 dataSize) ++ void haveDataSlot(qint64 pos, QByteArray dataArray, bool dataAtEnd, qint64 dataSize) + { ++ if (pos != m_pos) { ++ // Sometimes when re-sending a request in the qhttpnetwork* layer there is a pending haveData from the ++ // user thread on the way to us. We need to ignore it since it is the data for the wrong(later) chunk. ++ return; ++ } + wantDataPending = false; + + m_dataArray = dataArray; +@@ -285,7 +307,7 @@ signals: + + // to main thread: + void wantData(qint64); +- void processedData(qint64); ++ void processedData(qint64 pos, qint64 amount); + void resetData(bool *b); + }; + +diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp +index 4ce7303..974a101 100644 +--- a/src/network/access/qnetworkreplyhttpimpl.cpp ++++ b/src/network/access/qnetworkreplyhttpimpl.cpp +@@ -424,6 +424,7 @@ QNetworkReplyHttpImplPrivate::QNetworkReplyHttpImplPrivate() + , synchronous(false) + , state(Idle) + , statusCode(0) ++ , uploadByteDevicePosition(false) + , uploadDeviceChoking(false) + , outgoingData(0) + , bytesUploaded(-1) +@@ -863,9 +864,9 @@ void QNetworkReplyHttpImplPrivate::postRequest() + q, SLOT(uploadByteDeviceReadyReadSlot()), + Qt::QueuedConnection); + +- // From main thread to user thread: +- QObject::connect(q, SIGNAL(haveUploadData(QByteArray,bool,qint64)), +- forwardUploadDevice, SLOT(haveDataSlot(QByteArray,bool,qint64)), Qt::QueuedConnection); ++ // From user thread to http thread: ++ QObject::connect(q, SIGNAL(haveUploadData(qint64,QByteArray,bool,qint64)), ++ forwardUploadDevice, SLOT(haveDataSlot(qint64,QByteArray,bool,qint64)), Qt::QueuedConnection); + QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()), + forwardUploadDevice, SIGNAL(readyRead()), + Qt::QueuedConnection); +@@ -873,8 +874,8 @@ void QNetworkReplyHttpImplPrivate::postRequest() + // From http thread to user thread: + QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)), + q, SLOT(wantUploadDataSlot(qint64))); +- QObject::connect(forwardUploadDevice, SIGNAL(processedData(qint64)), +- q, SLOT(sentUploadDataSlot(qint64))); ++ QObject::connect(forwardUploadDevice,SIGNAL(processedData(qint64, qint64)), ++ q, SLOT(sentUploadDataSlot(qint64,qint64))); + QObject::connect(forwardUploadDevice, SIGNAL(resetData(bool*)), + q, SLOT(resetUploadDataSlot(bool*)), + Qt::BlockingQueuedConnection); // this is the only one with BlockingQueued! +@@ -1268,12 +1269,22 @@ void QNetworkReplyHttpImplPrivate::replySslConfigurationChanged(const QSslConfig + void QNetworkReplyHttpImplPrivate::resetUploadDataSlot(bool *r) + { + *r = uploadByteDevice->reset(); ++ if (*r) { ++ // reset our own position which is used for the inter-thread communication ++ uploadByteDevicePosition = 0; ++ } + } + + // Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread +-void QNetworkReplyHttpImplPrivate::sentUploadDataSlot(qint64 amount) ++void QNetworkReplyHttpImplPrivate::sentUploadDataSlot(qint64 pos, qint64 amount) + { ++ if (uploadByteDevicePosition + amount != pos) { ++ // Sanity check, should not happen. ++ error(QNetworkReply::UnknownNetworkError, ""); ++ return; ++ } + uploadByteDevice->advanceReadPointer(amount); ++ uploadByteDevicePosition += amount; + } + + // Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread +@@ -1298,7 +1309,7 @@ void QNetworkReplyHttpImplPrivate::wantUploadDataSlot(qint64 maxSize) + QByteArray dataArray(data, currentUploadDataLength); + + // Communicate back to HTTP thread +- emit q->haveUploadData(dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size()); ++ emit q->haveUploadData(uploadByteDevicePosition, dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size()); + } + + void QNetworkReplyHttpImplPrivate::uploadByteDeviceReadyReadSlot() +diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h +index 77d9c5a..1940922 100644 +--- a/src/network/access/qnetworkreplyhttpimpl_p.h ++++ b/src/network/access/qnetworkreplyhttpimpl_p.h +@@ -120,7 +120,7 @@ public: + + Q_PRIVATE_SLOT(d_func(), void resetUploadDataSlot(bool *r)) + Q_PRIVATE_SLOT(d_func(), void wantUploadDataSlot(qint64)) +- Q_PRIVATE_SLOT(d_func(), void sentUploadDataSlot(qint64)) ++ Q_PRIVATE_SLOT(d_func(), void sentUploadDataSlot(qint64,qint64)) + Q_PRIVATE_SLOT(d_func(), void uploadByteDeviceReadyReadSlot()) + Q_PRIVATE_SLOT(d_func(), void emitReplyUploadProgress(qint64, qint64)) + Q_PRIVATE_SLOT(d_func(), void _q_cacheSaveDeviceAboutToClose()) +@@ -144,7 +144,7 @@ signals: + + void startHttpRequestSynchronously(); + +- void haveUploadData(QByteArray dataArray, bool dataAtEnd, qint64 dataSize); ++ void haveUploadData(const qint64 pos, QByteArray dataArray, bool dataAtEnd, qint64 dataSize); + }; + + class QNetworkReplyHttpImplPrivate: public QNetworkReplyPrivate +@@ -195,6 +195,7 @@ public: + // upload + QNonContiguousByteDevice* createUploadByteDevice(); + QSharedPointer uploadByteDevice; ++ qint64 uploadByteDevicePosition; + bool uploadDeviceChoking; // if we couldn't readPointer() any data at the moment + QIODevice *outgoingData; + QSharedPointer outgoingDataBuffer; +@@ -283,7 +284,7 @@ public: + // From QNonContiguousByteDeviceThreadForwardImpl in HTTP thread: + void resetUploadDataSlot(bool *r); + void wantUploadDataSlot(qint64); +- void sentUploadDataSlot(qint64); ++ void sentUploadDataSlot(qint64, qint64); + + // From user's QNonContiguousByteDevice + void uploadByteDeviceReadyReadSlot(); +diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +index 3ccedf6..d2edf67 100644 +--- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp ++++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +@@ -457,6 +457,10 @@ private Q_SLOTS: + + void putWithRateLimiting(); + ++#ifndef QT_NO_SSL ++ void putWithServerClosingConnectionImmediately(); ++#endif ++ + // NOTE: This test must be last! + void parentingRepliesToTheApp(); + private: +@@ -4718,18 +4722,22 @@ void tst_QNetworkReply::ioPostToHttpNoBufferFlag() + class SslServer : public QTcpServer { + Q_OBJECT + public: +- SslServer() : socket(0) {}; ++ SslServer() : socket(0), m_ssl(true) {} + void incomingConnection(qintptr socketDescriptor) { + QSslSocket *serverSocket = new QSslSocket; + serverSocket->setParent(this); + + if (serverSocket->setSocketDescriptor(socketDescriptor)) { ++ connect(serverSocket, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); ++ if (!m_ssl) { ++ emit newPlainConnection(serverSocket); ++ return; ++ } + QString testDataDir = QFileInfo(QFINDTESTDATA("rfc3252.txt")).absolutePath(); + if (testDataDir.isEmpty()) + testDataDir = QCoreApplication::applicationDirPath(); + + connect(serverSocket, SIGNAL(encrypted()), this, SLOT(encryptedSlot())); +- connect(serverSocket, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); + serverSocket->setProtocol(QSsl::AnyProtocol); + connect(serverSocket, SIGNAL(sslErrors(QList)), serverSocket, SLOT(ignoreSslErrors())); + serverSocket->setLocalCertificate(testDataDir + "/certs/server.pem"); +@@ -4740,11 +4748,12 @@ public: + } + } + signals: +- void newEncryptedConnection(); ++ void newEncryptedConnection(QSslSocket *s); ++ void newPlainConnection(QSslSocket *s); + public slots: + void encryptedSlot() { + socket = (QSslSocket*) sender(); +- emit newEncryptedConnection(); ++ emit newEncryptedConnection(socket); + } + void readyReadSlot() { + // for the incoming sockets, not the server socket +@@ -4753,6 +4762,7 @@ public slots: + + public: + QSslSocket *socket; ++ bool m_ssl; + }; + + // very similar to ioPostToHttpUploadProgress but for SSL +@@ -4780,7 +4790,7 @@ void tst_QNetworkReply::ioPostToHttpsUploadProgress() + QNetworkReplyPtr reply(manager.post(request, sourceFile)); + + QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64))); +- connect(&server, SIGNAL(newEncryptedConnection()), &QTestEventLoop::instance(), SLOT(exitLoop())); ++ connect(&server, SIGNAL(newEncryptedConnection(QSslSocket*)), &QTestEventLoop::instance(), SLOT(exitLoop())); + connect(reply, SIGNAL(sslErrors(QList)), reply.data(), SLOT(ignoreSslErrors())); + + // get the request started and the incoming socket connected +@@ -4788,7 +4798,7 @@ void tst_QNetworkReply::ioPostToHttpsUploadProgress() + QVERIFY(!QTestEventLoop::instance().timeout()); + QTcpSocket *incomingSocket = server.socket; + QVERIFY(incomingSocket); +- disconnect(&server, SIGNAL(newEncryptedConnection()), &QTestEventLoop::instance(), SLOT(exitLoop())); ++ disconnect(&server, SIGNAL(newEncryptedConnection(QSslSocket*)), &QTestEventLoop::instance(), SLOT(exitLoop())); + + + incomingSocket->setReadBufferSize(1*1024); +@@ -7905,6 +7915,159 @@ void tst_QNetworkReply::putWithRateLimiting() + } + + ++#ifndef QT_NO_SSL ++ ++class PutWithServerClosingConnectionImmediatelyHandler: public QObject ++{ ++ Q_OBJECT ++public: ++ bool m_parsedHeaders; ++ QByteArray m_receivedData; ++ QByteArray m_expectedData; ++ QSslSocket *m_socket; ++ PutWithServerClosingConnectionImmediatelyHandler(QSslSocket *s, QByteArray expected) :m_parsedHeaders(false), m_expectedData(expected), m_socket(s) ++ { ++ m_socket->setParent(this); ++ connect(m_socket, SIGNAL(readyRead()), SLOT(readyReadSlot())); ++ connect(m_socket, SIGNAL(disconnected()), SLOT(disconnectedSlot())); ++ } ++signals: ++ void correctFileUploadReceived(); ++ void corruptFileUploadReceived(); ++ ++public slots: ++ void closeDelayed() { ++ m_socket->close(); ++ } ++ ++ void readyReadSlot() ++ { ++ QByteArray data = m_socket->readAll(); ++ m_receivedData += data; ++ if (!m_parsedHeaders && m_receivedData.contains("\r\n\r\n")) { ++ m_parsedHeaders = true; ++ QTimer::singleShot(qrand()%10, this, SLOT(closeDelayed())); // simulate random network latency ++ // This server simulates a web server connection closing, e.g. because of Apaches MaxKeepAliveRequests or KeepAliveTimeout ++ // In this case QNAM needs to re-send the upload data but it had a bug which then corrupts the upload ++ // This test catches that. ++ } ++ ++ } ++ void disconnectedSlot() ++ { ++ if (m_parsedHeaders) { ++ //qDebug() << m_receivedData.left(m_receivedData.indexOf("\r\n\r\n")); ++ m_receivedData = m_receivedData.mid(m_receivedData.indexOf("\r\n\r\n")+4); // check only actual data ++ } ++ if (m_receivedData.length() > 0 && !m_expectedData.startsWith(m_receivedData)) { ++ // We had received some data but it is corrupt! ++ qDebug() << "CORRUPT" << m_receivedData.count(); ++ ++ // Use this to track down the pattern of the corruption and conclude the source ++// QFile a("/tmp/corrupt"); ++// a.open(QIODevice::WriteOnly); ++// a.write(m_receivedData); ++// a.close(); ++ ++// QFile b("/tmp/correct"); ++// b.open(QIODevice::WriteOnly); ++// b.write(m_expectedData); ++// b.close(); ++ //exit(1); ++ emit corruptFileUploadReceived(); ++ } else { ++ emit correctFileUploadReceived(); ++ } ++ } ++}; ++ ++class PutWithServerClosingConnectionImmediatelyServer: public SslServer ++{ ++ Q_OBJECT ++public: ++ int m_correctUploads; ++ int m_corruptUploads; ++ int m_repliesFinished; ++ int m_expectedReplies; ++ QByteArray m_expectedData; ++ PutWithServerClosingConnectionImmediatelyServer() : SslServer(), m_correctUploads(0), m_corruptUploads(0), m_repliesFinished(0), m_expectedReplies(0) ++ { ++ QObject::connect(this, SIGNAL(newEncryptedConnection(QSslSocket*)), this, SLOT(createHandlerForConnection(QSslSocket*))); ++ QObject::connect(this, SIGNAL(newPlainConnection(QSslSocket*)), this, SLOT(createHandlerForConnection(QSslSocket*))); ++ } ++ ++public slots: ++ void createHandlerForConnection(QSslSocket* s) { ++ PutWithServerClosingConnectionImmediatelyHandler *handler = new PutWithServerClosingConnectionImmediatelyHandler(s, m_expectedData); ++ handler->setParent(this); ++ QObject::connect(handler, SIGNAL(correctFileUploadReceived()), this, SLOT(increaseCorrect())); ++ QObject::connect(handler, SIGNAL(corruptFileUploadReceived()), this, SLOT(increaseCorrupt())); ++ } ++ void increaseCorrect() { ++ m_correctUploads++; ++ } ++ void increaseCorrupt() { ++ m_corruptUploads++; ++ } ++ void replyFinished() { ++ m_repliesFinished++; ++ if (m_repliesFinished == m_expectedReplies) { ++ QTestEventLoop::instance().exitLoop(); ++ } ++ } ++}; ++ ++ ++ ++void tst_QNetworkReply::putWithServerClosingConnectionImmediately() ++{ ++ const int numUploads = 40; ++ qint64 wantedSize = 512*1024; // 512 kB ++ QByteArray sourceFile; ++ for (int i = 0; i < wantedSize; ++i) { ++ sourceFile += (char)'a' +(i%26); ++ } ++ bool withSsl = false; ++ ++ for (int s = 0; s <= 1; s++) { ++ withSsl = (s == 1); ++ // Test also needs to run several times because of 9c2ecf89 ++ for (int j = 0; j < 20; j++) { ++ // emulate a minimal https server ++ PutWithServerClosingConnectionImmediatelyServer server; ++ server.m_ssl = withSsl; ++ server.m_expectedData = sourceFile; ++ server.m_expectedReplies = numUploads; ++ server.listen(QHostAddress(QHostAddress::LocalHost), 0); ++ ++ for (int i = 0; i < numUploads; i++) { ++ // create the request ++ QUrl url = QUrl(QString("http%1://127.0.0.1:%2/file=%3").arg(withSsl ? "s" : "").arg(server.serverPort()).arg(i)); ++ QNetworkRequest request(url); ++ QNetworkReply *reply = manager.put(request, sourceFile); ++ connect(reply, SIGNAL(sslErrors(QList)), reply, SLOT(ignoreSslErrors())); ++ connect(reply, SIGNAL(finished()), &server, SLOT(replyFinished())); ++ reply->setParent(&server); ++ } ++ ++ // get the request started and the incoming socket connected ++ QTestEventLoop::instance().enterLoop(10); ++ ++ //qDebug() << "correct=" << server.m_correctUploads << "corrupt=" << server.m_corruptUploads << "expected=" < 5); ++ // Because actually important is that we don't get any corruption: ++ QCOMPARE(server.m_corruptUploads, 0); ++ ++ server.close(); ++ } ++ } ++ ++ ++} ++ ++#endif + + // NOTE: This test must be last testcase in tst_qnetworkreply! + void tst_QNetworkReply::parentingRepliesToTheApp() +-- +1.9.1 + diff --git a/admin/qt/patches/0014-Fix-SNI-for-TlsV1_0OrLater-TlsV1_1OrLater-and-TlsV1_.patch b/admin/qt/patches/0014-Fix-SNI-for-TlsV1_0OrLater-TlsV1_1OrLater-and-TlsV1_.patch deleted file mode 100644 index b58bacd29..000000000 --- a/admin/qt/patches/0014-Fix-SNI-for-TlsV1_0OrLater-TlsV1_1OrLater-and-TlsV1_.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 0d3c8feeeb3c49d01bcfbca63b672e20ab29aa9f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= -Date: Thu, 30 Jul 2015 10:08:36 +0200 -Subject: [PATCH] Fix SNI for TlsV1_0OrLater, TlsV1_1OrLater and TlsV1_2OrLater - -Since SslV3, SslV2 and UnknownProtocol do not support it we can -invert the IF clause here. - -Change-Id: I42e942337d01f3a8c97885b268bffa568e40d335 -Task-number: QTBUG-47528 -Reviewed-by: Mikkel Krautz -Reviewed-by: Richard J. Moore ---- - src/network/ssl/qsslsocket_openssl.cpp | 9 +++------ - 1 file changed, 3 insertions(+), 6 deletions(-) - -diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp -index 049666b..bc1df81 100644 ---- a/src/network/ssl/qsslsocket_openssl.cpp -+++ b/src/network/ssl/qsslsocket_openssl.cpp -@@ -377,12 +377,9 @@ bool QSslSocketBackendPrivate::initSslContext() - return false; - } - -- if ((configuration.protocol == QSsl::TlsV1SslV3 || -- configuration.protocol == QSsl::TlsV1_0 || -- configuration.protocol == QSsl::TlsV1_1 || -- configuration.protocol == QSsl::TlsV1_2 || -- configuration.protocol == QSsl::SecureProtocols || -- configuration.protocol == QSsl::AnyProtocol) && -+ if (configuration.protocol != QSsl::SslV2 && -+ configuration.protocol != QSsl::SslV3 && -+ configuration.protocol != QSsl::UnknownProtocol && - mode == QSslSocket::SslClientMode && q_SSLeay() >= 0x00090806fL) { - // Set server hostname on TLS extension. RFC4366 section 3.1 requires it in ACE format. - QString tlsHostName = verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName; --- -1.9.1 diff --git a/admin/qt/patches/README.md b/admin/qt/patches/README.md index f41bcd603..466a6c7ce 100644 --- a/admin/qt/patches/README.md +++ b/admin/qt/patches/README.md @@ -16,9 +16,10 @@ purpose is outlined in each patches' front matter. * 0003-Fix-crash-if-PAC-script-retrieval-returns-a-null-CFD.patch ### Part of Qt v5.4.2 and later -* 0004-OSX-Fix-disapearing-tray-icon.patch +* 0004-Cocoa-Fix-systray-SVG-icons.patch +* 0005-OSX-Fix-disapearing-tray-icon.patch (TODO: actual patch slighly differs) -* 0006-QNAM-Fix-upload-corruptions-when-server-closes-conne.patch +* 0007-QNAM-Fix-upload-corruptions-when-server-closes-conne.patch (TODO: Actual patch on build machine spans over two commit but is identical) ### Part of Qt v5.5.1 and later @@ -35,7 +36,7 @@ purpose is outlined in each patches' front matter. * 0016-Fix-possible-crash-when-passing-an-invalid-PAC-URL.patch ### Not submitted to be part of any release: -* 0005-Fix-force-debug-info-with-macx-clang_NOUPSTREAM.patch +* 0006-Fix-force-debug-info-with-macx-clang_NOUPSTREAM.patch This is only needed if you intent to harvest debugging symbols for breakpad.