From 07fd031d29198cc5a0d6f1da6bb8fea29274fa06 Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Tue, 19 Oct 2010 16:16:38 +0200 Subject: QNAM HTTP: Pause sockets while emitting to user code. This is needed because user code might display a dialog which spins an event loop and could make the sockets readyRead() fire. This event loop recursion is not desired as it can lead to nasty bugs when the state is messed up. Reviewed-by: Peter Hartmann Reviewed-by: Prasanth Task-Number: QTBUG-13234 --- src/network/access/qhttpnetworkconnection.cpp | 82 +++++++++++++++------------ src/network/access/qhttpnetworkconnection_p.h | 15 +++-- 2 files changed, 56 insertions(+), 41 deletions(-) diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 32881b2..f8f7620 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -56,6 +57,7 @@ #ifndef QT_NO_HTTP #ifndef QT_NO_OPENSSL +# include # include # include # include @@ -79,9 +81,9 @@ const int QHttpNetworkConnectionPrivate::defaultRePipelineLength = 2; QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt) -: hostName(hostName), port(port), encrypt(encrypt), - channelCount(defaultChannelCount), - pendingAuthSignal(false), pendingProxyAuthSignal(false) +: state(RunningState), + hostName(hostName), port(port), encrypt(encrypt), + channelCount(defaultChannelCount) #ifndef QT_NO_NETWORKPROXY , networkProxy(QNetworkProxy::NoProxy) #endif @@ -90,9 +92,9 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &host } QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt) -: hostName(hostName), port(port), encrypt(encrypt), - channelCount(channelCount), - pendingAuthSignal(false), pendingProxyAuthSignal(false) +: state(RunningState), + hostName(hostName), port(port), encrypt(encrypt), + channelCount(channelCount) #ifndef QT_NO_NETWORKPROXY , networkProxy(QNetworkProxy::NoProxy) #endif @@ -121,6 +123,37 @@ void QHttpNetworkConnectionPrivate::init() } } +void QHttpNetworkConnectionPrivate::pauseConnection() +{ + state = PausedState; + + // Disable all socket notifiers + for (int i = 0; i < channelCount; i++) { + if (encrypt) + QSslSocketPrivate::pauseSocketNotifiers(static_cast(channels[i].socket)); + else + QAbstractSocketPrivate::pauseSocketNotifiers(channels[i].socket); + } +} + +void QHttpNetworkConnectionPrivate::resumeConnection() +{ + state = RunningState; + // Enable all socket notifiers + for (int i = 0; i < channelCount; i++) { + if (encrypt) + QSslSocketPrivate::resumeSocketNotifiers(static_cast(channels[i].socket)); + else + QAbstractSocketPrivate::resumeSocketNotifiers(channels[i].socket); + } + + // Resume uploads + // FIXME + + // queue _q_startNextRequest + QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection); +} + int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const { for (int i = 0; i < channelCount; ++i) @@ -315,35 +348,19 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket priv->parseHttpResponse(fields, isProxy); if (priv->phase == QAuthenticatorPrivate::Done) { - if ((isProxy && pendingProxyAuthSignal) ||(!isProxy && pendingAuthSignal)) { - // drop the request - reply->d_func()->eraseData(); - channels[i].close(); - channels[i].lastStatus = 0; - channels[i].state = QHttpNetworkConnectionChannel::Wait4AuthState; - return false; - } - // cannot use this socket until the slot returns - channels[i].state = QHttpNetworkConnectionChannel::WaitingState; - socket->blockSignals(true); + pauseConnection(); if (!isProxy) { - pendingAuthSignal = true; emit q->authenticationRequired(reply->request(), auth, q); - pendingAuthSignal = false; #ifndef QT_NO_NETWORKPROXY } else { - pendingProxyAuthSignal = true; emit q->proxyAuthenticationRequired(networkProxy, auth, q); - pendingProxyAuthSignal = false; #endif } - socket->blockSignals(false); - // socket free to use - channels[i].state = QHttpNetworkConnectionChannel::IdleState; + resumeConnection(); + if (priv->phase != QAuthenticatorPrivate::Done) { // send any pending requests copyCredentials(i, auth, isProxy); - QMetaObject::invokeMethod(q, "_q_restartAuthPendingRequests", Qt::QueuedConnection); } } // - Changing values in QAuthenticator will reset the 'phase'. Therefore if it is still "Done" @@ -729,6 +746,10 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply) // although it is called _q_startNextRequest, it will actually start multiple requests when possible void QHttpNetworkConnectionPrivate::_q_startNextRequest() { + // If the QHttpNetworkConnection is currently paused then bail out immediatly + if (state == PausedState) + return; + //resend the necessary ones. for (int i = 0; i < channelCount; ++i) { if (channels[i].resendCurrent) { @@ -779,17 +800,6 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest() fillPipeline(channels[i].socket); } -void QHttpNetworkConnectionPrivate::_q_restartAuthPendingRequests() -{ - // send the request using the idle socket - for (int i = 0 ; i < channelCount; ++i) { - if (channels[i].state == QHttpNetworkConnectionChannel::Wait4AuthState) { - channels[i].state = QHttpNetworkConnectionChannel::IdleState; - if (channels[i].reply) - channels[i].sendRequest(); - } - } -} void QHttpNetworkConnectionPrivate::readMoreLater(QHttpNetworkReply *reply) { diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index 51666d6..f2e0b1c 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -141,10 +141,10 @@ private: Q_DECLARE_PRIVATE(QHttpNetworkConnection) Q_DISABLE_COPY(QHttpNetworkConnection) friend class QHttpNetworkReply; + friend class QHttpNetworkReplyPrivate; friend class QHttpNetworkConnectionChannel; Q_PRIVATE_SLOT(d_func(), void _q_startNextRequest()) - Q_PRIVATE_SLOT(d_func(), void _q_restartAuthPendingRequests()) }; @@ -160,11 +160,20 @@ public: static const int defaultPipelineLength; static const int defaultRePipelineLength; + enum ConnectionState { + RunningState = 0, + PausedState = 1, + }; + QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt); QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt); ~QHttpNetworkConnectionPrivate(); void init(); + void pauseConnection(); + void resumeConnection(); + ConnectionState state; + enum { ChunkSize = 4096 }; int indexOf(QAbstractSocket *socket) const; @@ -184,7 +193,6 @@ public: // private slots void _q_startNextRequest(); // send the next request from the queue - void _q_restartAuthPendingRequests(); // send the currently blocked request void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request); @@ -203,9 +211,6 @@ public: const int channelCount; QHttpNetworkConnectionChannel *channels; // parallel connections to the server - bool pendingAuthSignal; // there is an incomplete authentication signal - bool pendingProxyAuthSignal; // there is an incomplete proxy authentication signal - qint64 uncompressedBytesAvailable(const QHttpNetworkReply &reply) const; qint64 uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const; -- cgit v0.12