summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQt Continuous Integration System <qt-info@nokia.com>2012-01-05 09:04:56 (GMT)
committerQt Continuous Integration System <qt-info@nokia.com>2012-01-05 09:04:56 (GMT)
commit218ef02b6e3995f9dd01aafb6a41fe90e68b32e2 (patch)
tree3593584557ff594372dde11f92fd545a4b1e909b
parente87db399c27bfd325fb502b7b30db1dce9b87fa5 (diff)
parent450478d8281f54003c546b0e67bb61948c46207d (diff)
downloadQt-218ef02b6e3995f9dd01aafb6a41fe90e68b32e2.zip
Qt-218ef02b6e3995f9dd01aafb6a41fe90e68b32e2.tar.gz
Qt-218ef02b6e3995f9dd01aafb6a41fe90e68b32e2.tar.bz2
Merge branch 'master' of scm.dev.nokia.troll.no:qt/qt-symbian-staging into master-integration
* 'master' of scm.dev.nokia.troll.no:qt/qt-symbian-staging: Fix http authentication to a different realm on the same server Fix race in http connection channel Don't fetch credentials from cache following a failed proxy authentication Handle plain socket write errors in SSL Fix for assertion failure Fix faulty logic in http connection pipelining Test case for QTBUG-22875 QThreads on Symbian are named to allow them to be opened externally
-rw-r--r--src/corelib/thread/qthread_symbian.cpp18
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp25
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp15
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel_p.h2
-rw-r--r--src/network/access/qnetworkaccessauthenticationmanager.cpp5
-rw-r--r--src/network/access/qnetworkaccessauthenticationmanager_p.h2
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp12
-rw-r--r--src/network/kernel/qauthenticator.cpp7
-rw-r--r--src/network/kernel/qauthenticator_p.h1
-rw-r--r--src/network/socket/qhttpsocketengine.cpp20
-rw-r--r--src/network/socket/qhttpsocketengine_p.h1
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp11
-rw-r--r--tests/auto/qnetworkreply/tst_qnetworkreply.cpp218
13 files changed, 313 insertions, 24 deletions
diff --git a/src/corelib/thread/qthread_symbian.cpp b/src/corelib/thread/qthread_symbian.cpp
index 2ea1b44..78bb293 100644
--- a/src/corelib/thread/qthread_symbian.cpp
+++ b/src/corelib/thread/qthread_symbian.cpp
@@ -46,10 +46,12 @@
#include "qthreadstorage.h"
#include "qthread_p.h"
#include <private/qsystemerror_p.h>
+#include <private/qcore_symbian_p.h>
#include <sched.h>
#include <hal.h>
#include <hal_data.h>
+#include <e32math.h>
// You only find these enumerations on Symbian^3 onwards, so we need to provide our own
// to remain compatible with older releases. They won't be called by pre-Sym^3 SDKs.
@@ -509,7 +511,21 @@ void QThread::start(Priority priority)
// operations like file I/O fail, so we increase it by default.
d->stackSize = 0x14000; // Maximum stack size on Symbian.
- int code = d->data->symbian_thread_handle.Create(KNullDesC, (TThreadFunction) QThreadPrivate::start, d->stackSize, NULL, this);
+ int code = KErrAlreadyExists;
+ QString objName = objectName();
+ TPtrC objNamePtr(qt_QString2TPtrC(objName));
+ TName name;
+ objNamePtr.Set(objNamePtr.Left(qMin(objNamePtr.Length(), name.MaxLength() - 16)));
+ const int MaxRetries = 10;
+ for (int i=0; i<MaxRetries && code == KErrAlreadyExists; i++) {
+ // generate a thread name using a similar method to libpthread in Symbian
+ // a named thread can be opened from another process
+ name.Zero();
+ name.Append(objNamePtr);
+ name.AppendNumFixedWidth(int(this), EHex, 8);
+ name.AppendNumFixedWidth(Math::Random(), EHex, 8);
+ code = d->data->symbian_thread_handle.Create(name, (TThreadFunction) QThreadPrivate::start, d->stackSize, NULL, this);
+ }
if (code == KErrNone) {
d->thread_id = d->data->symbian_thread_handle.Id();
TThreadPriority symPriority = calculateSymbianPriority(priority);
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index 0365703..33e3be9 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -375,9 +375,23 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
if (priv->phase == QAuthenticatorPrivate::Done) {
pauseConnection();
if (!isProxy) {
+ if (channels[i].authenticationCredentialsSent) {
+ auth->detach();
+ priv = QAuthenticatorPrivate::getPrivate(*auth);
+ priv->hasFailed = true;
+ priv->phase = QAuthenticatorPrivate::Done;
+ channels[i].authenticationCredentialsSent = false;
+ }
emit reply->authenticationRequired(reply->request(), auth);
#ifndef QT_NO_NETWORKPROXY
} else {
+ if (channels[i].proxyCredentialsSent) {
+ auth->detach();
+ priv = QAuthenticatorPrivate::getPrivate(*auth);
+ priv->hasFailed = true;
+ priv->phase = QAuthenticatorPrivate::Done;
+ channels[i].proxyCredentialsSent = false;
+ }
emit reply->proxyAuthenticationRequired(networkProxy, auth);
#endif
}
@@ -415,7 +429,6 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
reply->d_func()->errorString = errorDetail(errorCode, socket);
emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
// ### at this point the reply could be deleted
- socket->close();
return true;
}
//resend the request
@@ -438,6 +451,7 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket,
if (priv && priv->method != QAuthenticatorPrivate::None) {
QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
request.setHeaderField("Authorization", response);
+ channels[i].authenticationCredentialsSent = true;
}
}
}
@@ -449,6 +463,7 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket,
if (priv && priv->method != QAuthenticatorPrivate::None) {
QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
request.setHeaderField("Proxy-Authorization", response);
+ channels[i].proxyCredentialsSent = true;
}
}
}
@@ -582,9 +597,13 @@ void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
// we do not like authentication stuff
// ### make sure to be OK with this in later releases
- if (!channels[i].authenticator.isNull() || !channels[i].authenticator.user().isEmpty())
+ if (!channels[i].authenticator.isNull()
+ && (!channels[i].authenticator.user().isEmpty()
+ || !channels[i].authenticator.password().isEmpty()))
return;
- if (!channels[i].proxyAuthenticator.isNull() || !channels[i].proxyAuthenticator.user().isEmpty())
+ if (!channels[i].proxyAuthenticator.isNull()
+ && (!channels[i].proxyAuthenticator.user().isEmpty()
+ || !channels[i].proxyAuthenticator.password().isEmpty()))
return;
// must be in ReadingState or WaitingState
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index b9db7fe..628114a 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -73,6 +73,8 @@ QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
, lastStatus(0)
, pendingEncrypt(false)
, reconnectAttempts(2)
+ , authenticationCredentialsSent(false)
+ , proxyCredentialsSent(false)
, authMethod(QAuthenticatorPrivate::None)
, proxyAuthMethod(QAuthenticatorPrivate::None)
#ifndef QT_NO_OPENSSL
@@ -555,6 +557,14 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
// reset state
pipeliningSupported = PipeliningSupportUnknown;
+ authenticationCredentialsSent = false;
+ proxyCredentialsSent = false;
+ authenticator.detach();
+ QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(authenticator);
+ priv->hasFailed = false;
+ proxyAuthenticator.detach();
+ priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
+ priv->hasFailed = false;
// This workaround is needed since we use QAuthenticator for NTLM authentication. The "phase == Done"
// is the usual criteria for emitting authentication signals. The "phase" is set to "Done" when the
@@ -562,7 +572,7 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
// check the "phase" for generating the Authorization header. NTLM authentication is a two stage
// process & needs the "phase". To make sure the QAuthenticator uses the current username/password
// the phase is reset to Start.
- QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(authenticator);
+ priv = QAuthenticatorPrivate::getPrivate(authenticator);
if (priv && priv->phase == QAuthenticatorPrivate::Done)
priv->phase = QAuthenticatorPrivate::Start;
priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
@@ -837,6 +847,9 @@ void QHttpNetworkConnectionChannel::handleStatus()
closeAndResendCurrentRequest();
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
+ } else {
+ //authentication cancelled, close the channel.
+ close();
}
} else {
emit reply->headerChanged();
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
index f635cc9..31a286b 100644
--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
@@ -113,6 +113,8 @@ public:
QAuthenticatorPrivate::Method proxyAuthMethod;
QAuthenticator authenticator;
QAuthenticator proxyAuthenticator;
+ bool authenticationCredentialsSent;
+ bool proxyCredentialsSent;
#ifndef QT_NO_OPENSSL
bool ignoreAllSslErrors;
QList<QSslError> ignoreSslErrorsList;
diff --git a/src/network/access/qnetworkaccessauthenticationmanager.cpp b/src/network/access/qnetworkaccessauthenticationmanager.cpp
index b618ccc..9551b76 100644
--- a/src/network/access/qnetworkaccessauthenticationmanager.cpp
+++ b/src/network/access/qnetworkaccessauthenticationmanager.cpp
@@ -159,6 +159,11 @@ void QNetworkAccessAuthenticationManager::cacheProxyCredentials(const QNetworkPr
QString realm = authenticator->realm();
QNetworkProxy proxy = p;
proxy.setUser(authenticator->user());
+
+ // don't cache null passwords, empty password may be valid though
+ if (authenticator->password().isNull())
+ return;
+
// Set two credentials: one with the username and one without
do {
// Set two credentials actually: one with and one without the realm
diff --git a/src/network/access/qnetworkaccessauthenticationmanager_p.h b/src/network/access/qnetworkaccessauthenticationmanager_p.h
index ddfc116..718c58f 100644
--- a/src/network/access/qnetworkaccessauthenticationmanager_p.h
+++ b/src/network/access/qnetworkaccessauthenticationmanager_p.h
@@ -73,7 +73,7 @@ public:
QString user;
QString password;
bool isNull() {
- return domain.isNull();
+ return domain.isNull() && user.isNull() && password.isNull();
}
};
Q_DECLARE_TYPEINFO(QNetworkAuthenticationCredential, Q_MOVABLE_TYPE);
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index 8fc8eb7..689441e 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -61,7 +61,7 @@
#include "QtCore/qbuffer.h"
#include "QtCore/qurl.h"
#include "QtCore/qvector.h"
-#include "QtNetwork/qauthenticator.h"
+#include "QtNetwork/private/qauthenticator_p.h"
#include "QtNetwork/qsslconfiguration.h"
#include "QtNetwork/qnetworkconfigmanager.h"
#include "QtNetwork/qhttpmultipart.h"
@@ -1093,14 +1093,8 @@ void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(QNetworkAccessBac
QAuthenticator *authenticator)
{
Q_Q(QNetworkAccessManager);
- // ### FIXME Tracking of successful authentications
- // This code is a bit broken right now for SOCKS authentication
- // first request: proxyAuthenticationRequired gets emitted, credentials gets saved
- // second request: (proxy != backend->reply->lastProxyAuthentication) does not evaluate to true,
- // proxyAuthenticationRequired gets emitted again
- // possible solution: some tracking inside the authenticator
- // or a new function proxyAuthenticationSucceeded(true|false)
- if (proxy != backend->reply->lastProxyAuthentication) {
+ QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*authenticator);
+ if (proxy != backend->reply->lastProxyAuthentication && (!priv || !priv->hasFailed)) {
QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedProxyCredentials(proxy);
if (!cred.isNull()) {
authenticator->setUser(cred.user);
diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp
index 7b63567..d0524ee 100644
--- a/src/network/kernel/qauthenticator.cpp
+++ b/src/network/kernel/qauthenticator.cpp
@@ -326,6 +326,7 @@ bool QAuthenticator::isNull() const
QAuthenticatorPrivate::QAuthenticatorPrivate()
: ref(0)
, method(None)
+ , hasFailed(false)
, phase(Start)
, nonceCount(0)
{
@@ -387,8 +388,7 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
switch(method) {
case Basic:
- if(realm.isEmpty())
- this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm"));
+ this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm"));
if (user.isEmpty() && password.isEmpty())
phase = Done;
break;
@@ -396,8 +396,7 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
// #### extract from header
break;
case DigestMd5: {
- if(realm.isEmpty())
- this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm"));
+ this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm"));
if (options.value("stale").toLower() == "true")
phase = Start;
if (user.isEmpty() && password.isEmpty())
diff --git a/src/network/kernel/qauthenticator_p.h b/src/network/kernel/qauthenticator_p.h
index 88a3051..937f4e0 100644
--- a/src/network/kernel/qauthenticator_p.h
+++ b/src/network/kernel/qauthenticator_p.h
@@ -77,6 +77,7 @@ public:
Method method;
QString realm;
QByteArray challenge;
+ bool hasFailed; //credentials have been tried but rejected by server.
enum Phase {
Start,
diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp
index b62bc05..c582c57 100644
--- a/src/network/socket/qhttpsocketengine.cpp
+++ b/src/network/socket/qhttpsocketengine.cpp
@@ -136,6 +136,8 @@ bool QHttpSocketEngine::connectInternal()
{
Q_D(QHttpSocketEngine);
+ d->credentialsSent = false;
+
// If the handshake is done, enter ConnectedState state and return true.
if (d->state == Connected) {
qWarning("QHttpSocketEngine::connectToHost: called when already connected");
@@ -514,6 +516,7 @@ void QHttpSocketEngine::slotSocketConnected()
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
//qDebug() << "slotSocketConnected: priv=" << priv << (priv ? (int)priv->method : -1);
if (priv && priv->method != QAuthenticatorPrivate::None) {
+ d->credentialsSent = true;
data += "Proxy-Authorization: " + priv->calculateResponse(method, path);
data += "\r\n";
}
@@ -591,15 +594,26 @@ void QHttpSocketEngine::slotSocketReadNotification()
d->readBuffer.clear(); // we parsed the proxy protocol response. from now on direct socket reading will be done
int statusCode = responseHeader.statusCode();
+ QAuthenticatorPrivate *priv = 0;
if (statusCode == 200) {
d->state = Connected;
setLocalAddress(d->socket->localAddress());
setLocalPort(d->socket->localPort());
setState(QAbstractSocket::ConnectedState);
+ d->authenticator.detach();
+ priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
+ priv->hasFailed = false;
} else if (statusCode == 407) {
- if (d->authenticator.isNull())
+ if (d->credentialsSent) {
+ //407 response again means the provided username/password were invalid.
+ d->authenticator = QAuthenticator(); //this is needed otherwise parseHttpResponse won't set the state, and then signal isn't emitted.
+ d->authenticator.detach();
+ priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
+ priv->hasFailed = true;
+ }
+ else if (d->authenticator.isNull())
d->authenticator.detach();
- QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
+ priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
priv->parseHttpResponse(responseHeader, true);
@@ -639,7 +653,6 @@ void QHttpSocketEngine::slotSocketReadNotification()
if (priv->phase == QAuthenticatorPrivate::Done)
emit proxyAuthenticationRequired(d->proxy, &d->authenticator);
-
// priv->phase will get reset to QAuthenticatorPrivate::Start if the authenticator got modified in the signal above.
if (priv->phase == QAuthenticatorPrivate::Done) {
setError(QAbstractSocket::ProxyAuthenticationRequiredError, tr("Authentication required"));
@@ -796,6 +809,7 @@ QHttpSocketEnginePrivate::QHttpSocketEnginePrivate()
, readNotificationPending(false)
, writeNotificationPending(false)
, connectionNotificationPending(false)
+ , credentialsSent(false)
, pendingResponseData(0)
{
socket = 0;
diff --git a/src/network/socket/qhttpsocketengine_p.h b/src/network/socket/qhttpsocketengine_p.h
index d7cc7c1..476d689 100644
--- a/src/network/socket/qhttpsocketengine_p.h
+++ b/src/network/socket/qhttpsocketengine_p.h
@@ -182,6 +182,7 @@ public:
bool readNotificationPending;
bool writeNotificationPending;
bool connectionNotificationPending;
+ bool credentialsSent;
uint pendingResponseData;
};
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 652bcbd..e432865 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -1040,10 +1040,17 @@ void QSslSocketBackendPrivate::transmit()
int encryptedBytesRead = q_BIO_read(writeBio, data.data(), pendingBytes);
// Write encrypted data from the buffer to the socket.
- plainSocket->write(data.constData(), encryptedBytesRead);
+ qint64 actualWritten = plainSocket->write(data.constData(), encryptedBytesRead);
#ifdef QSSLSOCKET_DEBUG
- qDebug() << "QSslSocketBackendPrivate::transmit: wrote" << encryptedBytesRead << "encrypted bytes to the socket";
+ qDebug() << "QSslSocketBackendPrivate::transmit: wrote" << encryptedBytesRead << "encrypted bytes to the socket" << actualWritten << "actual.";
#endif
+ if (actualWritten < 0) {
+ //plain socket write fails if it was in the pending close state.
+ q->setErrorString(plainSocket->errorString());
+ q->setSocketError(plainSocket->error());
+ emit q->error(plainSocket->error());
+ return;
+ }
transmitting = true;
}
diff --git a/tests/auto/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/qnetworkreply/tst_qnetworkreply.cpp
index 371ac57..6760b73 100644
--- a/tests/auto/qnetworkreply/tst_qnetworkreply.cpp
+++ b/tests/auto/qnetworkreply/tst_qnetworkreply.cpp
@@ -380,6 +380,9 @@ private Q_SLOTS:
void httpAbort();
void dontInsertPartialContentIntoTheCache();
+ void authenticationCacheAfterCancel_data();
+ void authenticationCacheAfterCancel();
+ void authenticationWithDifferentRealm();
void synchronousAuthenticationCache();
void pipelining();
@@ -6026,6 +6029,221 @@ void tst_QNetworkReply::qtbug4121unknownAuthentication()
QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
}
+void tst_QNetworkReply::authenticationCacheAfterCancel_data()
+{
+ QTest::addColumn<QNetworkProxy>("proxy");
+ QTest::addColumn<bool>("proxyAuth");
+ QTest::addColumn<QUrl>("url");
+ for (int i = 0; i < proxies.count(); ++i) {
+ QTest::newRow("http" + proxies.at(i).tag) << proxies.at(i).proxy << proxies.at(i).requiresAuthentication << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth/rfc3252.txt");
+#ifndef QT_NO_OPENSSL
+ QTest::newRow("https" + proxies.at(i).tag) << proxies.at(i).proxy << proxies.at(i).requiresAuthentication << QUrl("https://" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth/rfc3252.txt");
+#endif
+ }
+}
+
+class AuthenticationCacheHelper : public QObject
+{
+ Q_OBJECT
+public:
+ AuthenticationCacheHelper()
+ {}
+public slots:
+ void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth)
+ {
+ if (!proxyPassword.isNull()) {
+ auth->setUser(proxyUserName);
+ auth->setPassword(proxyPassword);
+ //clear credentials, if they are asked again, they were bad
+ proxyUserName.clear();
+ proxyPassword.clear();
+ }
+ }
+ void authenticationRequired(QNetworkReply*,QAuthenticator *auth)
+ {
+ if (!httpPassword.isNull()) {
+ auth->setUser(httpUserName);
+ auth->setPassword(httpPassword);
+ //clear credentials, if they are asked again, they were bad
+ httpUserName.clear();
+ httpPassword.clear();
+ }
+ }
+public:
+ QString httpUserName;
+ QString httpPassword;
+ QString proxyUserName;
+ QString proxyPassword;
+};
+
+/* Purpose of this test is to check credentials are cached correctly.
+ - If user cancels authentication dialog (i.e. nothing is set to the QAuthenticator by the callback) then this is not cached
+ - if user supplies a wrong password, then this is not cached
+ - if user supplies a correct user/password combination then this is cached
+
+ Test is checking both the proxyAuthenticationRequired and authenticationRequired signals.
+ */
+void tst_QNetworkReply::authenticationCacheAfterCancel()
+{
+ QFETCH(QNetworkProxy, proxy);
+ QFETCH(bool, proxyAuth);
+ QFETCH(QUrl, url);
+ QNetworkAccessManager manager;
+#ifndef QT_NO_OPENSSL
+ connect(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
+ SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
+#endif
+ manager.setProxy(proxy);
+ QSignalSpy authSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
+ QSignalSpy proxyAuthSpy(&manager, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)));
+
+ AuthenticationCacheHelper helper;
+ connect(&manager, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), &helper, SLOT(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)));
+ connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), &helper, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
+
+ QNetworkRequest request(url);
+ QNetworkReplyPtr reply;
+ if (proxyAuth) {
+ //should fail due to no credentials
+ reply = manager.get(request);
+ connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
+ QTestEventLoop::instance().enterLoop(10);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+
+ QCOMPARE(reply->error(), QNetworkReply::ProxyAuthenticationRequiredError);
+ QCOMPARE(authSpy.count(), 0);
+ QCOMPARE(proxyAuthSpy.count(), 1);
+ proxyAuthSpy.clear();
+
+ //should fail due to bad credentials
+ helper.proxyUserName = "qsockstest";
+ helper.proxyPassword = "badpassword";
+ reply = manager.get(request);
+ connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
+ QTestEventLoop::instance().enterLoop(10);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+
+ QEXPECT_FAIL("http+socksauth", "QTBUG-23136 - danted accepts bad authentication but blocks the connection", Continue);
+ QEXPECT_FAIL("https+socksauth", "QTBUG-23136 - danted accepts bad authentication but blocks the connection", Continue);
+
+ QCOMPARE(reply->error(), QNetworkReply::ProxyAuthenticationRequiredError);
+ QCOMPARE(authSpy.count(), 0);
+ QVERIFY(proxyAuthSpy.count() > 0);
+ proxyAuthSpy.clear();
+
+ //QTBUG-23136 workaround
+ if (proxy.port() == 1081) {
+#ifdef QT_BUILD_INTERNAL
+ QNetworkAccessManagerPrivate::clearCache(&manager);
+#else
+ return; //XFAIL result above
+#endif
+ }
+
+ //next proxy auth should succeed, due to correct credentials
+ helper.proxyUserName = "qsockstest";
+ helper.proxyPassword = "password";
+ }
+
+ //should fail due to no credentials
+ reply = manager.get(request);
+ connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
+ QTestEventLoop::instance().enterLoop(10);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+
+ QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
+ QVERIFY(authSpy.count() > 0);
+ authSpy.clear();
+ if (proxyAuth) {
+ QVERIFY(proxyAuthSpy.count() > 0);
+ proxyAuthSpy.clear();
+ }
+
+ //should fail due to bad credentials
+ helper.httpUserName = "baduser";
+ helper.httpPassword = "badpassword";
+ reply = manager.get(request);
+ connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
+ QTestEventLoop::instance().enterLoop(10);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+
+ QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
+ QVERIFY(authSpy.count() > 0);
+ authSpy.clear();
+ if (proxyAuth) {
+ //should be supplied from cache
+ QCOMPARE(proxyAuthSpy.count(), 0);
+ proxyAuthSpy.clear();
+ }
+
+ //next auth should succeed, due to correct credentials
+ helper.httpUserName = "httptest";
+ helper.httpPassword = "httptest";
+
+ reply = manager.get(request);
+ connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
+ QTestEventLoop::instance().enterLoop(10);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+
+ QCOMPARE(reply->error(), QNetworkReply::NoError);
+ QVERIFY(authSpy.count() > 0);
+ authSpy.clear();
+ if (proxyAuth) {
+ //should be supplied from cache
+ QCOMPARE(proxyAuthSpy.count(), 0);
+ proxyAuthSpy.clear();
+ }
+
+ //next auth should use cached credentials
+ reply = manager.get(request);
+ connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
+ QTestEventLoop::instance().enterLoop(10);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+
+ QCOMPARE(reply->error(), QNetworkReply::NoError);
+ //should be supplied from cache
+ QCOMPARE(authSpy.count(), 0);
+ authSpy.clear();
+ if (proxyAuth) {
+ //should be supplied from cache
+ QCOMPARE(proxyAuthSpy.count(), 0);
+ proxyAuthSpy.clear();
+ }
+
+}
+
+void tst_QNetworkReply::authenticationWithDifferentRealm()
+{
+ AuthenticationCacheHelper helper;
+ QNetworkAccessManager manager;
+#ifndef QT_NO_OPENSSL
+ connect(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
+ SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
+#endif
+ connect(&manager, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), &helper, SLOT(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)));
+ connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), &helper, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
+
+ helper.httpUserName = "httptest";
+ helper.httpPassword = "httptest";
+
+ QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth/rfc3252.txt"));
+ QNetworkReply* reply = manager.get(request);
+ connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
+ QTestEventLoop::instance().enterLoop(10);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+ QCOMPARE(reply->error(), QNetworkReply::NoError);
+
+ helper.httpUserName = "httptest";
+ helper.httpPassword = "httptest";
+
+ request.setUrl(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/auth-digest/"));
+ reply = manager.get(request);
+ connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
+ QTestEventLoop::instance().enterLoop(10);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+ QCOMPARE(reply->error(), QNetworkReply::NoError);
+}
+
class QtBug13431Helper : public QObject {
Q_OBJECT
public: