diff options
Diffstat (limited to 'src/network/access/qhttpnetworkconnection.cpp')
-rw-r--r-- | src/network/access/qhttpnetworkconnection.cpp | 157 |
1 files changed, 77 insertions, 80 deletions
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 2ab28c7..1ef58d8 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -39,6 +39,7 @@ ** ****************************************************************************/ +#include <private/qabstractsocket_p.h> #include "qhttpnetworkconnection_p.h" #include "qhttpnetworkconnectionchannel_p.h" #include "private/qnoncontiguousbytedevice_p.h" @@ -56,6 +57,7 @@ #ifndef QT_NO_HTTP #ifndef QT_NO_OPENSSL +# include <private/qsslsocket_p.h> # include <QtNetwork/qsslkey.h> # include <QtNetwork/qsslcipher.h> # include <QtNetwork/qsslconfiguration.h> @@ -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 @@ -122,6 +124,42 @@ void QHttpNetworkConnectionPrivate::init() } } +void QHttpNetworkConnectionPrivate::pauseConnection() +{ + state = PausedState; + + // Disable all socket notifiers + for (int i = 0; i < channelCount; i++) { +#ifndef QT_NO_OPENSSL + if (encrypt) + QSslSocketPrivate::pauseSocketNotifiers(static_cast<QSslSocket*>(channels[i].socket)); + else +#endif + QAbstractSocketPrivate::pauseSocketNotifiers(channels[i].socket); + } +} + +void QHttpNetworkConnectionPrivate::resumeConnection() +{ + state = RunningState; + // Enable all socket notifiers + for (int i = 0; i < channelCount; i++) { +#ifndef QT_NO_OPENSSL + if (encrypt) + QSslSocketPrivate::resumeSocketNotifiers(static_cast<QSslSocket*>(channels[i].socket)); + else +#endif + QAbstractSocketPrivate::resumeSocketNotifiers(channels[i].socket); + + // Resume pending upload if needed + if (channels[i].state == QHttpNetworkConnectionChannel::WritingState) + QMetaObject::invokeMethod(&channels[i], "_q_uploadDataReadyRead", Qt::QueuedConnection); + } + + // 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) @@ -258,6 +296,13 @@ void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthentica { Q_ASSERT(auth); + // NTLM is a multi phase authentication. Copying credentials between authenticators would mess things up. + if (!isProxy && channels[fromChannel].authMethod == QAuthenticatorPrivate::Ntlm) + return; + if (isProxy && channels[fromChannel].proxyAuthMethod == QAuthenticatorPrivate::Ntlm) + return; + + // select another channel QAuthenticator* otherAuth = 0; for (int i = 0; i < channelCount; ++i) { @@ -297,10 +342,10 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket QAuthenticator* auth = 0; if (isProxy) { auth = &channels[i].proxyAuthenticator; - channels[i].proxyAuthMehtod = authMethod; + channels[i].proxyAuthMethod = authMethod; } else { auth = &channels[i].authenticator; - channels[i].authMehtod = authMethod; + channels[i].authMethod = authMethod; } //proceed with the authentication. if (auth->isNull()) @@ -309,43 +354,23 @@ 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; + emit reply->authenticationRequired(reply->request(), auth); #ifndef QT_NO_NETWORKPROXY } else { - pendingProxyAuthSignal = true; - emit q->proxyAuthenticationRequired(networkProxy, auth, q); - pendingProxyAuthSignal = false; + emit reply->proxyAuthenticationRequired(networkProxy, auth); #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); } - } else if (priv->phase == QAuthenticatorPrivate::Start) { - // If the url's authenticator has a 'user' set we will end up here (phase is only set to 'Done' by - // parseHttpResponse above if 'user' is empty). So if credentials were supplied with the request, - // such as in the case of an XMLHttpRequest, this is our only opportunity to cache them. - emit q->cacheCredentials(reply->request(), auth, q); } - // - Changing values in QAuthenticator will reset the 'phase'. + // - Changing values in QAuthenticator will reset the 'phase'. Therefore if it is still "Done" + // then nothing was filled in by the user or the cache // - If withCredentials has been set to false (e.g. by QtWebKit for a cross-origin XMLHttpRequest) then // we need to bail out if authentication is required. if (priv->phase == QAuthenticatorPrivate::Done || !reply->request().withCredentials()) { @@ -357,15 +382,9 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket ? QNetworkReply::ProxyAuthenticationRequiredError : QNetworkReply::AuthenticationRequiredError; reply->d_func()->errorString = errorDetail(errorCode, socket); - emit q->error(errorCode, reply->d_func()->errorString); - emit channels[i].reply->finished(); + emit reply->finishedWithError(errorCode, reply->d_func()->errorString); // ### at this point the reply could be deleted socket->close(); - // remove pending request on the other channels - for (int j = 0; j < channelCount; ++j) { - if (j != i && channels[j].state == QHttpNetworkConnectionChannel::Wait4AuthState) - channels[j].state = QHttpNetworkConnectionChannel::IdleState; - } return true; } //resend the request @@ -381,8 +400,9 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, int i = indexOf(socket); - if (channels[i].authMehtod != QAuthenticatorPrivate::None) { - if (!(channels[i].authMehtod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 401)) { + // Send "Authorization" header, but not if it's NTLM and the socket is already authenticated. + if (channels[i].authMethod != QAuthenticatorPrivate::None) { + if (!(channels[i].authMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 401)) { QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator); if (priv && priv->method != QAuthenticatorPrivate::None) { QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false)); @@ -390,8 +410,10 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, } } } - if (channels[i].proxyAuthMehtod != QAuthenticatorPrivate::None) { - if (!(channels[i].proxyAuthMehtod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) { + + // Send "Proxy-Authorization" header, but not if it's NTLM and the socket is already authenticated. + if (channels[i].proxyAuthMethod != QAuthenticatorPrivate::None) { + if (!(channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) { QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator); if (priv && priv->method != QAuthenticatorPrivate::None) { QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false)); @@ -726,6 +748,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 immediately + if (state == PausedState) + return; + //resend the necessary ones. for (int i = 0; i < channelCount; ++i) { if (channels[i].resendCurrent) { @@ -776,17 +802,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) { @@ -835,33 +850,12 @@ QHttpNetworkReply* QHttpNetworkConnection::sendRequest(const QHttpNetworkRequest return d->queueRequest(request); } -void QHttpNetworkConnection::enableEncryption() -{ - Q_D(QHttpNetworkConnection); - d->encrypt = true; -} - bool QHttpNetworkConnection::isEncrypted() const { Q_D(const QHttpNetworkConnection); return d->encrypt; } -void QHttpNetworkConnection::setProxyAuthentication(QAuthenticator *authenticator) -{ - Q_D(QHttpNetworkConnection); - for (int i = 0; i < d->channelCount; ++i) - d->channels[i].proxyAuthenticator = *authenticator; -} - -void QHttpNetworkConnection::setAuthentication(const QString &domain, QAuthenticator *authenticator) -{ - Q_UNUSED(domain); // ### domain ? - Q_D(QHttpNetworkConnection); - for (int i = 0; i < d->channelCount; ++i) - d->channels[i].authenticator = *authenticator; -} - #ifndef QT_NO_NETWORKPROXY void QHttpNetworkConnection::setCacheProxy(const QNetworkProxy &networkProxy) { @@ -954,8 +948,11 @@ void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int // e.g. it is for SOCKS proxies which require authentication. void QHttpNetworkConnectionPrivate::emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator* auth) { - Q_Q(QHttpNetworkConnection); - emit q->proxyAuthenticationRequired(proxy, auth, q); + // Also pause the connection because socket notifiers may fire while an user + // dialog is displaying + pauseConnection(); + emit chan->reply->proxyAuthenticationRequired(proxy, auth); + resumeConnection(); int i = indexOf(chan->socket); copyCredentials(i, auth, true); } |