diff options
Diffstat (limited to 'tests/auto/network/access/spdy/tst_spdy.cpp')
-rw-r--r-- | tests/auto/network/access/spdy/tst_spdy.cpp | 690 |
1 files changed, 690 insertions, 0 deletions
diff --git a/tests/auto/network/access/spdy/tst_spdy.cpp b/tests/auto/network/access/spdy/tst_spdy.cpp new file mode 100644 index 0000000000..d2a220bad4 --- /dev/null +++ b/tests/auto/network/access/spdy/tst_spdy.cpp @@ -0,0 +1,690 @@ +/**************************************************************************** +** +** Copyright (C) 2014 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QtNetwork/QNetworkAccessManager> +#include <QtNetwork/QNetworkReply> +#include <QtNetwork/QHttpPart> +#include <QtNetwork/QHttpMultiPart> +#include <QtNetwork/QNetworkProxy> +#include <QtNetwork/QAuthenticator> +#ifdef QT_BUILD_INTERNAL +#include <QtNetwork/private/qsslsocket_openssl_p.h> +#endif // QT_BUILD_INTERNAL + +#include "../../../network-settings.h" + +Q_DECLARE_METATYPE(QAuthenticator*) + +class tst_Spdy: public QObject +{ + Q_OBJECT + +public: + tst_Spdy(); + ~tst_Spdy(); + +private Q_SLOTS: + void settingsAndNegotiation_data(); + void settingsAndNegotiation(); + void download_data(); + void download(); + void headerFields(); + void upload_data(); + void upload(); + void errors_data(); + void errors(); + void multipleRequests_data(); + void multipleRequests(); + +private: + QNetworkAccessManager m_manager; + int m_multipleRequestsCount; + int m_multipleRepliesFinishedCount; + +protected Q_SLOTS: + void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *authenticator); + void multipleRequestsFinishedSlot(); +}; + +tst_Spdy::tst_Spdy() +{ +#if defined(QT_BUILD_INTERNAL) && !defined(QT_NO_SSL) && OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) + qRegisterMetaType<QNetworkReply *>(); // for QSignalSpy + qRegisterMetaType<QAuthenticator *>(); + + connect(&m_manager, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), + this, SLOT(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *))); +#else + QSKIP("Qt built withouth OpenSSL, or the OpenSSL version is too old"); +#endif // defined(QT_BUILD_INTERNAL) && !defined(QT_NO_SSL) ... +} + +tst_Spdy::~tst_Spdy() +{ +} + +void tst_Spdy::settingsAndNegotiation_data() +{ + QTest::addColumn<QUrl>("url"); + QTest::addColumn<bool>("setAttribute"); + QTest::addColumn<bool>("enabled"); + QTest::addColumn<QByteArray>("expectedProtocol"); + QTest::addColumn<QByteArray>("expectedContent"); + + QTest::newRow("default-settings") << QUrl("https://" + QtNetworkSettings::serverName() + + "/qtest/cgi-bin/echo.cgi?1") + << false << false << QByteArray() + << QByteArray("1"); + + QTest::newRow("http-url") << QUrl("http://" + QtNetworkSettings::serverName() + + "/qtest/cgi-bin/echo.cgi?1") + << true << true << QByteArray() + << QByteArray("1"); + + QTest::newRow("spdy-disabled") << QUrl("https://" + QtNetworkSettings::serverName() + + "/qtest/cgi-bin/echo.cgi?1") + << true << false << QByteArray() + << QByteArray("1"); + +#ifndef QT_NO_OPENSSL + QTest::newRow("spdy-enabled") << QUrl("https://" + QtNetworkSettings::serverName() + + "/qtest/cgi-bin/echo.cgi?1") + << true << true << QByteArray(QSslConfiguration::NextProtocolSpdy3_0) + << QByteArray("1"); +#endif // QT_NO_OPENSSL +} + +void tst_Spdy::settingsAndNegotiation() +{ + QFETCH(QUrl, url); + QFETCH(bool, setAttribute); + QFETCH(bool, enabled); + + QNetworkRequest request(url); + + if (setAttribute) { + request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, QVariant(enabled)); + } + + QNetworkReply *reply = m_manager.get(request); + reply->ignoreSslErrors(); + QSignalSpy metaDataChangedSpy(reply, SIGNAL(metaDataChanged())); + QSignalSpy readyReadSpy(reply, SIGNAL(readyRead())); + QSignalSpy finishedSpy(reply, SIGNAL(finished())); + + QObject::connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QSignalSpy finishedManagerSpy(&m_manager, SIGNAL(finished(QNetworkReply*))); + + QTestEventLoop::instance().enterLoop(15); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QFETCH(QByteArray, expectedProtocol); + +#ifndef QT_NO_OPENSSL + bool expectedSpdyUsed = (expectedProtocol == QSslConfiguration::NextProtocolSpdy3_0) + ? true : false; + QCOMPARE(reply->attribute(QNetworkRequest::SpdyWasUsedAttribute).toBool(), expectedSpdyUsed); +#endif // QT_NO_OPENSSL + + QCOMPARE(metaDataChangedSpy.count(), 1); + QCOMPARE(finishedSpy.count(), 1); + + int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + QCOMPARE(statusCode, 200); + + QByteArray content = reply->readAll(); + + QFETCH(QByteArray, expectedContent); + QCOMPARE(expectedContent, content); + +#ifndef QT_NO_OPENSSL + QSslConfiguration::NextProtocolNegotiationStatus expectedStatus = + (expectedProtocol.isEmpty()) + ? QSslConfiguration::NextProtocolNegotiationNone + : QSslConfiguration::NextProtocolNegotiationNegotiated; + QCOMPARE(reply->sslConfiguration().nextProtocolNegotiationStatus(), + expectedStatus); + + QCOMPARE(reply->sslConfiguration().nextNegotiatedProtocol(), expectedProtocol); +#endif // QT_NO_OPENSSL +} + +void tst_Spdy::proxyAuthenticationRequired(const QNetworkProxy &/*proxy*/, + QAuthenticator *authenticator) +{ + authenticator->setUser("qsockstest"); + authenticator->setPassword("password"); +} + +void tst_Spdy::download_data() +{ + QTest::addColumn<QUrl>("url"); + QTest::addColumn<QString>("fileName"); + QTest::addColumn<QNetworkProxy>("proxy"); + + QTest::newRow("mediumfile") << QUrl("https://" + QtNetworkSettings::serverName() + + "/qtest/rfc3252.txt") + << QFINDTESTDATA("../qnetworkreply/rfc3252.txt") + << QNetworkProxy(); + + QHostInfo hostInfo = QHostInfo::fromName(QtNetworkSettings::serverName()); + QString proxyserver = hostInfo.addresses().first().toString(); + + QTest::newRow("mediumfile-http-proxy") << QUrl("https://" + QtNetworkSettings::serverName() + + "/qtest/rfc3252.txt") + << QFINDTESTDATA("../qnetworkreply/rfc3252.txt") + << QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3128); + + QTest::newRow("mediumfile-http-proxy-auth") << QUrl("https://" + QtNetworkSettings::serverName() + + "/qtest/rfc3252.txt") + << QFINDTESTDATA("../qnetworkreply/rfc3252.txt") + << QNetworkProxy(QNetworkProxy::HttpProxy, + proxyserver, 3129); + + QTest::newRow("mediumfile-socks-proxy") << QUrl("https://" + QtNetworkSettings::serverName() + + "/qtest/rfc3252.txt") + << QFINDTESTDATA("../qnetworkreply/rfc3252.txt") + << QNetworkProxy(QNetworkProxy::Socks5Proxy, proxyserver, 1080); + + QTest::newRow("mediumfile-socks-proxy-auth") << QUrl("https://" + QtNetworkSettings::serverName() + + "/qtest/rfc3252.txt") + << QFINDTESTDATA("../qnetworkreply/rfc3252.txt") + << QNetworkProxy(QNetworkProxy::Socks5Proxy, + proxyserver, 1081); + + QTest::newRow("bigfile") << QUrl("https://" + QtNetworkSettings::serverName() + + "/qtest/bigfile") + << QFINDTESTDATA("../qnetworkreply/bigfile") + << QNetworkProxy(); +} + +void tst_Spdy::download() +{ + QFETCH(QUrl, url); + QFETCH(QString, fileName); + QFETCH(QNetworkProxy, proxy); + + QNetworkRequest request(url); + request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true); + + if (proxy.type() != QNetworkProxy::DefaultProxy) { + m_manager.setProxy(proxy); + } + QNetworkReply *reply = m_manager.get(request); + reply->ignoreSslErrors(); + QSignalSpy metaDataChangedSpy(reply, SIGNAL(metaDataChanged())); + QSignalSpy downloadProgressSpy(reply, SIGNAL(downloadProgress(qint64, qint64))); + QSignalSpy readyReadSpy(reply, SIGNAL(readyRead())); + QSignalSpy finishedSpy(reply, SIGNAL(finished())); + + QObject::connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QSignalSpy finishedManagerSpy(&m_manager, SIGNAL(finished(QNetworkReply*))); + QSignalSpy proxyAuthRequiredSpy(&m_manager, SIGNAL( + proxyAuthenticationRequired(const QNetworkProxy &, + QAuthenticator *))); + + QTestEventLoop::instance().enterLoop(15); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(finishedManagerSpy.count(), 1); + QCOMPARE(metaDataChangedSpy.count(), 1); + QCOMPARE(finishedSpy.count(), 1); + QVERIFY(downloadProgressSpy.count() > 0); + QVERIFY(readyReadSpy.count() > 0); + + QVERIFY(proxyAuthRequiredSpy.count() <= 1); + + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->attribute(QNetworkRequest::SpdyWasUsedAttribute).toBool(), true); + QCOMPARE(reply->attribute(QNetworkRequest::ConnectionEncryptedAttribute).toBool(), true); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + + QFile file(fileName); + QVERIFY(file.open(QIODevice::ReadOnly)); + + qint64 contentLength = reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(); + qint64 expectedContentLength = file.bytesAvailable(); + QCOMPARE(contentLength, expectedContentLength); + + QByteArray expectedContent = file.readAll(); + QByteArray content = reply->readAll(); + QCOMPARE(content, expectedContent); + + reply->deleteLater(); + m_manager.setProxy(QNetworkProxy()); // reset +} + +void tst_Spdy::headerFields() +{ + QUrl url(QUrl("https://" + QtNetworkSettings::serverName())); + QNetworkRequest request(url); + request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true); + + QNetworkReply *reply = m_manager.get(request); + reply->ignoreSslErrors(); + + QObject::connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + + QTestEventLoop::instance().enterLoop(15); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(reply->rawHeader("Content-Type"), QByteArray("text/html")); + QVERIFY(reply->rawHeader("Content-Length").toInt() > 0); + QVERIFY(reply->rawHeader("server").contains("Apache")); + + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader).toByteArray(), QByteArray("text/html")); + QVERIFY(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong() > 0); + QVERIFY(reply->header(QNetworkRequest::LastModifiedHeader).toDateTime().isValid()); + QVERIFY(reply->header(QNetworkRequest::ServerHeader).toByteArray().contains("Apache")); +} + +static inline QByteArray md5sum(const QByteArray &data) +{ + return QCryptographicHash::hash(data, QCryptographicHash::Md5).toHex().append('\n'); +} + +void tst_Spdy::upload_data() +{ + QTest::addColumn<QUrl>("url"); + QTest::addColumn<QByteArray>("data"); + QTest::addColumn<QByteArray>("uploadMethod"); + QTest::addColumn<QObject *>("uploadObject"); + QTest::addColumn<QByteArray>("md5sum"); + QTest::addColumn<QNetworkProxy>("proxy"); + + + // 1. test uploading of byte arrays + + QUrl md5Url("https://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi"); + + QByteArray data; + data = ""; + QObject *dummyObject = 0; + QTest::newRow("empty") << md5Url << data << QByteArray("POST") << dummyObject + << md5sum(data) << QNetworkProxy(); + + data = "This is a normal message."; + QTest::newRow("generic") << md5Url << data << QByteArray("POST") << dummyObject + << md5sum(data) << QNetworkProxy(); + + data = "This is a message to show that Qt rocks!\r\n\n"; + QTest::newRow("small") << md5Url << data << QByteArray("POST") << dummyObject + << md5sum(data) << QNetworkProxy(); + + data = QByteArray("abcd\0\1\2\abcd",12); + QTest::newRow("with-nul") << md5Url << data << QByteArray("POST") << dummyObject + << md5sum(data) << QNetworkProxy(); + + data = QByteArray(4097, '\4'); + QTest::newRow("4k+1") << md5Url << data << QByteArray("POST") << dummyObject + << md5sum(data)<< QNetworkProxy(); + + QHostInfo hostInfo = QHostInfo::fromName(QtNetworkSettings::serverName()); + QString proxyserver = hostInfo.addresses().first().toString(); + + QTest::newRow("4k+1-with-http-proxy") << md5Url << data << QByteArray("POST") << dummyObject + << md5sum(data) + << QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3128); + + QTest::newRow("4k+1-with-http-proxy-auth") << md5Url << data << QByteArray("POST") << dummyObject + << md5sum(data) + << QNetworkProxy(QNetworkProxy::HttpProxy, + proxyserver, 3129); + + QTest::newRow("4k+1-with-socks-proxy") << md5Url << data << QByteArray("POST") << dummyObject + << md5sum(data) + << QNetworkProxy(QNetworkProxy::Socks5Proxy, proxyserver, 1080); + + QTest::newRow("4k+1-with-socks-proxy-auth") << md5Url << data << QByteArray("POST") << dummyObject + << md5sum(data) + << QNetworkProxy(QNetworkProxy::Socks5Proxy, + proxyserver, 1081); + + data = QByteArray(128*1024+1, '\177'); + QTest::newRow("128k+1") << md5Url << data << QByteArray("POST") << dummyObject + << md5sum(data) << QNetworkProxy(); + + data = QByteArray(128*1024+1, '\177'); + QTest::newRow("128k+1-put") << md5Url << data << QByteArray("PUT") << dummyObject + << md5sum(data) << QNetworkProxy(); + + data = QByteArray(2*1024*1024+1, '\177'); + QTest::newRow("2MB+1") << md5Url << data << QByteArray("POST") << dummyObject + << md5sum(data) << QNetworkProxy(); + + + // 2. test uploading of files + + QFile *file = new QFile(QFINDTESTDATA("../qnetworkreply/rfc3252.txt")); + file->open(QIODevice::ReadOnly); + QTest::newRow("file-26K") << md5Url << QByteArray() << QByteArray("POST") + << static_cast<QObject *>(file) + << QByteArray("b3e32ac459b99d3f59318f3ac31e4bee\n") << QNetworkProxy(); + + QFile *file2 = new QFile(QFINDTESTDATA("../qnetworkreply/image1.jpg")); + file2->open(QIODevice::ReadOnly); + QTest::newRow("file-1MB") << md5Url << QByteArray() << QByteArray("POST") + << static_cast<QObject *>(file2) + << QByteArray("87ef3bb319b004ba9e5e9c9fa713776e\n") << QNetworkProxy(); + + + // 3. test uploading of multipart + + QUrl multiPartUrl("https://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/multipart.cgi"); + + QHttpPart imagePart31; + imagePart31.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); + imagePart31.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage1\"")); + imagePart31.setRawHeader("Content-Location", "http://my.test.location.tld"); + imagePart31.setRawHeader("Content-ID", "my@id.tld"); + QFile *file31 = new QFile(QFINDTESTDATA("../qnetworkreply/image1.jpg")); + file31->open(QIODevice::ReadOnly); + imagePart31.setBodyDevice(file31); + QHttpMultiPart *imageMultiPart3 = new QHttpMultiPart(QHttpMultiPart::FormDataType); + imageMultiPart3->append(imagePart31); + file31->setParent(imageMultiPart3); + QHttpPart imagePart32; + imagePart32.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); + imagePart32.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage2\"")); + QFile *file32 = new QFile(QFINDTESTDATA("../qnetworkreply/image2.jpg")); + file32->open(QIODevice::ReadOnly); + imagePart32.setBodyDevice(file31); // check that resetting works + imagePart32.setBodyDevice(file32); + imageMultiPart3->append(imagePart32); + file32->setParent(imageMultiPart3); + QHttpPart imagePart33; + imagePart33.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); + imagePart33.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage3\"")); + QFile *file33 = new QFile(QFINDTESTDATA("../qnetworkreply/image3.jpg")); + file33->open(QIODevice::ReadOnly); + imagePart33.setBodyDevice(file33); + imageMultiPart3->append(imagePart33); + file33->setParent(imageMultiPart3); + QByteArray expectedData = "content type: multipart/form-data; boundary=\"" + + imageMultiPart3->boundary(); + expectedData.append("\"\nkey: testImage1, value: 87ef3bb319b004ba9e5e9c9fa713776e\n" + "key: testImage2, value: 483761b893f7fb1bd2414344cd1f3dfb\n" + "key: testImage3, value: ab0eb6fd4fcf8b4436254870b4513033\n"); + + QTest::newRow("multipart-3images") << multiPartUrl << QByteArray() << QByteArray("POST") + << static_cast<QObject *>(imageMultiPart3) << expectedData + << QNetworkProxy(); +} + +void tst_Spdy::upload() +{ + QFETCH(QUrl, url); + QNetworkRequest request(url); + request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true); + + QFETCH(QByteArray, data); + QFETCH(QByteArray, uploadMethod); + QFETCH(QObject *, uploadObject); + QFETCH(QNetworkProxy, proxy); + + if (proxy.type() != QNetworkProxy::DefaultProxy) { + m_manager.setProxy(proxy); + } + + QNetworkReply *reply; + QHttpMultiPart *multiPart = 0; + + if (uploadObject) { + // upload via device + if (QIODevice *device = qobject_cast<QIODevice *>(uploadObject)) { + reply = m_manager.post(request, device); + } else if ((multiPart = qobject_cast<QHttpMultiPart *>(uploadObject))) { + reply = m_manager.post(request, multiPart); + } else { + QFAIL("got unknown upload device"); + } + } else { + // upload via byte array + if (uploadMethod == "PUT") { + reply = m_manager.put(request, data); + } else { + reply = m_manager.post(request, data); + } + } + + reply->ignoreSslErrors(); + QSignalSpy metaDataChangedSpy(reply, SIGNAL(metaDataChanged())); + QSignalSpy uploadProgressSpy(reply, SIGNAL(uploadProgress(qint64, qint64))); + QSignalSpy readyReadSpy(reply, SIGNAL(readyRead())); + QSignalSpy finishedSpy(reply, SIGNAL(finished())); + + QObject::connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QSignalSpy finishedManagerSpy(&m_manager, SIGNAL(finished(QNetworkReply*))); + + QTestEventLoop::instance().enterLoop(20); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(finishedManagerSpy.count(), 1); + QCOMPARE(metaDataChangedSpy.count(), 1); + QCOMPARE(finishedSpy.count(), 1); + QVERIFY(uploadProgressSpy.count() > 0); + QVERIFY(readyReadSpy.count() > 0); + + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->attribute(QNetworkRequest::SpdyWasUsedAttribute).toBool(), true); + QCOMPARE(reply->attribute(QNetworkRequest::ConnectionEncryptedAttribute).toBool(), true); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + + qint64 contentLength = reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(); + if (!multiPart) // script to test multiparts does not return a content length + QCOMPARE(contentLength, 33); // 33 bytes for md5 sums (including new line) + + QFETCH(QByteArray, md5sum); + QByteArray content = reply->readAll(); + QCOMPARE(content, md5sum); + + reply->deleteLater(); + if (uploadObject) + uploadObject->deleteLater(); + + m_manager.setProxy(QNetworkProxy()); // reset +} + +void tst_Spdy::errors_data() +{ + QTest::addColumn<QUrl>("url"); + QTest::addColumn<QNetworkProxy>("proxy"); + QTest::addColumn<bool>("ignoreSslErrors"); + QTest::addColumn<int>("expectedReplyError"); + + QTest::newRow("http-404") << QUrl("https://" + QtNetworkSettings::serverName() + "/non-existent-url") + << QNetworkProxy() << true << int(QNetworkReply::ContentNotFoundError); + + QTest::newRow("ssl-errors") << QUrl("https://" + QtNetworkSettings::serverName()) + << QNetworkProxy() << false << int(QNetworkReply::SslHandshakeFailedError); + + QTest::newRow("host-not-found") << QUrl("https://this-host-does-not.exist") + << QNetworkProxy() + << true << int(QNetworkReply::HostNotFoundError); + + QTest::newRow("proxy-not-found") << QUrl("https://" + QtNetworkSettings::serverName()) + << QNetworkProxy(QNetworkProxy::HttpProxy, + "https://this-host-does-not.exist", 3128) + << true << int(QNetworkReply::HostNotFoundError); + + QHostInfo hostInfo = QHostInfo::fromName(QtNetworkSettings::serverName()); + QString proxyserver = hostInfo.addresses().first().toString(); + + QTest::newRow("proxy-unavailable") << QUrl("https://" + QtNetworkSettings::serverName()) + << QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 10) + << true << int(QNetworkReply::UnknownNetworkError); + + QTest::newRow("no-proxy-credentials") << QUrl("https://" + QtNetworkSettings::serverName()) + << QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3129) + << true << int(QNetworkReply::ProxyAuthenticationRequiredError); +} + +void tst_Spdy::errors() +{ + QFETCH(QUrl, url); + QFETCH(QNetworkProxy, proxy); + QFETCH(bool, ignoreSslErrors); + QFETCH(int, expectedReplyError); + + QNetworkRequest request(url); + request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true); + + disconnect(&m_manager, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), + 0, 0); + if (proxy.type() != QNetworkProxy::DefaultProxy) { + m_manager.setProxy(proxy); + } + QNetworkReply *reply = m_manager.get(request); + if (ignoreSslErrors) + reply->ignoreSslErrors(); + QSignalSpy finishedSpy(reply, SIGNAL(finished())); + QSignalSpy errorSpy(reply, SIGNAL(error(QNetworkReply::NetworkError))); + + QObject::connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + + QTestEventLoop::instance().enterLoop(15); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(errorSpy.count(), 1); + + QCOMPARE(reply->error(), static_cast<QNetworkReply::NetworkError>(expectedReplyError)); + + m_manager.setProxy(QNetworkProxy()); // reset + m_manager.clearAccessCache(); // e.g. to get an SSL error we need a new connection + connect(&m_manager, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), + this, SLOT(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), + Qt::UniqueConnection); // reset +} + +void tst_Spdy::multipleRequests_data() +{ + QTest::addColumn<QList<QUrl> >("urls"); + + QString baseUrl = "https://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/echo.cgi?"; + QList<QUrl> urls; + for (int a = 1; a <= 50; ++a) + urls.append(QUrl(baseUrl + QLatin1String(QByteArray::number(a)))); + + QTest::newRow("one-request") << urls.mid(0, 1); + QTest::newRow("two-requests") << urls.mid(0, 2); + QTest::newRow("ten-requests") << urls.mid(0, 10); + QTest::newRow("twenty-requests") << urls.mid(0, 20); + QTest::newRow("fifty-requests") << urls; +} + +void tst_Spdy::multipleRequestsFinishedSlot() +{ + m_multipleRepliesFinishedCount++; + if (m_multipleRepliesFinishedCount == m_multipleRequestsCount) + QTestEventLoop::instance().exitLoop(); +} + +void tst_Spdy::multipleRequests() +{ + QFETCH(QList<QUrl>, urls); + m_multipleRequestsCount = urls.count(); + m_multipleRepliesFinishedCount = 0; + + QList<QNetworkReply *> replies; + QList<QSignalSpy *> metaDataChangedSpies; + QList<QSignalSpy *> readyReadSpies; + QList<QSignalSpy *> finishedSpies; + + foreach (const QUrl &url, urls) { + QNetworkRequest request(url); + request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true); + QNetworkReply *reply = m_manager.get(request); + replies.append(reply); + reply->ignoreSslErrors(); + QObject::connect(reply, SIGNAL(finished()), this, SLOT(multipleRequestsFinishedSlot())); + QSignalSpy *metaDataChangedSpy = new QSignalSpy(reply, SIGNAL(metaDataChanged())); + metaDataChangedSpies << metaDataChangedSpy; + QSignalSpy *readyReadSpy = new QSignalSpy(reply, SIGNAL(readyRead())); + readyReadSpies << readyReadSpy; + QSignalSpy *finishedSpy = new QSignalSpy(reply, SIGNAL(finished())); + finishedSpies << finishedSpy; + } + + QSignalSpy finishedManagerSpy(&m_manager, SIGNAL(finished(QNetworkReply*))); + + QTestEventLoop::instance().enterLoop(15); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(finishedManagerSpy.count(), m_multipleRequestsCount); + + for (int a = 0; a < replies.count(); ++a) { + +#ifndef QT_NO_OPENSSL + QCOMPARE(replies.at(a)->sslConfiguration().nextProtocolNegotiationStatus(), + QSslConfiguration::NextProtocolNegotiationNegotiated); + QCOMPARE(replies.at(a)->sslConfiguration().nextNegotiatedProtocol(), + QByteArray(QSslConfiguration::NextProtocolSpdy3_0)); +#endif // QT_NO_OPENSSL + + QCOMPARE(replies.at(a)->error(), QNetworkReply::NoError); + QCOMPARE(replies.at(a)->attribute(QNetworkRequest::SpdyWasUsedAttribute).toBool(), true); + QCOMPARE(replies.at(a)->attribute(QNetworkRequest::ConnectionEncryptedAttribute).toBool(), true); + QCOMPARE(replies.at(a)->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + + // using the echo script, a request to "echo.cgi?1" will return a body of "1" + QByteArray expectedContent = replies.at(a)->url().query().toUtf8(); + QByteArray content = replies.at(a)->readAll(); + QCOMPARE(expectedContent, content); + + QCOMPARE(metaDataChangedSpies.at(a)->count(), 1); + metaDataChangedSpies.at(a)->deleteLater(); + + QCOMPARE(finishedSpies.at(a)->count(), 1); + finishedSpies.at(a)->deleteLater(); + + QVERIFY(readyReadSpies.at(a)->count() > 0); + readyReadSpies.at(a)->deleteLater(); + + replies.at(a)->deleteLater(); + } +} + +QTEST_MAIN(tst_Spdy) + +#include "tst_spdy.moc" |