qt5base-lts/tests/auto/qhttp/tst_qhttp.cpp
Qt by Nokia 38be0d1383 Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you
want to look at revision history older than this, please refer to the
Qt Git wiki for how to use Git history grafting. At the time of
writing, this wiki is located here:

http://qt.gitorious.org/qt/pages/GitIntroductionWithQt

If you have already performed the grafting and you don't see any
history beyond this commit, try running "git log" with the "--follow"
argument.

Branched from the monolithic repo, Qt master branch, at commit
896db169ea224deb96c59ce8af800d019de63f12
2011-04-27 12:05:43 +02:00

1576 lines
50 KiB
C++

/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** 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, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <qbuffer.h>
#include <qcoreapplication.h>
#include <qfile.h>
#include <qhostinfo.h>
#include <qhttp.h>
#include <qlist.h>
#include <qpointer.h>
#include <qtcpsocket.h>
#include <qtcpserver.h>
#include <qauthenticator.h>
#include <QNetworkProxy>
#ifndef QT_NO_OPENSSL
# include <qsslsocket.h>
#endif
#include "../network-settings.h"
//TESTED_CLASS=
//TESTED_FILES=
#ifdef Q_OS_SYMBIAN
// In Symbian OS test data is located in applications private dir
// And underlying Open C have application private dir in default search path
#define SRCDIR ""
#endif
Q_DECLARE_METATYPE(QHttpResponseHeader)
class tst_QHttp : public QObject
{
Q_OBJECT
public:
tst_QHttp();
virtual ~tst_QHttp();
public slots:
void initTestCase_data();
void initTestCase();
void cleanupTestCase();
void init();
void cleanup();
private slots:
void constructing();
void invalidRequests();
void get_data();
void get();
void head_data();
void head();
void post_data();
void post();
void request_data();
void request();
void authorization_data();
void authorization();
void proxy_data();
void proxy();
void proxy2();
void proxy3();
void postAuthNtlm();
void proxyAndSsl();
void cachingProxyAndSsl();
void reconnect();
void setSocket();
void unexpectedRemoteClose();
void pctEncodedPath();
void caseInsensitiveKeys();
void emptyBodyInReply();
void abortInReadyRead();
void abortInResponseHeaderReceived();
void nestedEventLoop();
void connectionClose();
protected slots:
void stateChanged( int );
void responseHeaderReceived( const QHttpResponseHeader & );
void readyRead( const QHttpResponseHeader& );
void dataSendProgress( int, int );
void dataReadProgress( int , int );
void requestStarted( int );
void requestFinished( int, bool );
void done( bool );
void reconnect_state(int state);
void proxy2_slot();
void nestedEventLoop_slot(int id);
void abortSender();
void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth);
private:
QHttp *newHttp(bool withAuth = false);
void addRequest( QHttpRequestHeader, int );
bool headerAreEqual( const QHttpHeader&, const QHttpHeader& );
QHttp *http;
struct RequestResult
{
QHttpRequestHeader req;
QHttpResponseHeader resp;
int success;
};
QMap< int, RequestResult > resultMap;
typedef QMap<int,RequestResult>::Iterator ResMapIt;
QList<int> ids; // helper to make sure that all expected signals are emitted
int current_id;
int cur_state;
int done_success;
QByteArray readyRead_ba;
int bytesTotalSend;
int bytesDoneSend;
int bytesTotalRead;
int bytesDoneRead;
int getId;
int headId;
int postId;
int reconnect_state_connect_count;
bool connectionWithAuth;
bool proxyAuthCalled;
};
class ClosingServer: public QTcpServer
{
Q_OBJECT
public:
ClosingServer()
{
connect(this, SIGNAL(newConnection()), SLOT(handleConnection()));
listen();
}
public slots:
void handleConnection()
{
delete nextPendingConnection();
}
};
//#define DUMP_SIGNALS
const int bytesTotal_init = -10;
const int bytesDone_init = -10;
tst_QHttp::tst_QHttp()
{
Q_SET_DEFAULT_IAP
}
tst_QHttp::~tst_QHttp()
{
}
void tst_QHttp::initTestCase_data()
{
QTest::addColumn<bool>("setProxy");
QTest::addColumn<int>("proxyType");
QTest::newRow("WithoutProxy") << false << 0;
QTest::newRow("WithSocks5Proxy") << true << int(QNetworkProxy::Socks5Proxy);
}
void tst_QHttp::initTestCase()
{
}
void tst_QHttp::cleanupTestCase()
{
}
void tst_QHttp::init()
{
QFETCH_GLOBAL(bool, setProxy);
if (setProxy) {
QFETCH_GLOBAL(int, proxyType);
if (proxyType == QNetworkProxy::Socks5Proxy) {
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080));
}
}
http = 0;
resultMap.clear();
ids.clear();
current_id = 0;
cur_state = QHttp::Unconnected;
done_success = -1;
readyRead_ba = QByteArray();
getId = -1;
headId = -1;
postId = -1;
}
void tst_QHttp::cleanup()
{
delete http;
http = 0;
QCoreApplication::processEvents();
QFETCH_GLOBAL(bool, setProxy);
if (setProxy) {
QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy);
}
}
void tst_QHttp::constructing()
{
//QHeader
{
QHttpRequestHeader header;
header.addValue("key1", "val1");
header.addValue("key2", "val2");
header.addValue("key1", "val3");
QCOMPARE(header.values().size(), 3);
QCOMPARE(header.allValues("key1").size(), 2);
QVERIFY(header.hasKey("key2"));
QCOMPARE(header.keys().count(), 2);
}
{
QHttpResponseHeader header(200);
}
}
void tst_QHttp::invalidRequests()
{
QHttp http;
http.setHost("localhost"); // we will not actually connect
QTest::ignoreMessage(QtWarningMsg, "QHttp: empty path requested is invalid -- using '/'");
http.get(QString());
QTest::ignoreMessage(QtWarningMsg, "QHttp: empty path requested is invalid -- using '/'");
http.head(QString());
QTest::ignoreMessage(QtWarningMsg, "QHttp: empty path requested is invalid -- using '/'");
http.post(QString(), QByteArray());
QTest::ignoreMessage(QtWarningMsg, "QHttp: empty path requested is invalid -- using '/'");
http.request(QHttpRequestHeader("PROPFIND", QString()));
}
void tst_QHttp::get_data()
{
QTest::addColumn<QString>("host");
QTest::addColumn<uint>("port");
QTest::addColumn<QString>("path");
QTest::addColumn<int>("success");
QTest::addColumn<int>("statusCode");
QTest::addColumn<QByteArray>("res");
QTest::addColumn<bool>("useIODevice");
// ### move this into external testdata
QFile file( SRCDIR "rfc3252.txt" );
QVERIFY( file.open( QIODevice::ReadOnly ) );
QByteArray rfc3252 = file.readAll();
file.close();
file.setFileName( SRCDIR "trolltech" );
QVERIFY( file.open( QIODevice::ReadOnly ) );
QByteArray trolltech = file.readAll();
file.close();
// test the two get() modes in one routine
for ( int i=0; i<2; i++ ) {
QTest::newRow(QString("path_01_%1").arg(i).toLatin1()) << QtNetworkSettings::serverName() << 80u
<< QString("/qtest/rfc3252.txt") << 1 << 200 << rfc3252 << (bool)(i==1);
QTest::newRow( QString("path_02_%1").arg(i).toLatin1() ) << QString("www.ietf.org") << 80u
<< QString("/rfc/rfc3252.txt") << 1 << 200 << rfc3252 << (bool)(i==1);
QTest::newRow( QString("uri_01_%1").arg(i).toLatin1() ) << QtNetworkSettings::serverName() << 80u
<< QString("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt") << 1 << 200 << rfc3252 << (bool)(i==1);
QTest::newRow( QString("uri_02_%1").arg(i).toLatin1() ) << "www.ietf.org" << 80u
<< QString("http://www.ietf.org/rfc/rfc3252.txt") << 1 << 200 << rfc3252 << (bool)(i==1);
QTest::newRow( QString("fail_01_%1").arg(i).toLatin1() ) << QString("this-host-will-not-exist.") << 80u
<< QString("/qtest/rfc3252.txt") << 0 << 0 << QByteArray() << (bool)(i==1);
QTest::newRow( QString("failprot_01_%1").arg(i).toLatin1() ) << QtNetworkSettings::serverName() << 80u
<< QString("/t") << 1 << 404 << QByteArray() << (bool)(i==1);
QTest::newRow( QString("failprot_02_%1").arg(i).toLatin1() ) << QtNetworkSettings::serverName() << 80u
<< QString("qtest/rfc3252.txt") << 1 << 400 << QByteArray() << (bool)(i==1);
// qt.nokia.com/doc uses transfer-encoding=chunked
/* qt.nokia.com/doc no longer seams to be using chuncked encodig.
QTest::newRow( QString("chunked_01_%1").arg(i).toLatin1() ) << QString("test.troll.no") << 80u
<< QString("/") << 1 << 200 << trolltech << (bool)(i==1);
*/
QTest::newRow( QString("chunked_02_%1").arg(i).toLatin1() ) << QtNetworkSettings::serverName() << 80u
<< QString("/qtest/cgi-bin/rfc.cgi") << 1 << 200 << rfc3252 << (bool)(i==1);
}
}
void tst_QHttp::get()
{
// for the overload that takes a QIODevice
QByteArray buf_ba;
QBuffer buf( &buf_ba );
QFETCH( QString, host );
QFETCH( uint, port );
QFETCH( QString, path );
QFETCH( bool, useIODevice );
http = newHttp();
QCOMPARE( http->currentId(), 0 );
QCOMPARE( (int)http->state(), (int)QHttp::Unconnected );
addRequest( QHttpRequestHeader(), http->setHost( host, port ) );
if ( useIODevice ) {
buf.open( QIODevice::WriteOnly );
getId = http->get( path, &buf );
} else {
getId = http->get( path );
}
addRequest( QHttpRequestHeader(), getId );
QTestEventLoop::instance().enterLoop( 50 );
if ( QTestEventLoop::instance().timeout() )
QFAIL( "Network operation timed out" );
ResMapIt res = resultMap.find( getId );
QVERIFY( res != resultMap.end() );
if ( res.value().success!=1 && host=="www.ietf.org" ) {
// The nightly tests fail from time to time. In order to make them more
// stable, add some debug output that might help locate the problem (I
// can't reproduce the problem in the non-nightly builds).
qDebug( "Error %d: %s", http->error(), http->errorString().toLatin1().constData() );
}
QTEST( res.value().success, "success" );
if ( res.value().success ) {
QTEST( res.value().resp.statusCode(), "statusCode" );
QFETCH( QByteArray, res );
if ( res.count() > 0 ) {
if ( useIODevice ) {
QCOMPARE(buf_ba, res);
if ( bytesDoneRead != bytesDone_init )
QVERIFY( (int)buf_ba.size() == bytesDoneRead );
} else {
QCOMPARE(readyRead_ba, res);
if ( bytesDoneRead != bytesDone_init )
QVERIFY( (int)readyRead_ba.size() == bytesDoneRead );
}
}
QVERIFY( bytesTotalRead != bytesTotal_init );
if ( bytesTotalRead > 0 )
QVERIFY( bytesDoneRead == bytesTotalRead );
} else {
QVERIFY( !res.value().resp.isValid() );
}
}
void tst_QHttp::head_data()
{
QTest::addColumn<QString>("host");
QTest::addColumn<uint>("port");
QTest::addColumn<QString>("path");
QTest::addColumn<int>("success");
QTest::addColumn<int>("statusCode");
QTest::addColumn<uint>("contentLength");
QTest::newRow( "path_01" ) << QtNetworkSettings::serverName() << 80u
<< QString("/qtest/rfc3252.txt") << 1 << 200 << 25962u;
QTest::newRow( "path_02" ) << QString("www.ietf.org") << 80u
<< QString("/rfc/rfc3252.txt") << 1 << 200 << 25962u;
QTest::newRow( "uri_01" ) << QtNetworkSettings::serverName() << 80u
<< QString("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt") << 1 << 200 << 25962u;
QTest::newRow( "uri_02" ) << QString("www.ietf.org") << 80u
<< QString("http://www.ietf.org/rfc/rfc3252.txt") << 1 << 200 << 25962u;
QTest::newRow( "fail_01" ) << QString("this-host-will-not-exist.") << 80u
<< QString("/qtest/rfc3252.txt") << 0 << 0 << 0u;
QTest::newRow( "failprot_01" ) << QtNetworkSettings::serverName() << 80u
<< QString("/t") << 1 << 404 << 0u;
QTest::newRow( "failprot_02" ) << QtNetworkSettings::serverName() << 80u
<< QString("qtest/rfc3252.txt") << 1 << 400 << 0u;
/* qt.nokia.com/doc no longer seams to be using chuncked encodig.
QTest::newRow( "chunked_01" ) << QString("qt.nokia.com/doc") << 80u
<< QString("/index.html") << 1 << 200 << 0u;
*/
QTest::newRow( "chunked_02" ) << QtNetworkSettings::serverName() << 80u
<< QString("/qtest/cgi-bin/rfc.cgi") << 1 << 200 << 0u;
}
void tst_QHttp::head()
{
QFETCH( QString, host );
QFETCH( uint, port );
QFETCH( QString, path );
http = newHttp();
QCOMPARE( http->currentId(), 0 );
QCOMPARE( (int)http->state(), (int)QHttp::Unconnected );
addRequest( QHttpRequestHeader(), http->setHost( host, port ) );
headId = http->head( path );
addRequest( QHttpRequestHeader(), headId );
QTestEventLoop::instance().enterLoop( 30 );
if ( QTestEventLoop::instance().timeout() )
QFAIL( "Network operation timed out" );
ResMapIt res = resultMap.find( headId );
QVERIFY( res != resultMap.end() );
if ( res.value().success!=1 && host=="www.ietf.org" ) {
// The nightly tests fail from time to time. In order to make them more
// stable, add some debug output that might help locate the problem (I
// can't reproduce the problem in the non-nightly builds).
qDebug( "Error %d: %s", http->error(), http->errorString().toLatin1().constData() );
}
QTEST( res.value().success, "success" );
if ( res.value().success ) {
QTEST( res.value().resp.statusCode(), "statusCode" );
QTEST( res.value().resp.contentLength(), "contentLength" );
QCOMPARE( (uint)readyRead_ba.size(), 0u );
QVERIFY( bytesTotalRead == bytesTotal_init );
QVERIFY( bytesDoneRead == bytesDone_init );
} else {
QVERIFY( !res.value().resp.isValid() );
}
}
void tst_QHttp::post_data()
{
QTest::addColumn<QString>("source");
QTest::addColumn<bool>("useIODevice");
QTest::addColumn<bool>("useProxy");
QTest::addColumn<QString>("host");
QTest::addColumn<int>("port");
QTest::addColumn<bool>("ssl");
QTest::addColumn<QString>("path");
QTest::addColumn<QByteArray>("result");
QByteArray md5sum;
md5sum = "d41d8cd98f00b204e9800998ecf8427e";
QTest::newRow("empty-data")
<< QString() << false << false
<< QtNetworkSettings::serverName() << 80 << false << "/qtest/cgi-bin/md5sum.cgi" << md5sum;
QTest::newRow("empty-device")
<< QString() << true << false
<< QtNetworkSettings::serverName() << 80 << false << "/qtest/cgi-bin/md5sum.cgi" << md5sum;
QTest::newRow("proxy-empty-data")
<< QString() << false << true
<< QtNetworkSettings::serverName() << 80 << false << "/qtest/cgi-bin/md5sum.cgi" << md5sum;
md5sum = "b3e32ac459b99d3f59318f3ac31e4bee";
QTest::newRow("data") << "rfc3252.txt" << false << false
<< QtNetworkSettings::serverName() << 80 << false << "/qtest/cgi-bin/md5sum.cgi"
<< md5sum;
QTest::newRow("device") << "rfc3252.txt" << true << false
<< QtNetworkSettings::serverName() << 80 << false << "/qtest/cgi-bin/md5sum.cgi"
<< md5sum;
QTest::newRow("proxy-data") << "rfc3252.txt" << false << true
<< QtNetworkSettings::serverName() << 80 << false << "/qtest/cgi-bin/md5sum.cgi"
<< md5sum;
#ifndef QT_NO_OPENSSL
md5sum = "d41d8cd98f00b204e9800998ecf8427e";
QTest::newRow("empty-data-ssl")
<< QString() << false << false
<< QtNetworkSettings::serverName() << 443 << true << "/qtest/cgi-bin/md5sum.cgi" << md5sum;
QTest::newRow("empty-device-ssl")
<< QString() << true << false
<< QtNetworkSettings::serverName() << 443 << true << "/qtest/cgi-bin/md5sum.cgi" << md5sum;
QTest::newRow("proxy-empty-data-ssl")
<< QString() << false << true
<< QtNetworkSettings::serverName() << 443 << true << "/qtest/cgi-bin/md5sum.cgi" << md5sum;
md5sum = "b3e32ac459b99d3f59318f3ac31e4bee";
QTest::newRow("data-ssl") << "rfc3252.txt" << false << false
<< QtNetworkSettings::serverName() << 443 << true << "/qtest/cgi-bin/md5sum.cgi"
<< md5sum;
QTest::newRow("device-ssl") << "rfc3252.txt" << true << false
<< QtNetworkSettings::serverName() << 443 << true << "/qtest/cgi-bin/md5sum.cgi"
<< md5sum;
QTest::newRow("proxy-data-ssl") << "rfc3252.txt" << false << true
<< QtNetworkSettings::serverName() << 443 << true << "/qtest/cgi-bin/md5sum.cgi"
<< md5sum;
#endif
// the following test won't work. See task 185996
/*
QTest::newRow("proxy-device") << "rfc3252.txt" << true << true
<< QtNetworkSettings::serverName() << 80 << "/qtest/cgi-bin/md5sum.cgi"
<< md5sum;
*/
}
void tst_QHttp::post()
{
QFETCH(QString, source);
QFETCH(bool, useIODevice);
QFETCH(bool, useProxy);
QFETCH(QString, host);
QFETCH(int, port);
QFETCH(bool, ssl);
QFETCH(QString, path);
http = newHttp(useProxy);
#ifndef QT_NO_OPENSSL
QObject::connect(http, SIGNAL(sslErrors(const QList<QSslError> &)),
http, SLOT(ignoreSslErrors()));
#endif
QCOMPARE(http->currentId(), 0);
QCOMPARE((int)http->state(), (int)QHttp::Unconnected);
if (useProxy)
addRequest(QHttpRequestHeader(), http->setProxy(QtNetworkSettings::serverName(), 3129));
addRequest(QHttpRequestHeader(), http->setHost(host, (ssl ? QHttp::ConnectionModeHttps : QHttp::ConnectionModeHttp), port));
// add the POST request
QFile file(SRCDIR + source);
QBuffer emptyBuffer;
QIODevice *dev;
if (!source.isEmpty()) {
QVERIFY(file.open(QIODevice::ReadOnly));
dev = &file;
} else {
emptyBuffer.open(QIODevice::ReadOnly);
dev = &emptyBuffer;
}
if (useIODevice)
postId = http->post(path, dev);
else
postId = http->post(path, dev->readAll());
addRequest(QHttpRequestHeader(), postId);
// run request
connect(http, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
QTestEventLoop::instance().enterLoop( 30 );
if ( QTestEventLoop::instance().timeout() )
QFAIL( "Network operation timed out" );
ResMapIt res = resultMap.find(postId);
QVERIFY(res != resultMap.end());
QVERIFY(res.value().success);
QCOMPARE(res.value().resp.statusCode(), 200);
QTEST(readyRead_ba.trimmed(), "result");
}
void tst_QHttp::request_data()
{
QTest::addColumn<QString>("source");
QTest::addColumn<bool>("useIODevice");
QTest::addColumn<bool>("useProxy");
QTest::addColumn<QString>("host");
QTest::addColumn<int>("port");
QTest::addColumn<QString>("method");
QTest::addColumn<QString>("path");
QTest::addColumn<QByteArray>("result");
QFile source(SRCDIR "rfc3252.txt");
if (!source.open(QIODevice::ReadOnly))
return;
QByteArray contents = source.readAll();
QByteArray md5sum = QCryptographicHash::hash(contents, QCryptographicHash::Md5).toHex() + '\n';
QByteArray emptyMd5sum = "d41d8cd98f00b204e9800998ecf8427e\n";
QTest::newRow("head") << QString() << false << false << QtNetworkSettings::serverName() << 80
<< "HEAD" << "/qtest/rfc3252.txt"
<< QByteArray();
QTest::newRow("get") << QString() << false << false << QtNetworkSettings::serverName() << 80
<< "GET" << "/qtest/rfc3252.txt"
<< contents;
QTest::newRow("post-empty-data") << QString() << false << false
<< QtNetworkSettings::serverName() << 80 << "POST" << "/qtest/cgi-bin/md5sum.cgi"
<< emptyMd5sum;
QTest::newRow("post-empty-device") << QString() << true << false
<< QtNetworkSettings::serverName() << 80 << "POST" << "/qtest/cgi-bin/md5sum.cgi"
<< emptyMd5sum;
QTest::newRow("post-data") << "rfc3252.txt" << false << false
<< QtNetworkSettings::serverName() << 80 << "POST" << "/qtest/cgi-bin/md5sum.cgi"
<< md5sum;
QTest::newRow("post-device") << "rfc3252.txt" << true << false
<< QtNetworkSettings::serverName() << 80 << "POST" << "/qtest/cgi-bin/md5sum.cgi"
<< md5sum;
QTest::newRow("proxy-head") << QString() << false << true << QtNetworkSettings::serverName() << 80
<< "HEAD" << "/qtest/rfc3252.txt"
<< QByteArray();
QTest::newRow("proxy-get") << QString() << false << true << QtNetworkSettings::serverName() << 80
<< "GET" << "/qtest/rfc3252.txt"
<< contents;
QTest::newRow("proxy-post-empty-data") << QString() << false << true
<< QtNetworkSettings::serverName() << 80 << "POST" << "/qtest/cgi-bin/md5sum.cgi"
<< emptyMd5sum;
QTest::newRow("proxy-post-data") << "rfc3252.txt" << false << true
<< QtNetworkSettings::serverName() << 80 << "POST" << "/qtest/cgi-bin/md5sum.cgi"
<< md5sum;
// the following test won't work. See task 185996
/*
QTest::newRow("proxy-post-device") << "rfc3252.txt" << true << true
<< QtNetworkSettings::serverName() << 80 << "POST" << "/qtest/cgi-bin/md5sum.cgi"
<< md5sum;
*/
}
void tst_QHttp::request()
{
QFETCH(QString, source);
QFETCH(bool, useIODevice);
QFETCH(bool, useProxy);
QFETCH(QString, host);
QFETCH(int, port);
QFETCH(QString, method);
QFETCH(QString, path);
http = newHttp(useProxy);
QCOMPARE(http->currentId(), 0);
QCOMPARE((int)http->state(), (int)QHttp::Unconnected);
if (useProxy)
addRequest(QHttpRequestHeader(), http->setProxy(QtNetworkSettings::serverName(), 3129));
addRequest(QHttpRequestHeader(), http->setHost(host, port));
QFile file(SRCDIR + source);
QBuffer emptyBuffer;
QIODevice *dev;
if (!source.isEmpty()) {
QVERIFY(file.open(QIODevice::ReadOnly));
dev = &file;
} else {
emptyBuffer.open(QIODevice::ReadOnly);
dev = &emptyBuffer;
}
// prepare the request
QHttpRequestHeader request;
request.setRequest(method, path, 1,1);
request.addValue("Host", host);
int *theId;
if (method == "POST")
theId = &postId;
else if (method == "GET")
theId = &getId;
else if (method == "HEAD")
theId = &headId;
else
QFAIL("You're lazy! Please implement your test!");
// now send the request
if (useIODevice)
*theId = http->request(request, dev);
else
*theId = http->request(request, dev->readAll());
addRequest(QHttpRequestHeader(), *theId);
// run request
connect(http, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
QTestEventLoop::instance().enterLoop( 30 );
if ( QTestEventLoop::instance().timeout() )
QFAIL( "Network operation timed out" );
ResMapIt res = resultMap.find(*theId);
QVERIFY(res != resultMap.end());
QVERIFY(res.value().success);
QCOMPARE(res.value().resp.statusCode(), 200);
QTEST(readyRead_ba, "result");
}
void tst_QHttp::authorization_data()
{
QTest::addColumn<QString>("host");
QTest::addColumn<QString>("path");
QTest::addColumn<QString>("user");
QTest::addColumn<QString>("pass");
QTest::addColumn<int>("result");
QTest::newRow("correct password") << QtNetworkSettings::serverName()
<< QString::fromLatin1("/qtest/rfcs-auth/index.html")
<< QString::fromLatin1("httptest")
<< QString::fromLatin1("httptest")
<< 200;
QTest::newRow("no password") << QtNetworkSettings::serverName()
<< QString::fromLatin1("/qtest/rfcs-auth/index.html")
<< QString::fromLatin1("")
<< QString::fromLatin1("")
<< 401;
QTest::newRow("wrong password") << QtNetworkSettings::serverName()
<< QString::fromLatin1("/qtest/rfcs-auth/index.html")
<< QString::fromLatin1("maliciu0s")
<< QString::fromLatin1("h4X0r")
<< 401;
}
void tst_QHttp::authorization()
{
QFETCH(QString, host);
QFETCH(QString, path);
QFETCH(QString, user);
QFETCH(QString, pass);
QFETCH(int, result);
QEventLoop loop;
QHttp http;
connect(&http, SIGNAL(done(bool)), &loop, SLOT(quit()));
if (!user.isEmpty())
http.setUser(user, pass);
http.setHost(host);
int id = http.get(path);
QTimer::singleShot(5000, &loop, SLOT(quit()));
loop.exec();
QCOMPARE(http.lastResponse().statusCode(), result);
}
void tst_QHttp::proxy_data()
{
QTest::addColumn<QString>("proxyhost");
QTest::addColumn<int>("port");
QTest::addColumn<QString>("host");
QTest::addColumn<QString>("path");
QTest::addColumn<QString>("proxyuser");
QTest::addColumn<QString>("proxypass");
QTest::newRow("qt-test-server") << QtNetworkSettings::serverName() << 3128
<< QString::fromLatin1("qt.nokia.com") << QString::fromLatin1("/")
<< QString::fromLatin1("") << QString::fromLatin1("");
QTest::newRow("qt-test-server pct") << QtNetworkSettings::serverName() << 3128
<< QString::fromLatin1("qt.nokia.com") << QString::fromLatin1("/%64eveloper")
<< QString::fromLatin1("") << QString::fromLatin1("");
QTest::newRow("qt-test-server-basic") << QtNetworkSettings::serverName() << 3129
<< QString::fromLatin1("qt.nokia.com") << QString::fromLatin1("/")
<< QString::fromLatin1("qsockstest") << QString::fromLatin1("password");
#if 0
// NTLM requires sending the same request three times for it to work
// the tst_QHttp class is too strict to handle the byte counts sent by dataSendProgress
// So don't run this test:
QTest::newRow("qt-test-server-ntlm") << QtNetworkSettings::serverName() << 3130
<< QString::fromLatin1("qt.nokia.com") << QString::fromLatin1("/")
<< QString::fromLatin1("qsockstest") << QString::fromLatin1("password");
#endif
}
void tst_QHttp::proxy()
{
QFETCH(QString, proxyhost);
QFETCH(int, port);
QFETCH(QString, host);
QFETCH(QString, path);
QFETCH(QString, proxyuser);
QFETCH(QString, proxypass);
http = newHttp(!proxyuser.isEmpty());
QCOMPARE(http->currentId(), 0);
QCOMPARE((int)http->state(), (int)QHttp::Unconnected);
addRequest(QHttpRequestHeader(), http->setProxy(proxyhost, port, proxyuser, proxypass));
addRequest(QHttpRequestHeader(), http->setHost(host));
getId = http->get(path);
addRequest(QHttpRequestHeader(), getId);
QTestEventLoop::instance().enterLoop(30);
if (QTestEventLoop::instance().timeout())
QFAIL("Network operation timed out");
ResMapIt res = resultMap.find(getId);
QVERIFY(res != resultMap.end());
QVERIFY(res.value().success);
QCOMPARE(res.value().resp.statusCode(), 200);
}
void tst_QHttp::proxy2()
{
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
readyRead_ba.clear();
QHttp http;
http.setProxy(QtNetworkSettings::serverName(), 3128);
http.setHost(QtNetworkSettings::serverName());
http.get("/index.html");
http.get("/index.html");
connect(&http, SIGNAL(requestFinished(int, bool)),
this, SLOT(proxy2_slot()));
QTestEventLoop::instance().enterLoop(30);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(readyRead_ba.count("Welcome to qt-test-server"), 2);
readyRead_ba.clear();
}
void tst_QHttp::proxy2_slot()
{
QHttp *http = static_cast<QHttp *>(sender());
readyRead_ba.append(http->readAll());
if (!http->hasPendingRequests())
QTestEventLoop::instance().exitLoop();
}
void tst_QHttp::proxy3()
{
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
readyRead_ba.clear();
QTcpSocket socket;
socket.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3128));
QHttp http;
http.setSocket(&socket);
http.setHost(QtNetworkSettings::serverName());
http.get("/index.html");
http.get("/index.html");
connect(&http, SIGNAL(requestFinished(int, bool)),
this, SLOT(proxy2_slot()));
QTestEventLoop::instance().enterLoop(30);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(readyRead_ba.count("Welcome to qt-test-server"), 2);
readyRead_ba.clear();
}
// test QHttp::currentId() and QHttp::currentRequest()
#define CURRENTREQUEST_TEST \
{ \
ResMapIt res = resultMap.find( http->currentId() ); \
QVERIFY( res != resultMap.end() ); \
if ( http->currentId() == getId ) { \
QCOMPARE( http->currentRequest().method(), QString("GET") ); \
} else if ( http->currentId() == headId ) { \
QCOMPARE( http->currentRequest().method(), QString("HEAD") ); \
} else if ( http->currentId() == postId ) { \
QCOMPARE( http->currentRequest().method(), QString("POST") ); \
} else { \
QVERIFY( headerAreEqual( http->currentRequest(), res.value().req ) ); \
} \
}
void tst_QHttp::requestStarted( int id )
{
#if defined( DUMP_SIGNALS )
qDebug( "%d:requestStarted( %d )", http->currentId(), id );
#endif
// make sure that the requestStarted and requestFinished are nested correctly
QVERIFY( current_id == 0 );
current_id = id;
QVERIFY( !ids.isEmpty() );
QVERIFY( ids.first() == id );
if ( ids.count() > 1 ) {
QVERIFY( http->hasPendingRequests() );
} else {
QVERIFY( !http->hasPendingRequests() );
}
QVERIFY( http->currentId() == id );
QVERIFY( cur_state == http->state() );
CURRENTREQUEST_TEST;
QVERIFY( http->error() == QHttp::NoError );
}
void tst_QHttp::requestFinished( int id, bool error )
{
#if defined( DUMP_SIGNALS )
qDebug( "%d:requestFinished( %d, %d ) -- errorString: '%s'",
http->currentId(), id, (int)error, http->errorString().toAscii().data() );
#endif
// make sure that the requestStarted and requestFinished are nested correctly
QVERIFY( current_id == id );
current_id = 0;
QVERIFY( !ids.isEmpty() );
QVERIFY( ids.first() == id );
if ( ids.count() > 1 ) {
QVERIFY( http->hasPendingRequests() );
} else {
QVERIFY( !http->hasPendingRequests() );
}
if ( error ) {
QVERIFY( http->error() != QHttp::NoError );
ids.clear();
} else {
QVERIFY( http->error() == QHttp::NoError );
ids.pop_front();
}
QVERIFY( http->currentId() == id );
QVERIFY( cur_state == http->state() );
CURRENTREQUEST_TEST;
ResMapIt res = resultMap.find( http->currentId() );
QVERIFY( res != resultMap.end() );
QVERIFY( res.value().success == -1 );
if ( error )
res.value().success = 0;
else
res.value().success = 1;
}
void tst_QHttp::done( bool error )
{
#if defined( DUMP_SIGNALS )
qDebug( "%d:done( %d )", http->currentId(), (int)error );
#endif
QVERIFY( http->currentId() == 0 );
QVERIFY( current_id == 0 );
QVERIFY( ids.isEmpty() );
QVERIFY( cur_state == http->state() );
QVERIFY( !http->hasPendingRequests() );
QVERIFY( done_success == -1 );
if ( error ) {
QVERIFY( http->error() != QHttp::NoError );
done_success = 0;
} else {
QVERIFY( http->error() == QHttp::NoError );
done_success = 1;
}
QTestEventLoop::instance().exitLoop();
}
void tst_QHttp::stateChanged( int state )
{
#if defined( DUMP_SIGNALS )
qDebug( "%d: stateChanged( %d )", http->currentId(), state );
#endif
QCOMPARE( http->currentId(), current_id );
if ( ids.count() > 0 )
CURRENTREQUEST_TEST;
QVERIFY( state != cur_state );
QVERIFY( state == http->state() );
if ( state != QHttp::Unconnected && !connectionWithAuth ) {
// make sure that the states are always emitted in the right order (for
// this, we assume an ordering on the enum values, which they have at
// the moment)
// connections with authentication will possibly reconnect, so ignore them
QVERIFY( cur_state < state );
}
cur_state = state;
if (state == QHttp::Connecting) {
bytesTotalSend = bytesTotal_init;
bytesDoneSend = bytesDone_init;
bytesTotalRead = bytesTotal_init;
bytesDoneRead = bytesDone_init;
}
}
void tst_QHttp::responseHeaderReceived( const QHttpResponseHeader &header )
{
#if defined( DUMP_SIGNALS )
qDebug( "%d: responseHeaderReceived(\n---{\n%s}---)", http->currentId(), header.toString().toAscii().data() );
#endif
QCOMPARE( http->currentId(), current_id );
if ( ids.count() > 1 ) {
QVERIFY( http->hasPendingRequests() );
} else {
QVERIFY( !http->hasPendingRequests() );
}
CURRENTREQUEST_TEST;
resultMap[ http->currentId() ].resp = header;
}
void tst_QHttp::readyRead( const QHttpResponseHeader & )
{
#if defined( DUMP_SIGNALS )
qDebug( "%d: readyRead()", http->currentId() );
#endif
QCOMPARE( http->currentId(), current_id );
if ( ids.count() > 1 ) {
QVERIFY( http->hasPendingRequests() );
} else {
QVERIFY( !http->hasPendingRequests() );
}
QVERIFY( cur_state == http->state() );
CURRENTREQUEST_TEST;
if ( QTest::currentTestFunction() != QLatin1String("bytesAvailable") ) {
int oldSize = readyRead_ba.size();
quint64 bytesAvail = http->bytesAvailable();
QByteArray ba = http->readAll();
QVERIFY( (quint64) ba.size() == bytesAvail );
readyRead_ba.resize( oldSize + ba.size() );
memcpy( readyRead_ba.data()+oldSize, ba.data(), ba.size() );
if ( bytesTotalRead > 0 ) {
QVERIFY( (int)readyRead_ba.size() <= bytesTotalRead );
}
QVERIFY( (int)readyRead_ba.size() == bytesDoneRead );
}
}
void tst_QHttp::dataSendProgress( int done, int total )
{
#if defined( DUMP_SIGNALS )
qDebug( "%d: dataSendProgress( %d, %d )", http->currentId(), done, total );
#endif
QCOMPARE( http->currentId(), current_id );
if ( ids.count() > 1 ) {
QVERIFY( http->hasPendingRequests() );
} else {
QVERIFY( !http->hasPendingRequests() );
}
QVERIFY( cur_state == http->state() );
CURRENTREQUEST_TEST;
if ( bytesTotalSend == bytesTotal_init ) {
bytesTotalSend = total;
} else {
QCOMPARE( bytesTotalSend, total );
}
QVERIFY( bytesTotalSend != bytesTotal_init );
QVERIFY( bytesDoneSend <= done );
bytesDoneSend = done;
if ( bytesTotalSend > 0 ) {
QVERIFY( bytesDoneSend <= bytesTotalSend );
}
if ( QTest::currentTestFunction() == QLatin1String("abort") ) {
// ### it would be nice if we could specify in our testdata when to do
// the abort
if ( done >= total/100000 ) {
if ( ids.count() != 1 ) {
// do abort only once
int tmpId = ids.first();
ids.clear();
ids << tmpId;
http->abort();
}
}
}
}
void tst_QHttp::dataReadProgress( int done, int total )
{
#if defined( DUMP_SIGNALS )
qDebug( "%d: dataReadProgress( %d, %d )", http->currentId(), done, total );
#endif
QCOMPARE( http->currentId(), current_id );
if ( ids.count() > 1 ) {
QVERIFY( http->hasPendingRequests() );
} else {
QVERIFY( !http->hasPendingRequests() );
}
QVERIFY( cur_state == http->state() );
CURRENTREQUEST_TEST;
if ( bytesTotalRead == bytesTotal_init )
bytesTotalRead = total;
else {
QVERIFY( bytesTotalRead == total );
}
QVERIFY( bytesTotalRead != bytesTotal_init );
QVERIFY( bytesDoneRead <= done );
bytesDoneRead = done;
if ( bytesTotalRead > 0 ) {
QVERIFY( bytesDoneRead <= bytesTotalRead );
}
if ( QTest::currentTestFunction() == QLatin1String("abort") ) {
// ### it would be nice if we could specify in our testdata when to do
// the abort
if ( done >= total/100000 ) {
if ( ids.count() != 1 ) {
// do abort only once
int tmpId = ids.first();
ids.clear();
ids << tmpId;
http->abort();
}
}
}
}
QHttp *tst_QHttp::newHttp(bool withAuth)
{
QHttp *nHttp = new QHttp( 0 );
connect( nHttp, SIGNAL(requestStarted(int)),
SLOT(requestStarted(int)) );
connect( nHttp, SIGNAL(requestFinished(int,bool)),
SLOT(requestFinished(int,bool)) );
connect( nHttp, SIGNAL(done(bool)),
SLOT(done(bool)) );
connect( nHttp, SIGNAL(stateChanged(int)),
SLOT(stateChanged(int)) );
connect( nHttp, SIGNAL(responseHeaderReceived(const QHttpResponseHeader&)),
SLOT(responseHeaderReceived(const QHttpResponseHeader&)) );
connect( nHttp, SIGNAL(readyRead(const QHttpResponseHeader&)),
SLOT(readyRead(const QHttpResponseHeader&)) );
connect( nHttp, SIGNAL(dataSendProgress(int,int)),
SLOT(dataSendProgress(int,int)) );
connect( nHttp, SIGNAL(dataReadProgress(int,int)),
SLOT(dataReadProgress(int,int)) );
connectionWithAuth = withAuth;
return nHttp;
}
void tst_QHttp::addRequest( QHttpRequestHeader header, int id )
{
ids << id;
RequestResult res;
res.req = header;
res.success = -1;
resultMap[ id ] = res;
}
bool tst_QHttp::headerAreEqual( const QHttpHeader &h1, const QHttpHeader &h2 )
{
if ( !h1.isValid() )
return !h2.isValid();
if ( !h2.isValid() )
return !h1.isValid();
return h1.toString() == h2.toString();
}
void tst_QHttp::reconnect()
{
reconnect_state_connect_count = 0;
QHttp http;
QObject::connect(&http, SIGNAL(stateChanged(int)), this, SLOT(reconnect_state(int)));
http.setHost("trolltech.com", 80);
http.get("/company/index.html");
http.setHost("trolltech.com", 8080);
http.get("/company/index.html");
QTestEventLoop::instance().enterLoop(60);
if (QTestEventLoop::instance().timeout())
QFAIL("Network operation timed out");
QCOMPARE(reconnect_state_connect_count, 1);
QTestEventLoop::instance().enterLoop(60);
if (QTestEventLoop::instance().timeout())
QFAIL("Network operation timed out");
QCOMPARE(reconnect_state_connect_count, 2);
}
void tst_QHttp::reconnect_state(int state)
{
if (state == QHttp::Connecting) {
++reconnect_state_connect_count;
QTestEventLoop::instance().exitLoop();
}
}
void tst_QHttp::setSocket()
{
QHttp *http = new QHttp;
QPointer<QTcpSocket> replacementSocket = new QTcpSocket;
http->setSocket(replacementSocket);
QCoreApplication::processEvents();
delete http;
QVERIFY(replacementSocket);
delete replacementSocket;
}
class Server : public QTcpServer
{
Q_OBJECT
public:
Server()
{
connect(this, SIGNAL(newConnection()),
this, SLOT(serveConnection()));
}
private slots:
void serveConnection()
{
QTcpSocket *socket = nextPendingConnection();
socket->write("HTTP/1.1 404 Not found\r\n"
"content-length: 4\r\n\r\nabcd");
socket->disconnectFromHost();
};
};
void tst_QHttp::unexpectedRemoteClose()
{
QFETCH_GLOBAL(int, proxyType);
if (proxyType == QNetworkProxy::Socks5Proxy) {
// This test doesn't make sense for SOCKS5
return;
}
Server server;
server.listen();
QCoreApplication::instance()->processEvents();
QEventLoop loop;
QTimer::singleShot(3000, &loop, SLOT(quit()));
QHttp http;
QObject::connect(&http, SIGNAL(done(bool)), &loop, SLOT(quit()));
QSignalSpy finishedSpy(&http, SIGNAL(requestFinished(int, bool)));
QSignalSpy doneSpy(&http, SIGNAL(done(bool)));
http.setHost("localhost", server.serverPort());
http.get("/");
http.get("/");
http.get("/");
loop.exec();
QCOMPARE(finishedSpy.count(), 4);
QVERIFY(!finishedSpy.at(1).at(1).toBool());
QVERIFY(!finishedSpy.at(2).at(1).toBool());
QVERIFY(!finishedSpy.at(3).at(1).toBool());
QCOMPARE(doneSpy.count(), 1);
QVERIFY(!doneSpy.at(0).at(0).toBool());
}
void tst_QHttp::pctEncodedPath()
{
QHttpRequestHeader header;
header.setRequest("GET", "/index.asp/a=%20&b=%20&c=%20");
QCOMPARE(header.toString(), QString("GET /index.asp/a=%20&b=%20&c=%20 HTTP/1.1\r\n\r\n"));
}
void tst_QHttp::caseInsensitiveKeys()
{
QHttpResponseHeader header("HTTP/1.1 200 OK\r\nContent-Length: 213\r\nX-Been-There: True\r\nLocation: http://www.TrollTech.com/\r\n\r\n");
QVERIFY(header.hasKey("Content-Length"));
QVERIFY(header.hasKey("X-Been-There"));
QVERIFY(header.hasKey("Location"));
QVERIFY(header.hasKey("content-length"));
QVERIFY(header.hasKey("x-been-there"));
QVERIFY(header.hasKey("location"));
QCOMPARE(header.value("Content-Length"), QString("213"));
QCOMPARE(header.value("X-Been-There"), QString("True"));
QCOMPARE(header.value("Location"), QString("http://www.TrollTech.com/"));
QCOMPARE(header.value("content-length"), QString("213"));
QCOMPARE(header.value("x-been-there"), QString("True"));
QCOMPARE(header.value("location"), QString("http://www.TrollTech.com/"));
QCOMPARE(header.allValues("location"), QStringList("http://www.TrollTech.com/"));
header.addValue("Content-Length", "213");
header.addValue("Content-Length", "214");
header.addValue("Content-Length", "215");
qDebug() << header.toString();
}
void tst_QHttp::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth)
{
proxyAuthCalled = true;
auth->setUser("qsockstest");
auth->setPassword("password");
}
void tst_QHttp::postAuthNtlm()
{
QSKIP("NTLM not working", SkipAll);
QHostInfo info = QHostInfo::fromName(QHostInfo::localHostName());
QByteArray postData("Hello World");
QHttp http;
http.setHost(QtNetworkSettings::serverName());
http.setProxy(QtNetworkSettings::serverName(), 3130);
http.post("/", postData);
proxyAuthCalled = false;
connect(&http, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
QObject::connect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(3);
QObject::disconnect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QVERIFY(proxyAuthCalled);
QVERIFY(!QTestEventLoop::instance().timeout());
};
void tst_QHttp::proxyAndSsl()
{
#ifdef QT_NO_OPENSSL
QSKIP("No OpenSSL support in this platform", SkipAll);
#else
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
QHttp http;
http.setHost(QtNetworkSettings::serverName(), QHttp::ConnectionModeHttps);
http.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3129));
http.get("/");
proxyAuthCalled = false;
connect(&http, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
connect(&http, SIGNAL(sslErrors(QList<QSslError>)),
&http, SLOT(ignoreSslErrors()));
QObject::connect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(3);
QObject::disconnect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(proxyAuthCalled);
QHttpResponseHeader header = http.lastResponse();
QVERIFY(header.isValid());
QVERIFY(header.statusCode() < 400); // Should be 200, but as long as it's not an error, we're happy
#endif
}
void tst_QHttp::cachingProxyAndSsl()
{
#ifdef QT_NO_OPENSSL
QSKIP("No OpenSSL support in this platform", SkipAll);
#else
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
QHttp http;
http.setHost(QtNetworkSettings::serverName(), QHttp::ConnectionModeHttps);
http.setProxy(QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129));
http.get("/");
proxyAuthCalled = false;
connect(&http, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
QObject::connect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(3);
QObject::disconnect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(!proxyAuthCalled); // NOT called! QHttp should get a socket error
QVERIFY(http.state() != QHttp::Connected);
QHttpResponseHeader header = http.lastResponse();
QVERIFY(!header.isValid());
#endif
}
void tst_QHttp::emptyBodyInReply()
{
// Note: if this test starts failing, please verify the date on the file
// returned by Apache on http://netiks.troll.no/
// It is right now hard-coded to the date below
QHttp http;
http.setHost(QtNetworkSettings::serverName());
QHttpRequestHeader headers("GET", "/");
headers.addValue("If-Modified-Since", "Sun, 16 Nov 2008 12:29:51 GMT");
headers.addValue("Host", QtNetworkSettings::serverName());
http.request(headers);
QObject::connect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QObject::disconnect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QVERIFY(!QTestEventLoop::instance().timeout());
// check the reply
if (http.lastResponse().statusCode() != 304) {
qWarning() << http.lastResponse().statusCode() << qPrintable(http.lastResponse().reasonPhrase());
qWarning() << "Last-Modified:" << qPrintable(http.lastResponse().value("last-modified"));
QFAIL("Server replied with the wrong status code; see warning output");
}
}
void tst_QHttp::abortSender()
{
QHttp *http = qobject_cast<QHttp *>(sender());
if (http)
http->abort();
}
void tst_QHttp::abortInReadyRead()
{
QHttp http;
http.setHost(QtNetworkSettings::serverName());
http.get("/qtest/bigfile");
qRegisterMetaType<QHttpResponseHeader>();
QSignalSpy spy(&http, SIGNAL(readyRead(QHttpResponseHeader)));
QObject::connect(&http, SIGNAL(readyRead(QHttpResponseHeader)), this, SLOT(abortSender()));
QObject::connect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QObject::disconnect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QVERIFY2(!QTestEventLoop::instance().timeout(), "Network timeout");
QVERIFY(http.state() != QHttp::Connected);
QCOMPARE(spy.count(), 1);
}
void tst_QHttp::abortInResponseHeaderReceived()
{
QHttp http;
http.setHost(QtNetworkSettings::serverName());
http.get("/qtest/bigfile");
qRegisterMetaType<QHttpResponseHeader>();
QSignalSpy spy(&http, SIGNAL(readyRead(QHttpResponseHeader)));
QObject::connect(&http, SIGNAL(responseHeaderReceived(QHttpResponseHeader)), this, SLOT(abortSender()));
QObject::connect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QObject::disconnect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QVERIFY2(!QTestEventLoop::instance().timeout(), "Network timeout");
QVERIFY(http.state() != QHttp::Connected);
QCOMPARE(spy.count(), 0);
}
void tst_QHttp::connectionClose()
{
// This was added in response to bug 176822
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
QHttp http;
ClosingServer server;
http.setHost("localhost", QHttp::ConnectionModeHttps, server.serverPort());
http.get("/login/gateway/processLogin");
// another possibility:
//http.setHost("nexus.passport.com", QHttp::ConnectionModeHttps, 443);
//http.get("/rdr/pprdr.asp");
QObject::connect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(900);
QObject::disconnect(&http, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QVERIFY(!QTestEventLoop::instance().timeout());
}
void tst_QHttp::nestedEventLoop_slot(int id)
{
if (!ids.contains(id))
return;
QEventLoop subloop;
// 16 seconds: fluke times out in 15 seconds, which triggers a QTcpSocket error
QTimer::singleShot(16000, &subloop, SLOT(quit()));
subloop.exec();
QTestEventLoop::instance().exitLoop();
}
void tst_QHttp::nestedEventLoop()
{
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
http = new QHttp;
http->setHost(QtNetworkSettings::serverName());
int getId = http->get("/");
ids.clear();
ids << getId;
QSignalSpy spy(http, SIGNAL(requestStarted(int)));
QSignalSpy spy2(http, SIGNAL(done(bool)));
connect(http, SIGNAL(requestFinished(int,bool)), SLOT(nestedEventLoop_slot(int)));
QTestEventLoop::instance().enterLoop(20);
QVERIFY2(!QTestEventLoop::instance().timeout(), "Network timeout");
// Find out how many signals with the first argument equalling our id were found
int spyCount = 0;
for (int i = 0; i < spy.count(); ++i)
if (spy.at(i).at(0).toInt() == getId)
++spyCount;
// each signal spied should have been emitted only once
QCOMPARE(spyCount, 1);
QCOMPARE(spy2.count(), 1);
}
QTEST_MAIN(tst_QHttp)
#include "tst_qhttp.moc"