[Pkg-owncloud-commits] [owncloud-client] 428/484: Qt: Add Windows-specific patches OBS, also a QNAM fix
Sandro Knauß
hefee-guest at moszumanska.debian.org
Wed Dec 16 00:38:16 UTC 2015
This is an automated email from the git hooks/post-receive script.
hefee-guest pushed a commit to branch master
in repository owncloud-client.
commit 2d604cee256ef555c85985b86f1b3cfe0b47ddf2
Author: Daniel Molkentin <danimo at owncloud.com>
Date: Wed Nov 25 22:35:57 2015 +0100
Qt: Add Windows-specific patches OBS, also a QNAM fix
---
...-Network-Fix-up-previous-corruption-patch.patch | 434 +++++++++++++++++++++
...losed-http-sockets-pass-as-valid-connecti.patch | 121 ++++++
...it-system-proxy-if-internet-settings-chan.patch | 152 ++++++++
...not-crash-if-SSL-context-is-gone-after-ro.patch | 32 ++
admin/qt/patches/README.md | 9 +-
5 files changed, 745 insertions(+), 3 deletions(-)
diff --git a/admin/qt/patches/0007-X-Network-Fix-up-previous-corruption-patch.patch b/admin/qt/patches/0007-X-Network-Fix-up-previous-corruption-patch.patch
new file mode 100644
index 0000000..4e2fe41
--- /dev/null
+++ b/admin/qt/patches/0007-X-Network-Fix-up-previous-corruption-patch.patch
@@ -0,0 +1,434 @@
+From eae0cb09f1310e755c2aff7c1112f7a6c09d7a53 Mon Sep 17 00:00:00 2001
+From: Markus Goetz <markus at woboq.com>
+Date: Fri, 19 Jun 2015 15:35:34 +0200
+Subject: [PATCH] Network: Fix up previous corruption patch
+
+This is a fix-up for cff39fba10ffc10ee4dcfdc66ff6528eb26462d3.
+That patch lead to some internal state issues that lead to the QTBUG-47048
+or to QNetworkReply objects erroring with "Connection Closed" when
+the server closed the Keep-Alive connection.
+
+This patch changes the QNAM socket slot connections to be DirectConnection.
+We don't close the socket anymore in slots where it is anyway in a closed state
+afterwards. This prevents event/stack recursions.
+We also flush QSslSocket/QTcpSocket receive buffers when receiving a disconnect
+so that the developer always gets the full decrypted data from the buffers.
+
+[ChangeLog][QtNetwork] Fix HTTP issues with "Unknown Error" and "Connection Closed"
+[ChangeLog][QtNetwork][Sockets] Read OS/encrypted read buffers when connection
+closed by server.
+
+Change-Id: Ib4d6a2d0d988317e3a5356f36e8dbcee4590beed
+Task-number: QTBUG-47048
+Reviewed-by: Kai Koehne <kai.koehne at theqtcompany.com>
+Reviewed-by: Richard J. Moore <rich at kde.org>
+---
+ src/network/access/qhttpnetworkconnection.cpp | 1 -
+ .../access/qhttpnetworkconnectionchannel.cpp | 108 +++++++++++++--------
+ .../access/qhttpnetworkconnectionchannel_p.h | 1 +
+ src/network/access/qhttpnetworkreply.cpp | 2 +-
+ src/network/access/qhttpprotocolhandler.cpp | 1 -
+ src/network/socket/qabstractsocket.cpp | 7 +-
+ src/network/ssl/qsslsocket.cpp | 8 ++
+ src/network/ssl/qsslsocket_openssl.cpp | 7 ++
+ .../access/qnetworkreply/tst_qnetworkreply.cpp | 9 +-
+ 9 files changed, 94 insertions(+), 50 deletions(-)
+
+diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
+index 365ce55..543c70e 100644
+--- a/src/network/access/qhttpnetworkconnection.cpp
++++ b/src/network/access/qhttpnetworkconnection.cpp
+@@ -917,7 +917,6 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
+ for (int i = 0; i < channelCount; ++i) {
+ if (channels[i].resendCurrent && (channels[i].state != QHttpNetworkConnectionChannel::ClosingState)) {
+ channels[i].resendCurrent = false;
+- channels[i].state = QHttpNetworkConnectionChannel::IdleState;
+
+ // if this is not possible, error will be emitted and connection terminated
+ if (!channels[i].resetUploadData())
+diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
+index 49c6793..e2f6307 100644
+--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
++++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
+@@ -58,6 +58,11 @@ QT_BEGIN_NAMESPACE
+
+ // TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp
+
++// Because in-flight when sending a request, the server might close our connection (because the persistent HTTP
++// connection times out)
++// We use 3 because we can get a _q_error 3 times depending on the timing:
++static const int reconnectAttemptsDefault = 3;
++
+ QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
+ : socket(0)
+ , ssl(false)
+@@ -69,7 +74,7 @@ QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
+ , resendCurrent(false)
+ , lastStatus(0)
+ , pendingEncrypt(false)
+- , reconnectAttempts(2)
++ , reconnectAttempts(reconnectAttemptsDefault)
+ , authMethod(QAuthenticatorPrivate::None)
+ , proxyAuthMethod(QAuthenticatorPrivate::None)
+ , authenticationCredentialsSent(false)
+@@ -106,19 +111,18 @@ 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.
++ // After some back and forth in all the last years, this is now a DirectConnection because otherwise
++ // the state inside the *Socket classes gets messed up, also in conjunction with the socket notifiers
++ // which behave slightly differently on Windows vs Linux
+ QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
+ this, SLOT(_q_bytesWritten(qint64)),
+- Qt::QueuedConnection);
++ Qt::DirectConnection);
+ QObject::connect(socket, SIGNAL(connected()),
+ this, SLOT(_q_connected()),
+- Qt::QueuedConnection);
++ Qt::DirectConnection);
+ QObject::connect(socket, SIGNAL(readyRead()),
+ this, SLOT(_q_readyRead()),
+- Qt::QueuedConnection);
++ Qt::DirectConnection);
+
+ // The disconnected() and error() signals may already come
+ // while calling connectToHost().
+@@ -129,10 +133,10 @@ void QHttpNetworkConnectionChannel::init()
+ qRegisterMetaType<QAbstractSocket::SocketError>();
+ QObject::connect(socket, SIGNAL(disconnected()),
+ this, SLOT(_q_disconnected()),
+- Qt::QueuedConnection);
++ Qt::DirectConnection);
+ QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
+ this, SLOT(_q_error(QAbstractSocket::SocketError)),
+- Qt::QueuedConnection);
++ Qt::DirectConnection);
+
+
+ #ifndef QT_NO_NETWORKPROXY
+@@ -147,13 +151,13 @@ void QHttpNetworkConnectionChannel::init()
+ // won't be a sslSocket if encrypt is false
+ QObject::connect(sslSocket, SIGNAL(encrypted()),
+ this, SLOT(_q_encrypted()),
+- Qt::QueuedConnection);
++ Qt::DirectConnection);
+ QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
+ this, SLOT(_q_sslErrors(QList<QSslError>)),
+ Qt::DirectConnection);
+ QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)),
+ this, SLOT(_q_encryptedBytesWritten(qint64)),
+- Qt::QueuedConnection);
++ Qt::DirectConnection);
+
+ if (ignoreAllSslErrors)
+ sslSocket->ignoreSslErrors();
+@@ -397,7 +401,7 @@ void QHttpNetworkConnectionChannel::allDone()
+
+ // reset the reconnection attempts after we receive a complete reply.
+ // in case of failures, each channel will attempt two reconnects before emitting error.
+- reconnectAttempts = 2;
++ reconnectAttempts = reconnectAttemptsDefault;
+
+ // now the channel can be seen as free/idle again, all signal emissions for the reply have been done
+ if (state != QHttpNetworkConnectionChannel::ClosingState)
+@@ -651,6 +655,15 @@ void QHttpNetworkConnectionChannel::closeAndResendCurrentRequest()
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
+ }
+
++void QHttpNetworkConnectionChannel::resendCurrentRequest()
++{
++ requeueCurrentlyPipelinedRequests();
++ if (reply)
++ resendCurrent = true;
++ if (qobject_cast<QHttpNetworkConnection*>(connection))
++ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
++}
++
+ bool QHttpNetworkConnectionChannel::isSocketBusy() const
+ {
+ return (state & QHttpNetworkConnectionChannel::BusyState);
+@@ -694,8 +707,8 @@ void QHttpNetworkConnectionChannel::_q_disconnected()
+ return;
+ }
+
+- // read the available data before closing
+- if (isSocketWaiting() || isSocketReading()) {
++ // read the available data before closing (also done in _q_error for other codepaths)
++ if ((isSocketWaiting() || isSocketReading()) && socket->bytesAvailable()) {
+ if (reply) {
+ state = QHttpNetworkConnectionChannel::ReadingState;
+ _q_receiveReply();
+@@ -707,7 +720,8 @@ void QHttpNetworkConnectionChannel::_q_disconnected()
+ state = QHttpNetworkConnectionChannel::IdleState;
+
+ requeueCurrentlyPipelinedRequests();
+- close();
++
++ pendingEncrypt = false;
+ }
+
+
+@@ -789,11 +803,19 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
+ errorCode = QNetworkReply::ConnectionRefusedError;
+ break;
+ case QAbstractSocket::RemoteHostClosedError:
+- // try to reconnect/resend before sending an error.
+- // while "Reading" the _q_disconnected() will handle this.
+- if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
++ // This error for SSL comes twice in a row, first from SSL layer ("The TLS/SSL connection has been closed") then from TCP layer.
++ // Depending on timing it can also come three times in a row (first time when we try to write into a closing QSslSocket).
++ // The reconnectAttempts handling catches the cases where we can re-send the request.
++ if (!reply && state == QHttpNetworkConnectionChannel::IdleState) {
++ // Not actually an error, it is normal for Keep-Alive connections to close after some time if no request
++ // is sent on them. No need to error the other replies below. Just bail out here.
++ // The _q_disconnected will handle the possibly pipelined replies
++ return;
++ } else if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
++ // Try to reconnect/resend before sending an error.
++ // While "Reading" the _q_disconnected() will handle this.
+ if (reconnectAttempts-- > 0) {
+- closeAndResendCurrentRequest();
++ resendCurrentRequest();
+ return;
+ } else {
+ errorCode = QNetworkReply::RemoteHostClosedError;
+@@ -818,24 +840,15 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
+ // we can ignore the readbuffersize as the data is already
+ // in memory and we will not receive more data on the socket.
+ reply->setReadBufferSize(0);
++ reply->setDownstreamLimited(false);
+ _q_receiveReply();
+-#ifndef QT_NO_SSL
+- if (ssl) {
+- // QT_NO_OPENSSL. The QSslSocket can still have encrypted bytes in the plainsocket.
+- // So we need to check this if the socket is a QSslSocket. When the socket is flushed
+- // it will force a decrypt of the encrypted data in the plainsocket.
+- QSslSocket *sslSocket = static_cast<QSslSocket*>(socket);
+- qint64 beforeFlush = sslSocket->encryptedBytesAvailable();
+- while (sslSocket->encryptedBytesAvailable()) {
+- sslSocket->flush();
+- _q_receiveReply();
+- qint64 afterFlush = sslSocket->encryptedBytesAvailable();
+- if (afterFlush == beforeFlush)
+- break;
+- beforeFlush = afterFlush;
+- }
++ if (!reply) {
++ // No more reply assigned after the previous call? Then it had been finished successfully.
++ requeueCurrentlyPipelinedRequests();
++ state = QHttpNetworkConnectionChannel::IdleState;
++ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
++ return;
+ }
+-#endif
+ }
+
+ errorCode = QNetworkReply::RemoteHostClosedError;
+@@ -846,7 +859,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
+ case QAbstractSocket::SocketTimeoutError:
+ // try to reconnect/resend before sending an error.
+ if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) {
+- closeAndResendCurrentRequest();
++ resendCurrentRequest();
+ return;
+ }
+ errorCode = QNetworkReply::TimeoutError;
+@@ -860,7 +873,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
+ case QAbstractSocket::ProxyConnectionClosedError:
+ // try to reconnect/resend before sending an error.
+ if (reconnectAttempts-- > 0) {
+- closeAndResendCurrentRequest();
++ resendCurrentRequest();
+ return;
+ }
+ errorCode = QNetworkReply::ProxyConnectionClosedError;
+@@ -868,7 +881,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
+ case QAbstractSocket::ProxyConnectionTimeoutError:
+ // try to reconnect/resend before sending an error.
+ if (reconnectAttempts-- > 0) {
+- closeAndResendCurrentRequest();
++ resendCurrentRequest();
+ return;
+ }
+ errorCode = QNetworkReply::ProxyTimeoutError;
+@@ -916,8 +929,18 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
+ // send the next request
+ QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
+
+- if (that) //signal emission triggered event loop
+- close();
++ if (that) {
++ //signal emission triggered event loop
++ if (!socket)
++ state = QHttpNetworkConnectionChannel::IdleState;
++ else if (socket->state() == QAbstractSocket::UnconnectedState)
++ state = QHttpNetworkConnectionChannel::IdleState;
++ else
++ state = QHttpNetworkConnectionChannel::ClosingState;
++
++ // pendingEncrypt must only be true in between connected and encrypted states
++ pendingEncrypt = false;
++ }
+ }
+
+ #ifndef QT_NO_NETWORKPROXY
+@@ -941,7 +964,8 @@ void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetwor
+
+ void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead()
+ {
+- sendRequest();
++ if (reply)
++ sendRequest();
+ }
+
+ #ifndef QT_NO_SSL
+diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
+index 231fe11..a834b7d 100644
+--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
++++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
+@@ -169,6 +169,7 @@ public:
+
+ void handleUnexpectedEOF();
+ void closeAndResendCurrentRequest();
++ void resendCurrentRequest();
+
+ bool isSocketBusy() const;
+ bool isSocketWriting() const;
+diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
+index 55863a3..8b71bd8 100644
+--- a/src/network/access/qhttpnetworkreply.cpp
++++ b/src/network/access/qhttpnetworkreply.cpp
+@@ -191,7 +191,7 @@ QByteArray QHttpNetworkReply::readAny()
+ return QByteArray();
+
+ // we'll take the last buffer, so schedule another read from http
+- if (d->downstreamLimited && d->responseData.bufferCount() == 1)
++ if (d->downstreamLimited && d->responseData.bufferCount() == 1 && !isFinished())
+ d->connection->d_func()->readMoreLater(this);
+ return d->responseData.read();
+ }
+diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp
+index 3357948..380aaac 100644
+--- a/src/network/access/qhttpprotocolhandler.cpp
++++ b/src/network/access/qhttpprotocolhandler.cpp
+@@ -250,7 +250,6 @@ bool QHttpProtocolHandler::sendRequest()
+ if (!m_reply) {
+ // heh, how should that happen!
+ qWarning() << "QAbstractProtocolHandler::sendRequest() called without QHttpNetworkReply";
+- m_channel->state = QHttpNetworkConnectionChannel::IdleState;
+ return false;
+ }
+
+diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
+index 2666771..0e82d4a 100644
+--- a/src/network/socket/qabstractsocket.cpp
++++ b/src/network/socket/qabstractsocket.cpp
+@@ -768,6 +768,7 @@ bool QAbstractSocketPrivate::canReadNotification()
+ void QAbstractSocketPrivate::canCloseNotification()
+ {
+ Q_Q(QAbstractSocket);
++ // Note that this method is only called on Windows. Other platforms close in the canReadNotification()
+
+ #if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::canCloseNotification()");
+@@ -777,7 +778,11 @@ void QAbstractSocketPrivate::canCloseNotification()
+ if (isBuffered) {
+ // Try to read to the buffer, if the read fail we can close the socket.
+ newBytes = buffer.size();
+- if (!readFromSocket()) {
++ qint64 oldReadBufferMaxSize = readBufferMaxSize;
++ readBufferMaxSize = 0; // temporarily disable max read buffer, we want to empty the OS buffer
++ bool hadReadFromSocket = readFromSocket();
++ readBufferMaxSize = oldReadBufferMaxSize;
++ if (!hadReadFromSocket) {
+ q->disconnectFromHost();
+ return;
+ }
+diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
+index c1fab94..2b9e923 100644
+--- a/src/network/ssl/qsslsocket.cpp
++++ b/src/network/ssl/qsslsocket.cpp
+@@ -2294,6 +2294,14 @@ void QSslSocketPrivate::_q_errorSlot(QAbstractSocket::SocketError error)
+ qCDebug(lcSsl) << "\tstate =" << q->state();
+ qCDebug(lcSsl) << "\terrorString =" << q->errorString();
+ #endif
++ // this moves encrypted bytes from plain socket into our buffer
++ if (plainSocket->bytesAvailable()) {
++ qint64 tmpReadBufferMaxSize = readBufferMaxSize;
++ readBufferMaxSize = 0; // reset temporarily so the plain sockets completely drained drained
++ transmit();
++ readBufferMaxSize = tmpReadBufferMaxSize;
++ }
++
+ q->setSocketError(plainSocket->error());
+ q->setErrorString(plainSocket->errorString());
+ emit q->error(error);
+diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
+index ac4336a..94655fe 100644
+--- a/src/network/ssl/qsslsocket_openssl.cpp
++++ b/src/network/ssl/qsslsocket_openssl.cpp
+@@ -1419,6 +1419,13 @@ void QSslSocketBackendPrivate::disconnected()
+ {
+ if (plainSocket->bytesAvailable() <= 0)
+ destroySslContext();
++ else {
++ // Move all bytes into the plain buffer
++ qint64 tmpReadBufferMaxSize = readBufferMaxSize;
++ readBufferMaxSize = 0; // reset temporarily so the plain socket buffer is completely drained
++ transmit();
++ readBufferMaxSize = tmpReadBufferMaxSize;
++ }
+ //if there is still buffered data in the plain socket, don't destroy the ssl context yet.
+ //it will be destroyed when the socket is deleted.
+ }
+diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
+index d2edf67..138f528 100644
+--- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
++++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
+@@ -1051,7 +1051,7 @@ protected:
+ // clean up QAbstractSocket's residue:
+ while (client->bytesToWrite() > 0) {
+ qDebug() << "Still having" << client->bytesToWrite() << "bytes to write, doing that now";
+- if (!client->waitForBytesWritten(2000)) {
++ if (!client->waitForBytesWritten(10000)) {
+ qDebug() << "ERROR: FastSender:" << client->error() << "cleaning up residue";
+ return;
+ }
+@@ -1071,7 +1071,7 @@ protected:
+ measuredSentBytes += writeNextData(client, bytesToWrite);
+
+ while (client->bytesToWrite() > 0) {
+- if (!client->waitForBytesWritten(2000)) {
++ if (!client->waitForBytesWritten(10000)) {
+ qDebug() << "ERROR: FastSender:" << client->error() << "during blocking write";
+ return;
+ }
+@@ -7946,7 +7946,7 @@ public slots:
+ 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
++ QTimer::singleShot(qrand()%60, 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.
+@@ -8052,11 +8052,12 @@ void tst_QNetworkReply::putWithServerClosingConnectionImmediately()
+
+ // get the request started and the incoming socket connected
+ QTestEventLoop::instance().enterLoop(10);
++ QVERIFY(!QTestEventLoop::instance().timeout());
+
+ //qDebug() << "correct=" << server.m_correctUploads << "corrupt=" << server.m_corruptUploads << "expected=" <<numUploads;
+
+ // Sanity check because ecause of 9c2ecf89 most replies will error out but we want to make sure at least some of them worked
+- QVERIFY(server.m_correctUploads > 5);
++ QVERIFY(server.m_correctUploads > 2);
+ // Because actually important is that we don't get any corruption:
+ QCOMPARE(server.m_corruptUploads, 0);
+
+--
+1.9.1
diff --git a/admin/qt/patches/0010-Don-t-let-closed-http-sockets-pass-as-valid-connecti.patch b/admin/qt/patches/0010-Don-t-let-closed-http-sockets-pass-as-valid-connecti.patch
new file mode 100644
index 0000000..c883e3d
--- /dev/null
+++ b/admin/qt/patches/0010-Don-t-let-closed-http-sockets-pass-as-valid-connecti.patch
@@ -0,0 +1,121 @@
+From 0df5d079290b4c3b13e58e9397fabdc1dfdba96b Mon Sep 17 00:00:00 2001
+From: Ulf Hermann <ulf.hermann at theqtcompany.com>
+Date: Fri, 25 Sep 2015 13:23:46 +0200
+Subject: [PATCH] Don't let closed http sockets pass as valid connections
+
+A QAbstractSocket can be close()'d at any time, independently of its
+current connection state. being closed means that we cannot use it to
+read or write data, but internally it might still have some data to
+send or receive, for example to an http server. We can even get a
+connected() signal after close()'ing the socket.
+
+We need to catch this condition and mark any pending data not yet
+written to the socket for resending.
+
+Task-number: QTBUG-48326
+Change-Id: I6f61c35f2c567f2a138f8cfe9ade7fd1ec039be6
+Reviewed-by: Simon Hausmann <simon.hausmann at theqtcompany.com>
+---
+ .../access/qhttpnetworkconnectionchannel.cpp | 7 ++-
+ .../tst_qhttpnetworkconnection.cpp | 54 ++++++++++++++++++++++
+ 2 files changed, 60 insertions(+), 1 deletion(-)
+
+diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
+index 293909c..b4eda34 100644
+--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
++++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
+@@ -272,7 +272,12 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
+ QAbstractSocket::SocketState socketState = socket->state();
+
+ // resend this request after we receive the disconnected signal
+- if (socketState == QAbstractSocket::ClosingState) {
++ // If !socket->isOpen() then we have already called close() on the socket, but there was still a
++ // pending connectToHost() for which we hadn't seen a connected() signal, yet. The connected()
++ // has now arrived (as indicated by socketState != ClosingState), but we cannot send anything on
++ // such a socket anymore.
++ if (socketState == QAbstractSocket::ClosingState ||
++ (socketState != QAbstractSocket::UnconnectedState && !socket->isOpen())) {
+ if (reply)
+ resendCurrent = true;
+ return false;
+diff --git a/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp b/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp
+index 5d072af..0d188a8 100644
+--- a/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp
++++ b/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp
+@@ -36,6 +36,7 @@
+ #include "private/qhttpnetworkconnection_p.h"
+ #include "private/qnoncontiguousbytedevice_p.h"
+ #include <QAuthenticator>
++#include <QTcpServer>
+
+ #include "../../../network-settings.h"
+
+@@ -106,6 +107,8 @@ private Q_SLOTS:
+
+ void getAndThenDeleteObject();
+ void getAndThenDeleteObject_data();
++
++ void overlappingCloseAndWrite();
+ };
+
+ tst_QHttpNetworkConnection::tst_QHttpNetworkConnection()
+@@ -1112,6 +1115,57 @@ void tst_QHttpNetworkConnection::getAndThenDeleteObject()
+ }
+ }
+
++class TestTcpServer : public QTcpServer
++{
++ Q_OBJECT
++public:
++ TestTcpServer() : errorCodeReports(0)
++ {
++ connect(this, &QTcpServer::newConnection, this, &TestTcpServer::onNewConnection);
++ QVERIFY(listen(QHostAddress::LocalHost));
++ }
++
++ int errorCodeReports;
++
++public slots:
++ void onNewConnection()
++ {
++ QTcpSocket *socket = nextPendingConnection();
++ if (!socket)
++ return;
++ // close socket instantly!
++ connect(socket, &QTcpSocket::readyRead, socket, &QTcpSocket::close);
++ }
++
++ void onReply(QNetworkReply::NetworkError code)
++ {
++ QCOMPARE(code, QNetworkReply::RemoteHostClosedError);
++ ++errorCodeReports;
++ }
++};
++
++void tst_QHttpNetworkConnection::overlappingCloseAndWrite()
++{
++ // server accepts connections, but closes the socket instantly
++ TestTcpServer server;
++ QNetworkAccessManager accessManager;
++
++ // ten requests are scheduled. All should result in an RemoteHostClosed...
++ QUrl url;
++ url.setScheme(QStringLiteral("http"));
++ url.setHost(server.serverAddress().toString());
++ url.setPort(server.serverPort());
++ for (int i = 0; i < 10; ++i) {
++ QNetworkRequest request(url);
++ QNetworkReply *reply = accessManager.get(request);
++ // Not using Qt5 connection syntax here because of overly baroque syntax to discern between
++ // different error() methods.
++ QObject::connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
++ &server, SLOT(onReply(QNetworkReply::NetworkError)));
++ }
++
++ QTRY_COMPARE(server.errorCodeReports, 10);
++}
+
+
+ QTEST_MAIN(tst_QHttpNetworkConnection)
+--
+1.9.1
diff --git a/admin/qt/patches/0017-Win32-Re-init-system-proxy-if-internet-settings-chan.patch b/admin/qt/patches/0017-Win32-Re-init-system-proxy-if-internet-settings-chan.patch
new file mode 100644
index 0000000..814c4d1
--- /dev/null
+++ b/admin/qt/patches/0017-Win32-Re-init-system-proxy-if-internet-settings-chan.patch
@@ -0,0 +1,152 @@
+From ae9d3f4c6c1a732788cd1f24c6a928cee16c3991 Mon Sep 17 00:00:00 2001
+From: Daniel Molkentin <daniel at molkentin.de>
+Date: Tue, 27 Jan 2015 16:58:32 +0100
+Subject: [PATCH] Win32: Re-init system proxy if internet settings change
+
+Because Proxy Auto Configuration performs DNS lookups,
+the proxy settings are being cached. For long-running
+programs this means that once users switch e.g. from or
+to company networks with a proxy, they instantly will
+lose connectivity because we cache the old setting.
+
+To remedy this, we monitor the Registry (locations
+courtesy of Chromium's platform support) for changes
+in its settings, and requery for the current proxy in
+that case.
+
+Task-number: QTBUG-3470
+Task-number: QTBUG-29990
+Change-Id: Id25a51387bcd232c5f879cea0371038986d0e2de
+Reviewed-by: Oliver Wolff <oliver.wolff at theqtcompany.com>
+---
+ src/network/kernel/qnetworkproxy_win.cpp | 86 +++++++++++++++++++++++++++++++-
+ 1 file changed, 84 insertions(+), 2 deletions(-)
+
+diff --git a/src/network/kernel/qnetworkproxy_win.cpp b/src/network/kernel/qnetworkproxy_win.cpp
+index da2c020..f7741ce 100644
+--- a/src/network/kernel/qnetworkproxy_win.cpp
++++ b/src/network/kernel/qnetworkproxy_win.cpp
+@@ -345,12 +345,66 @@ static QList<QNetworkProxy> parseServerList(const QNetworkProxyQuery &query, con
+ return removeDuplicateProxies(result);
+ }
+
++#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
++namespace {
++class QRegistryWatcher {
++public:
++ void addLocation(HKEY hive, const QString& path)
++ {
++ HKEY openedKey;
++ if (RegOpenKeyEx(hive, reinterpret_cast<const wchar_t*>(path.utf16()), 0, KEY_READ, &openedKey) != ERROR_SUCCESS)
++ return;
++
++ const DWORD filter = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES |
++ REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY;
++
++ // Watch the registry key for a change of value.
++ HANDLE handle = CreateEvent(NULL, true, false, NULL);
++ if (RegNotifyChangeKeyValue(openedKey, true, filter, handle, true) != ERROR_SUCCESS) {
++ CloseHandle(handle);
++ return;
++ }
++ m_watchEvents.append(handle);
++ m_registryHandles.append(openedKey);
++ }
++
++ bool hasChanged() const {
++ return !isEmpty() &&
++ WaitForMultipleObjects(m_watchEvents.size(), m_watchEvents.data(), false, 0) < WAIT_OBJECT_0 + m_watchEvents.size();
++ }
++
++ bool isEmpty() const {
++ return m_watchEvents.isEmpty();
++ }
++
++ void clear() {
++ foreach (HANDLE event, m_watchEvents)
++ CloseHandle(event);
++ foreach (HKEY key, m_registryHandles)
++ RegCloseKey(key);
++
++ m_watchEvents.clear();
++ m_registryHandles.clear();
++ }
++
++ ~QRegistryWatcher() {
++ clear();
++ }
++
++private:
++ QVector<HANDLE> m_watchEvents;
++ QVector<HKEY> m_registryHandles;
++};
++} // namespace
++#endif // !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
++
+ class QWindowsSystemProxy
+ {
+ public:
+ QWindowsSystemProxy();
+ ~QWindowsSystemProxy();
+ void init();
++ void reset();
+
+ QMutex mutex;
+
+@@ -361,7 +415,9 @@ public:
+ QStringList proxyServerList;
+ QStringList proxyBypass;
+ QList<QNetworkProxy> defaultResult;
+-
++#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
++ QRegistryWatcher proxySettingsWatcher;
++#endif
+ bool initialized;
+ bool functional;
+ bool isAutoConfig;
+@@ -381,16 +437,42 @@ QWindowsSystemProxy::~QWindowsSystemProxy()
+ ptrWinHttpCloseHandle(hHttpSession);
+ }
+
++void QWindowsSystemProxy::reset()
++{
++ autoConfigUrl.clear();
++ proxyServerList.clear();
++ proxyBypass.clear();
++ defaultResult.clear();
++ defaultResult << QNetworkProxy::NoProxy;
++ functional = false;
++ isAutoConfig = false;
++}
++
+ void QWindowsSystemProxy::init()
+ {
+- if (initialized)
++ bool proxySettingsChanged = false;
++#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
++ proxySettingsChanged = proxySettingsWatcher.hasChanged();
++#endif
++
++ if (initialized && !proxySettingsChanged)
+ return;
+ initialized = true;
+
++ reset();
++
+ #ifdef Q_OS_WINCE
+ // Windows CE does not have any of the following API
+ return;
+ #else
++
++#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
++ proxySettingsWatcher.clear(); // needs reset to trigger a new detection
++ proxySettingsWatcher.addLocation(HKEY_CURRENT_USER, QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
++ proxySettingsWatcher.addLocation(HKEY_LOCAL_MACHINE, QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
++ proxySettingsWatcher.addLocation(HKEY_LOCAL_MACHINE, QStringLiteral("Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
++#endif
++
+ // load the winhttp.dll library
+ QSystemLibrary lib(L"winhttp");
+ if (!lib.load())
+--
+1.9.1
diff --git a/admin/qt/patches/0018-Windows-Do-not-crash-if-SSL-context-is-gone-after-ro.patch b/admin/qt/patches/0018-Windows-Do-not-crash-if-SSL-context-is-gone-after-ro.patch
new file mode 100644
index 0000000..6669179
--- /dev/null
+++ b/admin/qt/patches/0018-Windows-Do-not-crash-if-SSL-context-is-gone-after-ro.patch
@@ -0,0 +1,32 @@
+From c1a67e7dc3a6f8876efa32cdbabbfde1c5a37bc6 Mon Sep 17 00:00:00 2001
+From: Daniel Molkentin <daniel at molkentin.de>
+Date: Tue, 31 Mar 2015 17:43:44 +0200
+Subject: [PATCH] Windows: Do not crash if SSL context is gone after root cert
+ lookup
+
+On Windows, we perform an extra certificate lookup for root CAs that
+are not in Windows' (minimal) root store. This check can take up to
+15 seconds. The SSL context can already be gone once we return. Hence
+we now check for a non-null SSL context on Windows before proceeding.
+
+Change-Id: I1951569d9b17da33fa604f7c9d8b33255acf200d
+Reviewed-by: Richard J. Moore <rich at kde.org>
+---
+ src/network/ssl/qsslsocket_openssl.cpp | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
+index 0e1a3e5..b132aec 100644
+--- a/src/network/ssl/qsslsocket_openssl.cpp
++++ b/src/network/ssl/qsslsocket_openssl.cpp
+@@ -1281,7 +1281,7 @@ void QSslSocketBackendPrivate::_q_caRootLoaded(QSslCertificate cert, QSslCertifi
+ if (plainSocket)
+ plainSocket->resume();
+ paused = false;
+- if (checkSslErrors())
++ if (checkSslErrors() && ssl)
+ continueHandshake();
+ }
+
+--
+1.9.1
diff --git a/admin/qt/patches/README.md b/admin/qt/patches/README.md
index 466a6c7..5705069 100644
--- a/admin/qt/patches/README.md
+++ b/admin/qt/patches/README.md
@@ -18,17 +18,20 @@ purpose is outlined in each patches' front matter.
### Part of Qt v5.4.2 and later
* 0004-Cocoa-Fix-systray-SVG-icons.patch
* 0005-OSX-Fix-disapearing-tray-icon.patch
- (TODO: actual patch slighly differs)
* 0007-QNAM-Fix-upload-corruptions-when-server-closes-conne.patch
- (TODO: Actual patch on build machine spans over two commit but is identical)
+* 0018-Windows-Do-not-crash-if-SSL-context-is-gone-after-ro.patch
+
+### Part of Qt v5.5.0 and later
+* 0017-Win32-Re-init-system-proxy-if-internet-settings-chan.patch
### Part of Qt v5.5.1 and later
+* 0007-X-Network-Fix-up-previous-corruption-patch.patch
* 0008-QNAM-Fix-reply-deadlocks-on-server-closing-connectio.patch
- (TODO: actual patch has different name)
* 0014-Fix-SNI-for-TlsV1_0OrLater-TlsV1_1OrLater-and-TlsV1_.patch
### Upstreamed but not in any release yet (as of 2015-11-16)
* 0009-QNAM-Assign-proper-channel-before-sslErrors-emission.patch
+* 0010-Don-t-let-closed-http-sockets-pass-as-valid-connecti.patch
* 0011-Make-sure-to-report-correct-NetworkAccessibility.patch
* 0012-Make-sure-networkAccessibilityChanged-is-emitted.patch
* 0013-Make-UnknownAccessibility-not-block-requests.patch
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-owncloud/owncloud-client.git
More information about the Pkg-owncloud-commits
mailing list