diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 69fb7226ee..51aed45753 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -435,6 +435,11 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket // send any pending requests copyCredentials(i, auth, isProxy); } + } 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 reply->cacheCredentials(reply->request(), auth); } // - 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 diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index f2450f5662..a35750fa3f 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -202,7 +202,6 @@ bool QHttpNetworkConnectionChannel::sendRequest() || (!url.password().isEmpty() && url.password() != auth.password())) { auth.setUser(url.userName()); auth.setPassword(url.password()); - emit reply->cacheCredentials(request, &auth); connection->d_func()->copyCredentials(connection->d_func()->indexOf(socket), &auth, false); } // clear the userinfo, since we use the same request for resending diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp index 9c361e0cf4..e03b3fc59d 100644 --- a/src/network/access/qhttpthreaddelegate.cpp +++ b/src/network/access/qhttpthreaddelegate.cpp @@ -312,8 +312,6 @@ void QHttpThreadDelegate::startRequest() // some signals are only interesting when normal asynchronous style is used connect(httpReply,SIGNAL(readyRead()), this, SLOT(readyReadSlot())); connect(httpReply,SIGNAL(dataReadProgress(qint64, qint64)), this, SLOT(dataReadProgressSlot(qint64,qint64))); - connect(httpReply, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)), - this, SLOT(cacheCredentialsSlot(QHttpNetworkRequest,QAuthenticator*))); #ifndef QT_NO_OPENSSL connect(httpReply,SIGNAL(sslErrors(const QList)), this, SLOT(sslErrorsSlot(QList))); #endif @@ -325,6 +323,9 @@ void QHttpThreadDelegate::startRequest() connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), this, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); } + + connect(httpReply, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)), + this, SLOT(cacheCredentialsSlot(QHttpNetworkRequest,QAuthenticator*))); } // This gets called from the user thread or by the synchronous HTTP timeout timer diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp index e7a9f7d0d3..7f00ba90ca 100644 --- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp @@ -378,6 +378,7 @@ private Q_SLOTS: void dontInsertPartialContentIntoTheCache(); void httpUserAgent(); + void synchronousAuthenticationCache(); // NOTE: This test must be last! void parentingRepliesToTheApp(); @@ -499,6 +500,14 @@ protected: client->setParent(this); ++totalConnections; } + + virtual void reply() { + // we need to emulate the bytesWrittenSlot call if the data is empty. + if (dataToTransmit.size() == 0) + QMetaObject::invokeMethod(this, "bytesWrittenSlot", Qt::QueuedConnection); + else + client->write(dataToTransmit); + } private: void connectSocketSignals() { @@ -532,11 +541,7 @@ public slots: if (multiple) receivedData.remove(0, doubleEndlPos+4); - // we need to emulate the bytesWrittenSlot call if the data is empty. - if (dataToTransmit.size() == 0) - QMetaObject::invokeMethod(this, "bytesWrittenSlot", Qt::QueuedConnection); - else - client->write(dataToTransmit); + reply(); } } @@ -6359,6 +6364,79 @@ void tst_QNetworkReply::httpUserAgent() QVERIFY(server.receivedData.contains("\r\nUser-Agent: abcDEFghi\r\n")); } +void tst_QNetworkReply::synchronousAuthenticationCache() +{ + class MiniAuthServer : public MiniHttpServer { + public: + MiniAuthServer(QThread *thread) : MiniHttpServer(QByteArray(), false, thread) {}; + virtual void reply() { + + dataToTransmit = + "HTTP/1.0 401 Unauthorized\r\n" + "WWW-Authenticate: Basic realm=\"QNetworkAccessManager Test Realm\"\r\n" + "Content-Length: 4\r\n" + "Connection: close\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + "auth"; + QRegExp rx("Authorization: Basic ([^\r\n]*)\r\n"); + if (rx.indexIn(receivedData) > 0) { + if (QByteArray::fromBase64(rx.cap(1).toLatin1()) == "login:password") { + dataToTransmit = + "HTTP/1.0 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 2\r\n" + "\r\n" + "OK"; + } + } + receivedData.clear(); + MiniHttpServer::reply(); + } + }; + + // when using synchronous commands, we need a different event loop for + // the server thread, because the client is never returning to the + // event loop + QScopedPointer serverThread(new QThread); + QScopedPointer server(new MiniAuthServer(serverThread.data())); + server->doClose = true; + + //1) URL without credentials, we are not authenticated + { + QUrl url = "http://localhost:" + QString::number(server->serverPort()) + "/path"; + QNetworkRequest request(url); + request.setAttribute(QNetworkRequest::SynchronousRequestAttribute, true); + + QNetworkReplyPtr reply = manager.get(request); + QVERIFY(reply->isFinished()); + QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError); + } + + //2) URL with credentials, we are authenticated + { + QUrl url = "http://login:password@localhost:" + QString::number(server->serverPort()) + "/path2"; + QNetworkRequest request(url); + request.setAttribute(QNetworkRequest::SynchronousRequestAttribute, true); + + QNetworkReplyPtr reply = manager.get(request); + QVERIFY(reply->isFinished()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->readAll().constData(), "OK"); + } + + //3) URL without credentials, we are authenticated because they are cached + { + QUrl url = "http://localhost:" + QString::number(server->serverPort()) + "/path3"; + QNetworkRequest request(url); + request.setAttribute(QNetworkRequest::SynchronousRequestAttribute, true); + + QNetworkReplyPtr reply = manager.get(request); + QVERIFY(reply->isFinished()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->readAll().constData(), "OK"); + } +} // NOTE: This test must be last testcase in tst_qnetworkreply! void tst_QNetworkReply::parentingRepliesToTheApp()