7aa1a7f1c1
Task-number: QTBUG-37171 Change-Id: I835764978cf75592d46a20fa5f644f6accec43f5 Reviewed-by: Andrew Knight <andrew.knight@digia.com>
705 lines
29 KiB
C++
705 lines
29 KiB
C++
/****************************************************************************
|
|
**
|
|
** 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>
|
|
#if defined(QT_BUILD_INTERNAL) && !defined(QT_NO_OPENSSL)
|
|
#include <QtNetwork/private/qsslsocket_openssl_p.h>
|
|
#endif // QT_BUILD_INTERNAL && !QT_NO_OPENSSL
|
|
|
|
#include "../../../network-settings.h"
|
|
|
|
Q_DECLARE_METATYPE(QAuthenticator*)
|
|
|
|
class tst_Spdy: public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
tst_Spdy();
|
|
~tst_Spdy();
|
|
|
|
private Q_SLOTS:
|
|
void initTestCase();
|
|
void settingsAndNegotiation_data();
|
|
void settingsAndNegotiation();
|
|
#ifndef QT_NO_NETWORKPROXY
|
|
void download_data();
|
|
void download();
|
|
#endif // !QT_NO_NETWORKPROXY
|
|
void headerFields();
|
|
#ifndef QT_NO_NETWORKPROXY
|
|
void upload_data();
|
|
void upload();
|
|
void errors_data();
|
|
void errors();
|
|
#endif // !QT_NO_NETWORKPROXY
|
|
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::initTestCase()
|
|
{
|
|
QVERIFY(QtNetworkSettings::verifyTestNetworkSettings());
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
#ifndef QT_NO_NETWORKPROXY
|
|
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
|
|
}
|
|
#endif // !QT_NO_NETWORKPROXY
|
|
|
|
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');
|
|
}
|
|
|
|
#ifndef QT_NO_NETWORKPROXY
|
|
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
|
|
}
|
|
#endif // !QT_NO_NETWORKPROXY
|
|
|
|
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"
|