summaryrefslogtreecommitdiffstats
path: root/src/network/access
diff options
context:
space:
mode:
authorLiang Qi <liang.qi@theqtcompany.com>2015-08-26 20:06:57 +0200
committerLiang Qi <liang.qi@theqtcompany.com>2015-08-26 20:06:57 +0200
commitafab1546a7665bac2a8d7a6452e6aea46bfd2127 (patch)
treed616a7559c54a40e35a12b464f606774c9b0475b /src/network/access
parent06b457c693f207e392d3021d77a0ab18cd78da92 (diff)
parent53ecaade10319ecc1d8115521ae6d8eba1ee55c1 (diff)
Merge remote-tracking branch 'origin/5.5' into 5.6
Conflicts: qmake/doc/snippets/code/doc_src_qmake-manual.pro qmake/doc/src/qmake-manual.qdoc src/corelib/io/qstorageinfo_unix.cpp src/corelib/tools/qbytearray.cpp src/widgets/kernel/qwidgetwindow.cpp tests/auto/corelib/io/qprocess/tst_qprocess.cpp tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp tests/auto/network/access/qnetworkreply/BLACKLIST Change-Id: I9efcd7e1cce1c394eed425c43aa6fce7d2edf31c
Diffstat (limited to 'src/network/access')
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp1
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp112
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel_p.h1
-rw-r--r--src/network/access/qhttpnetworkreply.cpp2
-rw-r--r--src/network/access/qhttpprotocolhandler.cpp8
5 files changed, 78 insertions, 46 deletions
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index 16ed67fbc0..c2d986ef3d 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -971,7 +971,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 98247b7df0..8980ed7a41 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,7 +151,7 @@ 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);
@@ -156,7 +160,7 @@ void QHttpNetworkConnectionChannel::init()
Qt::DirectConnection);
QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)),
this, SLOT(_q_encryptedBytesWritten(qint64)),
- Qt::QueuedConnection);
+ Qt::DirectConnection);
if (ignoreAllSslErrors)
sslSocket->ignoreSslErrors();
@@ -400,7 +404,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)
@@ -668,6 +672,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);
@@ -711,8 +724,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();
@@ -724,7 +737,8 @@ void QHttpNetworkConnectionChannel::_q_disconnected()
state = QHttpNetworkConnectionChannel::IdleState;
requeueCurrentlyPipelinedRequests();
- close();
+
+ pendingEncrypt = false;
}
@@ -806,11 +820,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;
@@ -821,11 +843,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
@@ -835,24 +861,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;
@@ -863,7 +880,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;
@@ -877,7 +894,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;
@@ -885,7 +902,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;
@@ -933,8 +950,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
@@ -958,7 +985,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 16d6c3b40f..37ad6c9b0a 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 172d4dab5b..dd108ad5c7 100644
--- a/src/network/access/qhttpnetworkreply.cpp
+++ b/src/network/access/qhttpnetworkreply.cpp
@@ -208,7 +208,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 55187755bf..a2083158f1 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();
}
@@ -250,7 +255,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;
}