diff --git a/admin/qt/patches/0001-Fix-crash-on-Mac-OS-if-PAC-URL-contains-non-URL-lega.patch b/admin/qt/patches/0001-Fix-crash-on-Mac-OS-if-PAC-URL-contains-non-URL-lega.patch new file mode 100644 index 0000000000..1e46cd9fb6 --- /dev/null +++ b/admin/qt/patches/0001-Fix-crash-on-Mac-OS-if-PAC-URL-contains-non-URL-lega.patch @@ -0,0 +1,42 @@ +From ea4dcc5931d455e4ee3e958ffa54a9f54ab022c8 Mon Sep 17 00:00:00 2001 +From: Daniel Molkentin +Date: Mon, 5 Jan 2015 10:45:25 +0100 +Subject: [PATCH 1/3] Fix crash on Mac OS if PAC URL contains non-URL legal + chars + +macQueryInternal() was retrieving the PAC URL string as-entered by +the user in the 'Proxies' tab of the system network settings dialog +and passing it to CFURLCreateWithString(). + +CFURLCreateWithString() returns null if the input string contains +non-URL legal chars or is empty. + +Change-Id: I9166d0433a62c7b2274b5435a7dea0a16997d10e +Patch-By: Robert Knight +Task-number: QTBUG-36787 +Reviewed-by: Peter Hartmann +Reviewed-by: Markus Goetz +--- + src/network/kernel/qnetworkproxy_mac.cpp | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_mac.cpp +index 7d26246..81bce0c 100644 +--- a/src/network/kernel/qnetworkproxy_mac.cpp ++++ b/src/network/kernel/qnetworkproxy_mac.cpp +@@ -221,7 +221,11 @@ QList macQueryInternal(const QNetworkProxyQuery &query) + int enabled; + if (CFNumberGetValue(pacEnabled, kCFNumberIntType, &enabled) && enabled) { + // PAC is enabled +- CFStringRef cfPacLocation = (CFStringRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigURLString); ++ // kSCPropNetProxiesProxyAutoConfigURLString returns the URL string ++ // as entered in the system proxy configuration dialog ++ CFStringRef pacLocationSetting = (CFStringRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigURLString); ++ QCFType cfPacLocation = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, pacLocationSetting, NULL, NULL, ++ kCFStringEncodingUTF8); + + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) { + QCFType pacData; +-- +1.8.3.4 (Apple Git-47) + diff --git a/admin/qt/patches/0002-Fix-possible-crash-when-passing-an-invalid-PAC-URL.patch b/admin/qt/patches/0002-Fix-possible-crash-when-passing-an-invalid-PAC-URL.patch new file mode 100644 index 0000000000..275b3479a9 --- /dev/null +++ b/admin/qt/patches/0002-Fix-possible-crash-when-passing-an-invalid-PAC-URL.patch @@ -0,0 +1,38 @@ +From a83e4d1d9dd90d4563ce60f27dfb7802a780e33e Mon Sep 17 00:00:00 2001 +From: Daniel Molkentin +Date: Mon, 5 Jan 2015 11:42:52 +0100 +Subject: [PATCH 2/3] Fix possible crash when passing an invalid PAC URL + +This commit checks whether CFURLCreateWithString() succeeded. + +It does not appear to be possible to enter an empty URL directly in the +PAC configuration dialog but I can't rule out the possibility +that it could find its way into the settings via some other means. + +Change-Id: I6c2053d385503bf0330f5ae9fb1ec36a473d425d +Patch-By: Robert Knight +Task-number: QTBUG-36787 +Reviewed-by: Markus Goetz +Reviewed-by: Peter Hartmann +--- + src/network/kernel/qnetworkproxy_mac.cpp | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_mac.cpp +index 81bce0c..6be032e 100644 +--- a/src/network/kernel/qnetworkproxy_mac.cpp ++++ b/src/network/kernel/qnetworkproxy_mac.cpp +@@ -230,6 +230,10 @@ QList macQueryInternal(const QNetworkProxyQuery &query) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) { + QCFType pacData; + QCFType pacUrl = CFURLCreateWithString(kCFAllocatorDefault, cfPacLocation, NULL); ++ if (!pacUrl) { ++ qWarning("Invalid PAC URL \"%s\"", qPrintable(QCFString::toQString(cfPacLocation))); ++ return result; ++ } + SInt32 errorCode; + if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, pacUrl, &pacData, NULL, NULL, &errorCode)) { + QString pacLocation = QCFString::toQString(cfPacLocation); +-- +1.8.3.4 (Apple Git-47) + diff --git a/admin/qt/patches/0003-Fix-crash-if-PAC-script-retrieval-returns-a-null-CFD.patch b/admin/qt/patches/0003-Fix-crash-if-PAC-script-retrieval-returns-a-null-CFD.patch new file mode 100644 index 0000000000..c144c2d97f --- /dev/null +++ b/admin/qt/patches/0003-Fix-crash-if-PAC-script-retrieval-returns-a-null-CFD.patch @@ -0,0 +1,39 @@ +From 83bd9393e5564ea9168fda90c0f44456633a483a Mon Sep 17 00:00:00 2001 +From: Daniel Molkentin +Date: Mon, 5 Jan 2015 15:22:57 +0100 +Subject: [PATCH 3/3] Fix crash if PAC script retrieval returns a null CFData + instance + +The documentation for CFURLCreateDataAndPropertiesFromResource() +does not make this clear but from looking at the CFNetwork implementation +and a user stacktrace it appears that this function can return true +but not set the data argument under certain circumstances. + +Change-Id: I48034a640d6f47a51cd5883bbafacad4bcbd0415 +Task-number: QTBUG-36787 +Patch-By: Robert Knight +Reviewed-by: Markus Goetz +Reviewed-by: Peter Hartmann +--- + src/network/kernel/qnetworkproxy_mac.cpp | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_mac.cpp +index 6be032e..a1ac349 100644 +--- a/src/network/kernel/qnetworkproxy_mac.cpp ++++ b/src/network/kernel/qnetworkproxy_mac.cpp +@@ -240,7 +240,10 @@ QList macQueryInternal(const QNetworkProxyQuery &query) + qWarning("Unable to get the PAC script at \"%s\" (%s)", qPrintable(pacLocation), cfurlErrorDescription(errorCode)); + return result; + } +- ++ if (!pacData) { ++ qWarning("\"%s\" returned an empty PAC script", qPrintable(QCFString::toQString(cfPacLocation))); ++ return result; ++ } + QCFType pacScript = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, pacData, kCFStringEncodingISOLatin1); + if (!pacScript) { + // This should never happen, but the documentation says it may return NULL if there was a problem creating the object. +-- +1.8.3.4 (Apple Git-47) + diff --git a/admin/qt/patches/0004-OSX-Fix-disapearing-tray-icon.patch b/admin/qt/patches/0004-OSX-Fix-disapearing-tray-icon.patch new file mode 100644 index 0000000000..df6b4eff3e --- /dev/null +++ b/admin/qt/patches/0004-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/0005-Fix-force-debug-info-with-macx-clang_NOUPSTREAM.patch b/admin/qt/patches/0005-Fix-force-debug-info-with-macx-clang_NOUPSTREAM.patch new file mode 100644 index 0000000000..ea9c8b875a --- /dev/null +++ b/admin/qt/patches/0005-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 new file mode 100644 index 0000000000..edd46eb7e2 --- /dev/null +++ b/admin/qt/patches/0006-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/0008-QNAM-Fix-reply-deadlocks-on-server-closing-connectio.patch b/admin/qt/patches/0008-QNAM-Fix-reply-deadlocks-on-server-closing-connectio.patch new file mode 100644 index 0000000000..c425118aaf --- /dev/null +++ b/admin/qt/patches/0008-QNAM-Fix-reply-deadlocks-on-server-closing-connectio.patch @@ -0,0 +1,64 @@ +From bc32c0ebc0bc00db84ca2f28eb16ab2e5b53a1b6 Mon Sep 17 00:00:00 2001 +From: Markus Goetz +Date: Fri, 24 Jul 2015 09:53:20 +0200 +Subject: [PATCH] QNAM: Fix reply deadlocks on server closing connection + +The _q_readyRead can also be called from readMoreLater() because we implemented +it so that bandwidth limited reading can be implemented. +This can lead to a race condition if the socket is closing at the specific moment +and then deadlock the channel: It will stay unusable with a zombie request. +The fix in QHttpProtocolaHandler checks if there is actually bytes available to read +from the socket and only then continue. + +The fix in the HTTP channel needs to be done to properly finish the reply in +cases of a server replying with HTTP/1.0 or "Connection: close". +The delayed incovation of _q_receiveReply will properly finish up the reply. + +Change-Id: I19ce2ae595f91d56386cc7406ccacc9935672b6b +Reviewed-by: Richard J. Moore +--- + src/network/access/qhttpnetworkconnectionchannel.cpp | 4 ++++ + src/network/access/qhttpprotocolhandler.cpp | 7 ++++++- + 2 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp +index 7428f9b..257aa13 100644 +--- a/src/network/access/qhttpnetworkconnectionchannel.cpp ++++ b/src/network/access/qhttpnetworkconnectionchannel.cpp +@@ -829,11 +829,15 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket + + if (!reply->d_func()->expectContent()) { + // No content expected, this is a valid way to have the connection closed by the server ++ // We need to invoke this asynchronously to make sure the state() of the socket is on QAbstractSocket::UnconnectedState ++ QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection); + return; + } + if (reply->contentLength() == -1 && !reply->d_func()->isChunked()) { + // There was no content-length header and it's not chunked encoding, + // so this is a valid way to have the connection closed by the server ++ // We need to invoke this asynchronously to make sure the state() of the socket is on QAbstractSocket::UnconnectedState ++ QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection); + return; + } + // ok, we got a disconnect even though we did not expect it +diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp +index ab2e3da..a208315 100644 +--- a/src/network/access/qhttpprotocolhandler.cpp ++++ b/src/network/access/qhttpprotocolhandler.cpp +@@ -237,7 +237,12 @@ void QHttpProtocolHandler::_q_readyRead() + } + + if (m_channel->isSocketWaiting() || m_channel->isSocketReading()) { +- m_channel->state = QHttpNetworkConnectionChannel::ReadingState; ++ if (m_socket->bytesAvailable()) { ++ // We might get a spurious call from readMoreLater() ++ // call of the QHttpNetworkConnection even while the socket is disconnecting. ++ // Therefore check if there is actually bytes available before changing the channel state. ++ m_channel->state = QHttpNetworkConnectionChannel::ReadingState; ++ } + if (m_reply) + _q_receiveReply(); + } +-- +1.9.1 + diff --git a/admin/qt/patches/0009-QNAM-Assign-proper-channel-before-sslErrors-emission.patch b/admin/qt/patches/0009-QNAM-Assign-proper-channel-before-sslErrors-emission.patch new file mode 100644 index 0000000000..e66c4c2d5c --- /dev/null +++ b/admin/qt/patches/0009-QNAM-Assign-proper-channel-before-sslErrors-emission.patch @@ -0,0 +1,33 @@ +From 63cf5d3d26a6f65938c3cdec1734eac9faaaf8cb Mon Sep 17 00:00:00 2001 +From: Markus Goetz +Date: Tue, 22 Sep 2015 14:26:24 -0400 +Subject: [PATCH] QNAM: Assign proper channel before sslErrors() emission + +There can be a race condition where another channel connects +and gets the sslErrors() from the socket first. Then the +QSslConfiguration from the wrong socket (the default +channel 0's socket) was used. + +Task-number: QTBUG-18722 +Change-Id: Ibbfa48c27f181563745daf540fa792a57cc09682 +Reviewed-by: Richard J. Moore +--- + src/network/access/qhttpnetworkconnectionchannel.cpp | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp +index 257aa13..477cba2 100644 +--- a/src/network/access/qhttpnetworkconnectionchannel.cpp ++++ b/src/network/access/qhttpnetworkconnectionchannel.cpp +@@ -1066,6 +1066,8 @@ void QHttpNetworkConnectionChannel::_q_sslErrors(const QList &errors) + connection->d_func()->pauseConnection(); + if (pendingEncrypt && !reply) + connection->d_func()->dequeueRequest(socket); ++ if (reply) // a reply was actually dequeued. ++ reply->d_func()->connectionChannel = this; // set correct channel like in sendRequest() and queueRequest(); + if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP) { + if (reply) + emit reply->sslErrors(errors); +-- +1.9.1 + diff --git a/admin/qt/patches/0011-Make-sure-to-report-correct-NetworkAccessibility.patch b/admin/qt/patches/0011-Make-sure-to-report-correct-NetworkAccessibility.patch new file mode 100644 index 0000000000..9fe366df2f --- /dev/null +++ b/admin/qt/patches/0011-Make-sure-to-report-correct-NetworkAccessibility.patch @@ -0,0 +1,113 @@ +From c056e63cea1915667997c982f48296ce5acdcc80 Mon Sep 17 00:00:00 2001 +From: Lorn Potter +Date: Tue, 2 Jun 2015 13:22:23 +1000 +Subject: [PATCH] Make sure to report correct NetworkAccessibility + +Task-number: QTBUG-46323 +Change-Id: Ibdeb3280091a97d785d4314340678a63e88fb219 +Reviewed-by: Markus Goetz (Woboq GmbH) +Reviewed-by: Alex Blasche +--- + src/network/access/qnetworkaccessmanager.cpp | 25 +++++++++++++++++-------- + src/network/access/qnetworkaccessmanager_p.h | 2 ++ + 2 files changed, 19 insertions(+), 8 deletions(-) + +diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp +index e878feb..84931cb 100644 +--- a/src/network/access/qnetworkaccessmanager.cpp ++++ b/src/network/access/qnetworkaccessmanager.cpp +@@ -472,11 +472,11 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent) + // the QNetworkSession's signals + connect(&d->networkConfigurationManager, SIGNAL(onlineStateChanged(bool)), + SLOT(_q_onlineStateChanged(bool))); +- // we would need all active configurations to check for +- // d->networkConfigurationManager.isOnline(), which is asynchronous +- // and potentially expensive. We can just check the configuration here +- d->online = (d->networkConfiguration.state() & QNetworkConfiguration::Active); + } ++ // we would need all active configurations to check for ++ // d->networkConfigurationManager.isOnline(), which is asynchronous ++ // and potentially expensive. We can just check the configuration here ++ d->online = (d->networkConfiguration.state() & QNetworkConfiguration::Active); + #endif + } + +@@ -946,6 +946,7 @@ QNetworkConfiguration QNetworkAccessManager::activeConfiguration() const + void QNetworkAccessManager::setNetworkAccessible(QNetworkAccessManager::NetworkAccessibility accessible) + { + Q_D(QNetworkAccessManager); ++ d->defaultAccessControl = false; + + if (d->networkAccessible != accessible) { + NetworkAccessibility previous = networkAccessible(); +@@ -964,7 +965,6 @@ void QNetworkAccessManager::setNetworkAccessible(QNetworkAccessManager::NetworkA + QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccessible() const + { + Q_D(const QNetworkAccessManager); +- + if (d->networkSessionRequired) { + QSharedPointer networkSession(d->getNetworkSession()); + if (networkSession) { +@@ -975,7 +975,13 @@ QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccess + return NotAccessible; + } else { + // Network accessibility is either disabled or unknown. +- return (d->networkAccessible == NotAccessible) ? NotAccessible : UnknownAccessibility; ++ if (d->defaultAccessControl) { ++ if (d->online) ++ return d->networkAccessible; ++ else ++ return NotAccessible; ++ } ++ return (d->networkAccessible); + } + } else { + if (d->online) +@@ -1568,7 +1574,7 @@ void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &co + if (!networkSessionStrongRef) { + online = false; + +- if (networkAccessible == QNetworkAccessManager::NotAccessible) ++ if (networkAccessible == QNetworkAccessManager::NotAccessible || !online) + emit q->networkAccessibleChanged(QNetworkAccessManager::NotAccessible); + else + emit q->networkAccessibleChanged(QNetworkAccessManager::UnknownAccessibility); +@@ -1616,11 +1622,14 @@ void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession + if (online) { + if (state != QNetworkSession::Connected && state != QNetworkSession::Roaming) { + online = false; +- emit q->networkAccessibleChanged(QNetworkAccessManager::NotAccessible); ++ networkAccessible = QNetworkAccessManager::NotAccessible; ++ emit q->networkAccessibleChanged(networkAccessible); + } + } else { + if (state == QNetworkSession::Connected || state == QNetworkSession::Roaming) { + online = true; ++ if (defaultAccessControl) ++ networkAccessible = QNetworkAccessManager::Accessible; + emit q->networkAccessibleChanged(networkAccessible); + } + } +diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h +index f513324..c715da0 100644 +--- a/src/network/access/qnetworkaccessmanager_p.h ++++ b/src/network/access/qnetworkaccessmanager_p.h +@@ -84,6 +84,7 @@ public: + initializeSession(true), + #endif + cookieJarCreated(false), ++ defaultAccessControl(true), + authenticationManager(QSharedPointer::create()) + { } + ~QNetworkAccessManagerPrivate(); +@@ -164,6 +165,7 @@ public: + #endif + + bool cookieJarCreated; ++ bool defaultAccessControl; + + // The cache with authorization data: + QSharedPointer authenticationManager; +-- +1.9.1 + diff --git a/admin/qt/patches/0012-Make-sure-networkAccessibilityChanged-is-emitted.patch b/admin/qt/patches/0012-Make-sure-networkAccessibilityChanged-is-emitted.patch new file mode 100644 index 0000000000..5879b538a4 --- /dev/null +++ b/admin/qt/patches/0012-Make-sure-networkAccessibilityChanged-is-emitted.patch @@ -0,0 +1,233 @@ +From bb281eea179d50a413f4ec1ff172d27ee48d3a41 Mon Sep 17 00:00:00 2001 +From: Lorn Potter +Date: Fri, 17 Jul 2015 15:32:23 +1000 +Subject: [PATCH] Make sure networkAccessibilityChanged is emitted + +Task-number: QTBUG-46323 +Change-Id: I8297072b62763136f457ca6ae15282d1c22244f4 +Reviewed-by: Timo Jyrinki +Reviewed-by: Alex Blasche +--- + src/network/access/qnetworkaccessmanager.cpp | 70 +++++++++++++++------- + src/network/access/qnetworkaccessmanager_p.h | 14 ++++- + .../tst_qnetworkaccessmanager.cpp | 31 +++++----- + 3 files changed, 77 insertions(+), 38 deletions(-) + +diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp +index 84931cb..f9e9513 100644 +--- a/src/network/access/qnetworkaccessmanager.cpp ++++ b/src/network/access/qnetworkaccessmanager.cpp +@@ -278,7 +278,8 @@ static void ensureInitialized() + + \snippet code/src_network_access_qnetworkaccessmanager.cpp 4 + +- Network requests can be reenabled again by calling ++ Network requests can be re-enabled again, and this property will resume to ++ reflect the actual device state by calling + + \snippet code/src_network_access_qnetworkaccessmanager.cpp 5 + +@@ -467,16 +468,12 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent) + qRegisterMetaType >(); + + #ifndef QT_NO_BEARERMANAGEMENT +- if (!d->networkSessionRequired) { +- // if a session is required, we track online state through +- // the QNetworkSession's signals +- connect(&d->networkConfigurationManager, SIGNAL(onlineStateChanged(bool)), +- SLOT(_q_onlineStateChanged(bool))); +- } +- // we would need all active configurations to check for +- // d->networkConfigurationManager.isOnline(), which is asynchronous +- // and potentially expensive. We can just check the configuration here +- d->online = (d->networkConfiguration.state() & QNetworkConfiguration::Active); ++ // if a session is required, we track online state through ++ // the QNetworkSession's signals if a request is already made. ++ // we need to track current accessibility state by default ++ // ++ connect(&d->networkConfigurationManager, SIGNAL(onlineStateChanged(bool)), ++ SLOT(_q_onlineStateChanged(bool))); + #endif + } + +@@ -946,7 +943,8 @@ QNetworkConfiguration QNetworkAccessManager::activeConfiguration() const + void QNetworkAccessManager::setNetworkAccessible(QNetworkAccessManager::NetworkAccessibility accessible) + { + Q_D(QNetworkAccessManager); +- d->defaultAccessControl = false; ++ ++ d->defaultAccessControl = accessible == NotAccessible ? false : true; + + if (d->networkAccessible != accessible) { + NetworkAccessibility previous = networkAccessible(); +@@ -965,6 +963,10 @@ void QNetworkAccessManager::setNetworkAccessible(QNetworkAccessManager::NetworkA + QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccessible() const + { + Q_D(const QNetworkAccessManager); ++ ++ if (d->networkConfiguration.state().testFlag(QNetworkConfiguration::Undefined)) ++ return UnknownAccessibility; ++ + if (d->networkSessionRequired) { + QSharedPointer networkSession(d->getNetworkSession()); + if (networkSession) { +@@ -1622,32 +1624,56 @@ void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession + if (online) { + if (state != QNetworkSession::Connected && state != QNetworkSession::Roaming) { + online = false; +- networkAccessible = QNetworkAccessManager::NotAccessible; +- emit q->networkAccessibleChanged(networkAccessible); ++ if (networkAccessible != QNetworkAccessManager::NotAccessible) { ++ networkAccessible = QNetworkAccessManager::NotAccessible; ++ emit q->networkAccessibleChanged(networkAccessible); ++ } + } + } else { + if (state == QNetworkSession::Connected || state == QNetworkSession::Roaming) { + online = true; + if (defaultAccessControl) +- networkAccessible = QNetworkAccessManager::Accessible; +- emit q->networkAccessibleChanged(networkAccessible); ++ if (networkAccessible != QNetworkAccessManager::Accessible) { ++ networkAccessible = QNetworkAccessManager::Accessible; ++ emit q->networkAccessibleChanged(networkAccessible); ++ } + } + } + } + + void QNetworkAccessManagerPrivate::_q_onlineStateChanged(bool isOnline) + { +- // if the user set a config, we only care whether this one is active. ++ Q_Q(QNetworkAccessManager); ++ // if the user set a config, we only care whether this one is active. + // Otherwise, this QNAM is online if there is an online config. + if (customNetworkConfiguration) { + online = (networkConfiguration.state() & QNetworkConfiguration::Active); + } else { +- if (isOnline && online != isOnline) { +- networkSessionStrongRef.clear(); +- networkSessionWeakRef.clear(); ++ if (online != isOnline) { ++ if (isOnline) { ++ networkSessionStrongRef.clear(); ++ networkSessionWeakRef.clear(); ++ } ++ online = isOnline; ++ } ++ } ++ if (online) { ++ if (defaultAccessControl) { ++ if (networkAccessible != QNetworkAccessManager::Accessible) { ++ networkAccessible = QNetworkAccessManager::Accessible; ++ emit q->networkAccessibleChanged(networkAccessible); ++ } ++ } ++ } else if (networkConfiguration.state().testFlag(QNetworkConfiguration::Undefined)) { ++ if (networkAccessible != QNetworkAccessManager::UnknownAccessibility) { ++ networkAccessible = QNetworkAccessManager::UnknownAccessibility; ++ emit q->networkAccessibleChanged(networkAccessible); ++ } ++ } else { ++ if (networkAccessible != QNetworkAccessManager::NotAccessible) { ++ networkAccessible = QNetworkAccessManager::NotAccessible; ++ emit q->networkAccessibleChanged(networkAccessible); + } +- +- online = isOnline; + } + } + +diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h +index c715da0..54ae114 100644 +--- a/src/network/access/qnetworkaccessmanager_p.h ++++ b/src/network/access/qnetworkaccessmanager_p.h +@@ -78,7 +78,6 @@ public: + customNetworkConfiguration(false), + networkSessionRequired(networkConfigurationManager.capabilities() + & QNetworkConfigurationManager::NetworkSessionRequired), +- networkAccessible(QNetworkAccessManager::Accessible), + activeReplyCount(0), + online(false), + initializeSession(true), +@@ -86,7 +85,18 @@ public: + cookieJarCreated(false), + defaultAccessControl(true), + authenticationManager(QSharedPointer::create()) +- { } ++ { ++#ifndef QT_NO_BEARERMANAGEMENT ++ // we would need all active configurations to check for ++ // d->networkConfigurationManager.isOnline(), which is asynchronous ++ // and potentially expensive. We can just check the configuration here ++ online = (networkConfiguration.state().testFlag(QNetworkConfiguration::Active)); ++ if (online) ++ networkAccessible = QNetworkAccessManager::Accessible; ++ else ++ networkAccessible = QNetworkAccessManager::NotAccessible; ++#endif ++ } + ~QNetworkAccessManagerPrivate(); + + void _q_replyFinished(); +diff --git a/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp b/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp +index b4e4b9c..8ecb57d 100644 +--- a/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp ++++ b/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp +@@ -74,6 +74,10 @@ void tst_QNetworkAccessManager::networkAccessible() + // if there is no session, we cannot know in which state we are in + QNetworkAccessManager::NetworkAccessibility initialAccessibility = + manager.networkAccessible(); ++ ++ if (initialAccessibility == QNetworkAccessManager::UnknownAccessibility) ++ QSKIP("Unknown accessibility", SkipAll); ++ + QCOMPARE(manager.networkAccessible(), initialAccessibility); + + manager.setNetworkAccessible(QNetworkAccessManager::NotAccessible); +@@ -94,29 +98,28 @@ void tst_QNetworkAccessManager::networkAccessible() + QCOMPARE(manager.networkAccessible(), initialAccessibility); + + QNetworkConfigurationManager configManager; +- bool sessionRequired = (configManager.capabilities() +- & QNetworkConfigurationManager::NetworkSessionRequired); + QNetworkConfiguration defaultConfig = configManager.defaultConfiguration(); + if (defaultConfig.isValid()) { + manager.setConfiguration(defaultConfig); + +- // the accessibility has not changed if no session is required +- if (sessionRequired) { ++ QCOMPARE(spy.count(), 0); ++ ++ if (defaultConfig.state().testFlag(QNetworkConfiguration::Active)) ++ QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::Accessible); ++ else ++ QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::NotAccessible); ++ ++ manager.setNetworkAccessible(QNetworkAccessManager::NotAccessible); ++ ++ if (defaultConfig.state().testFlag(QNetworkConfiguration::Active)) { + QCOMPARE(spy.count(), 1); +- QCOMPARE(spy.takeFirst().at(0).value(), +- QNetworkAccessManager::Accessible); ++ QCOMPARE(QNetworkAccessManager::NetworkAccessibility(spy.takeFirst().at(0).toInt()), ++ QNetworkAccessManager::NotAccessible); + } else { + QCOMPARE(spy.count(), 0); + } +- QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::Accessible); +- +- manager.setNetworkAccessible(QNetworkAccessManager::NotAccessible); +- +- QCOMPARE(spy.count(), 1); +- QCOMPARE(QNetworkAccessManager::NetworkAccessibility(spy.takeFirst().at(0).toInt()), +- QNetworkAccessManager::NotAccessible); +- QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::NotAccessible); + } ++ QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::NotAccessible); + #endif + } + +-- +1.9.1 + diff --git a/admin/qt/patches/0013-Make-UnknownAccessibility-not-block-requests.patch b/admin/qt/patches/0013-Make-UnknownAccessibility-not-block-requests.patch new file mode 100644 index 0000000000..7714254eb9 --- /dev/null +++ b/admin/qt/patches/0013-Make-UnknownAccessibility-not-block-requests.patch @@ -0,0 +1,52 @@ +From e996d68f6130847637ba287518cff1289cfa48e5 Mon Sep 17 00:00:00 2001 +From: Lorn Potter +Date: Fri, 6 Nov 2015 14:22:44 +1000 +Subject: [PATCH] Make UnknownAccessibility not block requests + +This allows requests to proceed without needing bearer plugins. + +Task-number: QTBUG-49267 +Change-Id: Ie5ce188ddefebd14d666bb5846e8f93ee2925ed1 +Reviewed-by: Markus Goetz (Woboq GmbH) +--- + src/network/access/qnetworkaccessmanager.cpp | 3 +-- + src/network/access/qnetworkaccessmanager_p.h | 2 ++ + 2 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp +index 086140f..0e5870a 100644 +--- a/src/network/access/qnetworkaccessmanager.cpp ++++ b/src/network/access/qnetworkaccessmanager.cpp +@@ -976,7 +976,6 @@ QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccess + else + return NotAccessible; + } else { +- // Network accessibility is either disabled or unknown. + if (d->defaultAccessControl) { + if (d->online) + return d->networkAccessible; +@@ -1161,7 +1160,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera + #ifndef QT_NO_BEARERMANAGEMENT + // Return a disabled network reply if network access is disabled. + // Except if the scheme is empty or file://. +- if (!d->networkAccessible && !isLocalFile) { ++ if (d->networkAccessible == NotAccessible && !isLocalFile) { + return new QDisabledNetworkReply(this, req, op); + } + +diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h +index 54ae114..3fc33b5 100644 +--- a/src/network/access/qnetworkaccessmanager_p.h ++++ b/src/network/access/qnetworkaccessmanager_p.h +@@ -93,6 +93,8 @@ public: + online = (networkConfiguration.state().testFlag(QNetworkConfiguration::Active)); + if (online) + networkAccessible = QNetworkAccessManager::Accessible; ++ else if (networkConfiguration.state().testFlag(QNetworkConfiguration::Undefined)) ++ networkAccessible = QNetworkAccessManager::UnknownAccessibility; + else + networkAccessible = QNetworkAccessManager::NotAccessible; + #endif +-- +1.9.1 + diff --git a/admin/qt/patches/README.md b/admin/qt/patches/README.md new file mode 100644 index 0000000000..c2327cf702 --- /dev/null +++ b/admin/qt/patches/README.md @@ -0,0 +1,37 @@ +## Patches used + +There are our patches on top of Qt 5.4.0, which we are currently +using for our binary packages on Windows and Mac OS. Most of them +have been sent upstream and are part of newer Qt releases. + +All changes are designed to up upstream, and all those that are +special hacks to Qt will bear a NOUPSTREAM in their name + +The git-style numeration is ordered by order of creation, their +purpose is outlined in each patches' front matter. + +### Part of Qt v5.4.1 and later +* 0001-Fix-crash-on-Mac-OS-if-PAC-URL-contains-non-URL-lega.patch +* 0002-Fix-possible-crash-when-passing-an-invalid-PAC-URL.patch +* 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 + (TODO: actual patch slighly differs) +* 0006-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 +* 0008-QNAM-Fix-reply-deadlocks-on-server-closing-connectio.patch + (TODO: actual patch has different name) + +### Upstreamed but not in any release yet (as of 2015-11-16) +* 0009-QNAM-Assign-proper-channel-before-sslErrors-emission.patch +* 0011-Make-sure-to-report-correct-NetworkAccessibility.patch +* 0012-Make-sure-networkAccessibilityChanged-is-emitted.patch +* 0013-Make-UnknownAccessibility-not-block-requests.patch + +### Not submitted to be part of any release: +0005-Fix-force-debug-info-with-macx-clang_NOUPSTREAM.patch + +