qt5base-lts/tests/auto/qnetworkreply/tst_qnetworkreply.cpp
Shane Kearns 605102d5c6 Fix some warnings in symbian network tests
Ignore warning when the test intentionally sets an invalid socket descriptor.
Make sure to set content type on all http post tests in tst_qnetworkreply.
Run test with enough capabilities to avoid platsec errors when accessing
certificate store.

Reviewed-By: Markus Goetz
(cherry picked from commit 9632fdefa9012ca11cd1345d66bafd6f417de88e)
2011-05-11 16:37:16 +02:00

6194 lines
232 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 <QtCore/QCryptographicHash>
#include <QtCore/QDataStream>
#include <QtCore/QUrl>
#include <QtCore/QEventLoop>
#include <QtCore/QFile>
#include <QtCore/QSharedPointer>
#include <QtCore/QScopedPointer>
#include <QtCore/QTemporaryFile>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QtNetwork/QLocalSocket>
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QHostInfo>
#include <QtNetwork/QFtp>
#include <QtNetwork/QAbstractNetworkCache>
#include <QtNetwork/qauthenticator.h>
#include <QtNetwork/qnetworkaccessmanager.h>
#include <QtNetwork/qnetworkrequest.h>
#include <QtNetwork/qnetworkreply.h>
#include <QtNetwork/qnetworkcookie.h>
#include <QtNetwork/QHttpPart>
#include <QtNetwork/QHttpMultiPart>
#ifndef QT_NO_OPENSSL
#include <QtNetwork/qsslerror.h>
#include <QtNetwork/qsslconfiguration.h>
#endif
#ifndef QT_NO_BEARERMANAGEMENT
#include <QtNetwork/qnetworkconfigmanager.h>
#include <QtNetwork/qnetworkconfiguration.h>
#include <QtNetwork/qnetworksession.h>
#endif
#include <time.h>
#include "private/qnetworkaccessmanager_p.h"
#ifdef Q_OS_SYMBIAN
#define SRCDIR "."
#endif
#include "../network-settings.h"
Q_DECLARE_METATYPE(QSharedPointer<char>)
Q_DECLARE_METATYPE(QNetworkReply*)
Q_DECLARE_METATYPE(QAuthenticator*)
Q_DECLARE_METATYPE(QNetworkProxy)
Q_DECLARE_METATYPE(QNetworkProxyQuery)
Q_DECLARE_METATYPE(QList<QNetworkProxy>)
Q_DECLARE_METATYPE(QNetworkReply::NetworkError)
Q_DECLARE_METATYPE(QBuffer*)
Q_DECLARE_METATYPE(QHttpMultiPart *)
Q_DECLARE_METATYPE(QList<QFile*>) // for multiparts
#ifndef QT_NO_OPENSSL
Q_DECLARE_METATYPE(QSslConfiguration)
#endif
class QNetworkReplyPtr: public QSharedPointer<QNetworkReply>
{
public:
inline QNetworkReplyPtr(QNetworkReply *ptr = 0)
: QSharedPointer<QNetworkReply>(ptr)
{ }
inline operator QNetworkReply *() const { return data(); }
};
class MyCookieJar;
class tst_QNetworkReply: public QObject
{
Q_OBJECT
struct ProxyData {
ProxyData(const QNetworkProxy &p, const QByteArray &t, bool auth)
: tag(t), proxy(p), requiresAuthentication(auth)
{ }
QByteArray tag;
QNetworkProxy proxy;
bool requiresAuthentication;
};
static bool seedCreated;
static QString createUniqueExtension() {
if (!seedCreated) {
qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()) + QCoreApplication::applicationPid());
seedCreated = true; // not thread-safe, but who cares
}
QString s = QString("%1-%2-%3").arg(QTime(0,0,0).msecsTo(QTime::currentTime())).arg(QCoreApplication::applicationPid()).arg(qrand());
return s;
};
QEventLoop *loop;
enum RunSimpleRequestReturn { Timeout = 0, Success, Failure };
int returnCode;
QString testFileName;
#if !defined Q_OS_WIN
QString wronlyFileName;
#endif
QString uniqueExtension;
QList<ProxyData> proxies;
QNetworkAccessManager manager;
MyCookieJar *cookieJar;
#ifndef QT_NO_OPENSSL
QSslConfiguration storedSslConfiguration;
QList<QSslError> storedExpectedSslErrors;
#endif
#ifndef QT_NO_BEARERMANAGEMENT
QNetworkConfigurationManager *netConfMan;
QNetworkConfiguration networkConfiguration;
QScopedPointer<QNetworkSession> networkSession;
#endif
public:
tst_QNetworkReply();
~tst_QNetworkReply();
QString runSimpleRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request,
QNetworkReplyPtr &reply, const QByteArray &data = QByteArray());
QString runMultipartRequest(const QNetworkRequest &request, QNetworkReplyPtr &reply,
QHttpMultiPart *multiPart, const QByteArray &verb);
QString runCustomRequest(const QNetworkRequest &request, QNetworkReplyPtr &reply,
const QByteArray &verb, QIODevice *data);
public Q_SLOTS:
void finished();
void gotError();
void authenticationRequired(QNetworkReply*,QAuthenticator*);
void proxyAuthenticationRequired(const QNetworkProxy &,QAuthenticator*);
#ifndef QT_NO_OPENSSL
void sslErrors(QNetworkReply*,const QList<QSslError> &);
void storeSslConfiguration();
void ignoreSslErrorListSlot(QNetworkReply *reply, const QList<QSslError> &);
#endif
protected Q_SLOTS:
void nestedEventLoops_slot();
private Q_SLOTS:
void init();
void cleanup();
void initTestCase();
void cleanupTestCase();
void stateChecking();
void invalidProtocol();
void getFromData_data();
void getFromData();
void getFromFile();
void getFromFileSpecial_data();
void getFromFileSpecial();
void getFromFtp_data();
void getFromFtp();
void getFromHttp_data();
void getFromHttp();
void getErrors_data();
void getErrors();
void putToFile_data();
void putToFile();
void putToFtp_data();
void putToFtp();
void putToHttp_data();
void putToHttp();
void putToHttpSynchronous_data();
void putToHttpSynchronous();
void putToHttpMultipart_data();
void putToHttpMultipart();
void postToHttp_data();
void postToHttp();
void postToHttpSynchronous_data();
void postToHttpSynchronous();
void postToHttpMultipart_data();
void postToHttpMultipart();
void deleteFromHttp_data();
void deleteFromHttp();
void putGetDeleteGetFromHttp_data();
void putGetDeleteGetFromHttp();
void sendCustomRequestToHttp_data();
void sendCustomRequestToHttp();
void ioGetFromData_data();
void ioGetFromData();
void ioGetFromFileSpecial_data();
void ioGetFromFileSpecial();
void ioGetFromFile_data();
void ioGetFromFile();
void ioGetFromFtp_data();
void ioGetFromFtp();
void ioGetFromFtpWithReuse();
void ioGetFromHttp();
void ioGetFromBuiltinHttp_data();
void ioGetFromBuiltinHttp();
void ioGetFromHttpWithReuseParallel();
void ioGetFromHttpWithReuseSequential();
void ioGetFromHttpWithAuth_data();
void ioGetFromHttpWithAuth();
void ioGetFromHttpWithAuthSynchronous();
void ioGetFromHttpWithProxyAuth();
void ioGetFromHttpWithProxyAuthSynchronous();
void ioGetFromHttpWithSocksProxy();
#ifndef QT_NO_OPENSSL
void ioGetFromHttpsWithSslErrors();
void ioGetFromHttpsWithIgnoreSslErrors();
void ioGetFromHttpsWithSslHandshakeError();
#endif
void ioGetFromHttpBrokenServer_data();
void ioGetFromHttpBrokenServer();
void ioGetFromHttpStatus100_data();
void ioGetFromHttpStatus100();
void ioGetFromHttpNoHeaders_data();
void ioGetFromHttpNoHeaders();
void ioGetFromHttpWithCache_data();
void ioGetFromHttpWithCache();
void ioGetWithManyProxies_data();
void ioGetWithManyProxies();
void ioPutToFileFromFile_data();
void ioPutToFileFromFile();
void ioPutToFileFromSocket_data();
void ioPutToFileFromSocket();
void ioPutToFileFromLocalSocket_data();
void ioPutToFileFromLocalSocket();
void ioPutToFileFromProcess_data();
void ioPutToFileFromProcess();
void ioPutToFtpFromFile_data();
void ioPutToFtpFromFile();
void ioPutToHttpFromFile_data();
void ioPutToHttpFromFile();
void ioPostToHttpFromFile_data();
void ioPostToHttpFromFile();
void ioPostToHttpFromSocket_data();
void ioPostToHttpFromSocket();
void ioPostToHttpFromSocketSynchronous();
void ioPostToHttpFromSocketSynchronous_data();
void ioPostToHttpFromMiddleOfFileToEnd();
void ioPostToHttpFromMiddleOfFileFiveBytes();
void ioPostToHttpFromMiddleOfQBufferFiveBytes();
void ioPostToHttpNoBufferFlag();
void ioPostToHttpUploadProgress();
void ioPostToHttpEmptyUploadProgress();
void lastModifiedHeaderForFile();
void lastModifiedHeaderForHttp();
void httpCanReadLine();
void rateControl_data();
void rateControl();
void downloadProgress_data();
void downloadProgress();
void uploadProgress_data();
void uploadProgress();
void chaining_data();
void chaining();
void receiveCookiesFromHttp_data();
void receiveCookiesFromHttp();
void receiveCookiesFromHttpSynchronous_data();
void receiveCookiesFromHttpSynchronous();
void sendCookies_data();
void sendCookies();
void sendCookiesSynchronous_data();
void sendCookiesSynchronous();
void nestedEventLoops();
void httpProxyCommands_data();
void httpProxyCommands();
void httpProxyCommandsSynchronous_data();
void httpProxyCommandsSynchronous();
void proxyChange();
void authorizationError_data();
void authorizationError();
void httpConnectionCount();
void httpReUsingConnectionSequential_data();
void httpReUsingConnectionSequential();
void httpReUsingConnectionFromFinishedSlot_data();
void httpReUsingConnectionFromFinishedSlot();
void httpRecursiveCreation();
#ifndef QT_NO_OPENSSL
void ioPostToHttpsUploadProgress();
void ignoreSslErrorsList_data();
void ignoreSslErrorsList();
void ignoreSslErrorsListWithSlot_data();
void ignoreSslErrorsListWithSlot();
void sslConfiguration_data();
void sslConfiguration();
#endif
void getAndThenDeleteObject_data();
void getAndThenDeleteObject();
void symbianOpenCDataUrlCrash();
void getFromHttpIntoBuffer_data();
void getFromHttpIntoBuffer();
void getFromHttpIntoBuffer2_data();
void getFromHttpIntoBuffer2();
void ioGetFromHttpWithoutContentLength();
void ioGetFromHttpBrokenChunkedEncoding();
void qtbug12908compressedHttpReply();
void compressedHttpReplyBrokenGzip();
void getFromUnreachableIp();
void qtbug4121unknownAuthentication();
void qtbug13431replyThrottling();
void httpWithNoCredentialUsage();
void qtbug15311doubleContentLength();
void qtbug18232gzipContentLengthZero();
void synchronousRequest_data();
void synchronousRequest();
#ifndef QT_NO_OPENSSL
void synchronousRequestSslFailure();
#endif
void httpAbort();
void dontInsertPartialContentIntoTheCache();
// NOTE: This test must be last!
void parentingRepliesToTheApp();
};
bool tst_QNetworkReply::seedCreated = false;
QT_BEGIN_NAMESPACE
namespace QTest {
template<>
char *toString(const QNetworkReply::NetworkError& code)
{
const QMetaObject *mo = &QNetworkReply::staticMetaObject;
int index = mo->indexOfEnumerator("NetworkError");
if (index == -1)
return qstrdup("");
QMetaEnum qme = mo->enumerator(index);
return qstrdup(qme.valueToKey(code));
}
template<>
char *toString(const QNetworkCookie &cookie)
{
return qstrdup(cookie.toRawForm());
}
template<>
char *toString(const QList<QNetworkCookie> &list)
{
QString result = "QList(";
bool first = true;
foreach (QNetworkCookie cookie, list) {
if (!first)
result += ", ";
first = false;
result += QString::fromLatin1("QNetworkCookie(%1)").arg(QLatin1String(cookie.toRawForm()));
}
return qstrdup(result.append(')').toLocal8Bit());
}
}
QT_END_NAMESPACE
#define RUN_REQUEST(call) \
do { \
QString errorMsg = call; \
if (!errorMsg.isEmpty()) \
QFAIL(qPrintable(errorMsg)); \
} while (0);
#ifndef QT_NO_OPENSSL
static void setupSslServer(QSslSocket* serverSocket)
{
serverSocket->setProtocol(QSsl::AnyProtocol);
serverSocket->setLocalCertificate(SRCDIR "/certs/server.pem");
serverSocket->setPrivateKey(SRCDIR "/certs/server.key");
}
#endif
// Does not work for POST/PUT!
class MiniHttpServer: public QTcpServer
{
Q_OBJECT
public:
QTcpSocket *client; // always the last one that was received
QByteArray dataToTransmit;
QByteArray receivedData;
QSemaphore ready;
bool doClose;
bool doSsl;
bool multiple;
int totalConnections;
MiniHttpServer(const QByteArray &data, bool ssl = false, QThread *thread = 0)
: client(0), dataToTransmit(data), doClose(true), doSsl(ssl),
multiple(false), totalConnections(0)
{
listen();
if (thread) {
connect(thread, SIGNAL(started()), this, SLOT(threadStartedSlot()));
moveToThread(thread);
thread->start();
ready.acquire();
}
}
protected:
void incomingConnection(int socketDescriptor)
{
//qDebug() << "incomingConnection" << socketDescriptor;
if (!doSsl) {
client = new QTcpSocket;
client->setSocketDescriptor(socketDescriptor);
connectSocketSignals();
} else {
#ifndef QT_NO_OPENSSL
QSslSocket *serverSocket = new QSslSocket;
serverSocket->setParent(this);
if (serverSocket->setSocketDescriptor(socketDescriptor)) {
connect(serverSocket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(slotSslErrors(QList<QSslError>)));
setupSslServer(serverSocket);
serverSocket->startServerEncryption();
client = serverSocket;
connectSocketSignals();
} else {
delete serverSocket;
return;
}
#endif
}
client->setParent(this);
++totalConnections;
}
private:
void connectSocketSignals()
{
//qDebug() << "connectSocketSignals" << client;
connect(client, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
connect(client, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWrittenSlot()));
connect(client, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(slotError(QAbstractSocket::SocketError)));
}
private slots:
#ifndef QT_NO_OPENSSL
void slotSslErrors(const QList<QSslError>& errors)
{
qDebug() << "slotSslErrors" << client->errorString() << errors;
}
#endif
void slotError(QAbstractSocket::SocketError err)
{
qDebug() << "slotError" << err << client->errorString();
}
public slots:
void readyReadSlot()
{
receivedData += client->readAll();
int doubleEndlPos = receivedData.indexOf("\r\n\r\n");
if (doubleEndlPos != -1) {
// multiple requests incoming. remove the bytes of the current one
if (multiple)
receivedData.remove(0, doubleEndlPos+4);
// we need to emulate the bytesWrittenSlot call if the data is empty.
if (dataToTransmit.size() == 0)
QMetaObject::invokeMethod(this, "bytesWrittenSlot", Qt::QueuedConnection);
else
client->write(dataToTransmit);
}
}
void bytesWrittenSlot() {
if (doClose && client->bytesToWrite() == 0) {
client->disconnectFromHost();
disconnect(client, 0, this, 0);
}
}
void threadStartedSlot()
{
ready.release();
}
};
class MyCookieJar: public QNetworkCookieJar
{
public:
inline QList<QNetworkCookie> allCookies() const
{ return QNetworkCookieJar::allCookies(); }
inline void setAllCookies(const QList<QNetworkCookie> &cookieList)
{ QNetworkCookieJar::setAllCookies(cookieList); }
};
class MyProxyFactory: public QNetworkProxyFactory
{
public:
int callCount;
QList<QNetworkProxy> toReturn;
QNetworkProxyQuery lastQuery;
inline MyProxyFactory() { clear(); }
inline void clear()
{
callCount = 0;
toReturn = QList<QNetworkProxy>() << QNetworkProxy::DefaultProxy;
lastQuery = QNetworkProxyQuery();
}
virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query)
{
lastQuery = query;
++callCount;
return toReturn;
}
};
class MyMemoryCache: public QAbstractNetworkCache
{
public:
typedef QPair<QNetworkCacheMetaData, QByteArray> CachedContent;
typedef QHash<QByteArray, CachedContent> CacheData;
CacheData cache;
MyMemoryCache(QObject *parent) : QAbstractNetworkCache(parent) {}
QNetworkCacheMetaData metaData(const QUrl &url)
{
return cache.value(url.toEncoded()).first;
}
void updateMetaData(const QNetworkCacheMetaData &metaData)
{
cache[metaData.url().toEncoded()].first = metaData;
}
QIODevice *data(const QUrl &url)
{
CacheData::ConstIterator it = cache.find(url.toEncoded());
if (it == cache.constEnd())
return 0;
QBuffer *io = new QBuffer(this);
io->setData(it->second);
io->open(QIODevice::ReadOnly);
io->seek(0);
return io;
}
bool remove(const QUrl &url)
{
cache.remove(url.toEncoded());
return true;
}
qint64 cacheSize() const
{
qint64 total = 0;
foreach (const CachedContent &entry, cache)
total += entry.second.size();
return total;
}
QIODevice *prepare(const QNetworkCacheMetaData &)
{ Q_ASSERT(0 && "Should not have tried to add to the cache"); return 0; }
void insert(QIODevice *)
{ Q_ASSERT(0 && "Should not have tried to add to the cache"); }
void clear() { cache.clear(); }
};
Q_DECLARE_METATYPE(MyMemoryCache::CachedContent)
Q_DECLARE_METATYPE(MyMemoryCache::CacheData)
class MySpyMemoryCache: public QAbstractNetworkCache
{
public:
MySpyMemoryCache(QObject *parent) : QAbstractNetworkCache(parent) {}
~MySpyMemoryCache()
{
qDeleteAll(m_buffers);
m_buffers.clear();
}
QHash<QUrl, QIODevice*> m_buffers;
QList<QUrl> m_insertedUrls;
QNetworkCacheMetaData metaData(const QUrl &)
{
return QNetworkCacheMetaData();
}
void updateMetaData(const QNetworkCacheMetaData &)
{
}
QIODevice *data(const QUrl &)
{
return 0;
}
bool remove(const QUrl &url)
{
delete m_buffers.take(url);
return m_insertedUrls.removeAll(url) > 0;
}
qint64 cacheSize() const
{
return 0;
}
QIODevice *prepare(const QNetworkCacheMetaData &metaData)
{
QBuffer* buffer = new QBuffer;
buffer->open(QIODevice::ReadWrite);
buffer->setProperty("url", metaData.url());
m_buffers.insert(metaData.url(), buffer);
return buffer;
}
void insert(QIODevice *buffer)
{
QUrl url = buffer->property("url").toUrl();
m_insertedUrls << url;
delete m_buffers.take(url);
}
void clear() { m_insertedUrls.clear(); }
};
class DataReader: public QObject
{
Q_OBJECT
public:
qint64 totalBytes;
QByteArray data;
QIODevice *device;
bool accumulate;
DataReader(QIODevice *dev, bool acc = true) : totalBytes(0), device(dev), accumulate(acc)
{
connect(device, SIGNAL(readyRead()), SLOT(doRead()));
}
public slots:
void doRead()
{
QByteArray buffer;
buffer.resize(device->bytesAvailable());
qint64 bytesRead = device->read(buffer.data(), device->bytesAvailable());
if (bytesRead == -1) {
QTestEventLoop::instance().exitLoop();
return;
}
buffer.truncate(bytesRead);
totalBytes += bytesRead;
if (accumulate)
data += buffer;
}
};
class SocketPair: public QObject
{
Q_OBJECT
public:
QIODevice *endPoints[2];
SocketPair(QObject *parent = 0)
: QObject(parent)
{
endPoints[0] = endPoints[1] = 0;
}
bool create()
{
QTcpServer server;
server.listen();
QTcpSocket *active = new QTcpSocket(this);
active->connectToHost("127.0.0.1", server.serverPort());
#ifndef Q_OS_SYMBIAN
// need more time as working with embedded
// device and testing from emualtor
// things tend to get slower
if (!active->waitForConnected(1000))
return false;
if (!server.waitForNewConnection(1000))
return false;
#else
if (!active->waitForConnected(100))
return false;
if (!server.waitForNewConnection(100))
return false;
#endif
QTcpSocket *passive = server.nextPendingConnection();
passive->setParent(this);
endPoints[0] = active;
endPoints[1] = passive;
return true;
}
};
// A blocking tcp server (must be used in a thread) which supports SSL.
class BlockingTcpServer : public QTcpServer
{
Q_OBJECT
public:
BlockingTcpServer(bool ssl) : doSsl(ssl), sslSocket(0) {}
QTcpSocket* waitForNextConnectionSocket() {
waitForNewConnection(-1);
if (doSsl) {
Q_ASSERT(sslSocket);
return sslSocket;
} else {
//qDebug() << "returning nextPendingConnection";
return nextPendingConnection();
}
}
virtual void incomingConnection(int socketDescriptor)
{
#ifndef QT_NO_OPENSSL
if (doSsl) {
QSslSocket *serverSocket = new QSslSocket;
serverSocket->setParent(this);
serverSocket->setSocketDescriptor(socketDescriptor);
connect(serverSocket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(slotSslErrors(QList<QSslError>)));
setupSslServer(serverSocket);
serverSocket->startServerEncryption();
sslSocket = serverSocket;
} else
#endif
{
QTcpServer::incomingConnection(socketDescriptor);
}
}
private slots:
#ifndef QT_NO_OPENSSL
void slotSslErrors(const QList<QSslError>& errors)
{
qDebug() << "slotSslErrors" << sslSocket->errorString() << errors;
}
#endif
private:
const bool doSsl;
QTcpSocket* sslSocket;
};
// This server tries to send data as fast as possible (like most servers)
// but it measures how fast it was able to send it, which shows at which
// rate the reader is processing the data.
class FastSender: public QThread
{
Q_OBJECT
QSemaphore ready;
qint64 wantedSize;
int port;
enum Protocol { DebugPipe, ProvidedData };
const Protocol protocol;
const bool doSsl;
const bool fillKernelBuffer;
public:
int transferRate;
QWaitCondition cond;
QByteArray dataToTransmit;
int dataIndex;
// a server that sends debugpipe data
FastSender(qint64 size)
: wantedSize(size), port(-1), protocol(DebugPipe),
doSsl(false), fillKernelBuffer(true), transferRate(-1),
dataIndex(0)
{
start();
ready.acquire();
}
// a server that sends the data provided at construction time, useful for HTTP
FastSender(const QByteArray& data, bool https, bool fillBuffer)
: wantedSize(data.size()), port(-1), protocol(ProvidedData),
doSsl(https), fillKernelBuffer(fillBuffer), transferRate(-1),
dataToTransmit(data), dataIndex(0)
{
start();
ready.acquire();
}
inline int serverPort() const { return port; }
int writeNextData(QTcpSocket* socket, qint32 size)
{
if (protocol == DebugPipe) {
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream << QVariantMap() << QByteArray(size, 'a');
socket->write((char*)&size, sizeof size);
socket->write(data);
dataIndex += size;
return size;
} else {
const QByteArray data = dataToTransmit.mid(dataIndex, size);
socket->write(data);
dataIndex += data.size();
//qDebug() << "wrote" << dataIndex << "/" << dataToTransmit.size();
return data.size();
}
}
void writeLastData(QTcpSocket* socket)
{
if (protocol == DebugPipe) {
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream << QVariantMap() << QByteArray();
const qint32 size = data.size();
socket->write((char*)&size, sizeof size);
socket->write(data);
}
}
protected:
void run()
{
BlockingTcpServer server(doSsl);
server.listen();
port = server.serverPort();
ready.release();
QTcpSocket *client = server.waitForNextConnectionSocket();
// get the "request" packet
if (!client->waitForReadyRead(2000)) {
qDebug() << "FastSender:" << client->error() << "waiting for \"request\" packet";
return;
}
client->readAll(); // we're not interested in the actual contents (e.g. HTTP request)
enum { BlockSize = 1024 };
if (fillKernelBuffer) {
// write a bunch of bytes to fill up the buffers
bool done = false;
do {
if (writeNextData(client, BlockSize) < BlockSize) {
qDebug() << "ERROR: FastSender: not enough data to write in order to fill buffers; or client is reading too fast";
return;
}
while (client->bytesToWrite() > 0) {
if (!client->waitForBytesWritten(0)) {
done = true;
break;
}
}
//qDebug() << "Filling kernel buffer: wrote" << dataIndex << "bytes";
} while (!done);
qDebug() << "FastSender: ok, kernel buffer is full after writing" << dataIndex << "bytes";
}
// Tell the client to start reading
emit dataReady();
// the kernel buffer is full
// clean up QAbstractSocket's residue:
while (client->bytesToWrite() > 0) {
qDebug() << "Still having" << client->bytesToWrite() << "bytes to write, doing that now";
if (!client->waitForBytesWritten(2000)) {
qDebug() << "ERROR: FastSender:" << client->error() << "cleaning up residue";
return;
}
}
// now write in "blocking mode", this is where the rate measuring starts
QTime timer;
timer.start();
//const qint64 writtenBefore = dataIndex;
//qint64 measuredTotalBytes = wantedSize - writtenBefore;
qint64 measuredSentBytes = 0;
while (dataIndex < wantedSize) {
const int remainingBytes = wantedSize - measuredSentBytes;
const int bytesToWrite = qMin(remainingBytes, static_cast<int>(BlockSize));
Q_ASSERT(bytesToWrite);
measuredSentBytes += writeNextData(client, bytesToWrite);
while (client->bytesToWrite() > 0) {
if (!client->waitForBytesWritten(2000)) {
qDebug() << "ERROR: FastSender:" << client->error() << "during blocking write";
return;
}
}
/*qDebug() << "FastSender:" << bytesToWrite << "bytes written now;"
<< measuredSentBytes << "measured bytes" << measuredSentBytes + writtenBefore << "total ("
<< measuredSentBytes*100/measuredTotalBytes << "% complete);"
<< timer.elapsed() << "ms elapsed";*/
}
transferRate = measuredSentBytes * 1000 / timer.elapsed();
qDebug() << "FastSender: flushed" << measuredSentBytes << "bytes in" << timer.elapsed() << "ms: rate =" << transferRate << "B/s";
// write a "close connection" packet, if the protocol needs it
writeLastData(client);
}
signals:
void dataReady();
};
class RateControlledReader: public QObject
{
Q_OBJECT
QIODevice *device;
int bytesToRead;
int interval;
int readBufferSize;
public:
QByteArray data;
qint64 totalBytesRead;
RateControlledReader(QObject& senderObj, QIODevice *dev, int kbPerSec, int maxBufferSize = 0)
: device(dev), readBufferSize(maxBufferSize), totalBytesRead(0)
{
// determine how often we have to wake up
int timesPerSecond;
if (readBufferSize == 0) {
// The requirement is simply "N KB per seconds"
timesPerSecond = 20;
bytesToRead = kbPerSec * 1024 / timesPerSecond;
} else {
// The requirement also includes "<readBufferSize> bytes at a time"
bytesToRead = readBufferSize;
timesPerSecond = kbPerSec * 1024 / readBufferSize;
}
interval = 1000 / timesPerSecond; // in ms
qDebug() << "RateControlledReader: going to read" << bytesToRead
<< "bytes every" << interval << "ms";
qDebug() << "actual read rate will be"
<< (bytesToRead * 1000 / interval) << "bytes/sec (wanted"
<< kbPerSec * 1024 << "bytes/sec)";
// Wait for data to be readyRead
bool ok = connect(&senderObj, SIGNAL(dataReady()), this, SLOT(slotDataReady()));
Q_ASSERT(ok);
}
void wrapUp()
{
QByteArray someData = device->read(device->bytesAvailable());
data += someData;
totalBytesRead += someData.size();
qDebug() << "wrapUp: found" << someData.size() << "bytes left. progress" << data.size();
//qDebug() << "wrapUp: now bytesAvailable=" << device->bytesAvailable();
}
private slots:
void slotDataReady()
{
//qDebug() << "RateControlledReader: ready to go";
startTimer(interval);
}
protected:
void timerEvent(QTimerEvent *)
{
//qDebug() << "RateControlledReader: timerEvent bytesAvailable=" << device->bytesAvailable();
if (readBufferSize > 0) {
// This asserts passes all the time, except in the final flush.
//Q_ASSERT(device->bytesAvailable() <= readBufferSize);
}
qint64 bytesRead = 0;
QTime stopWatch;
stopWatch.start();
do {
if (device->bytesAvailable() == 0) {
if (stopWatch.elapsed() > 20) {
qDebug() << "RateControlledReader: Not enough data available for reading, waited too much, timing out";
break;
}
if (!device->waitForReadyRead(5)) {
qDebug() << "RateControlledReader: Not enough data available for reading, even after waiting 5ms, bailing out";
break;
}
}
QByteArray someData = device->read(bytesToRead - bytesRead);
data += someData;
bytesRead += someData.size();
//qDebug() << "RateControlledReader: successfully read" << someData.size() << "progress:" << data.size();
} while (bytesRead < bytesToRead);
totalBytesRead += bytesRead;
if (bytesRead < bytesToRead)
qWarning() << "RateControlledReader: WARNING:" << bytesToRead - bytesRead << "bytes not read";
}
};
tst_QNetworkReply::tst_QNetworkReply()
{
qRegisterMetaType<QNetworkReply *>(); // for QSignalSpy
qRegisterMetaType<QAuthenticator *>();
qRegisterMetaType<QNetworkProxy>();
#ifndef QT_NO_OPENSSL
qRegisterMetaType<QList<QSslError> >();
#endif
qRegisterMetaType<QNetworkReply::NetworkError>();
Q_SET_DEFAULT_IAP
testFileName = QDir::currentPath() + "/testfile";
uniqueExtension = createUniqueExtension();
cookieJar = new MyCookieJar;
manager.setCookieJar(cookieJar);
QHostInfo hostInfo = QHostInfo::fromName(QtNetworkSettings::serverName());
proxies << ProxyData(QNetworkProxy::NoProxy, "", false);
if (hostInfo.error() == QHostInfo::NoError && !hostInfo.addresses().isEmpty()) {
QString proxyserver = hostInfo.addresses().first().toString();
proxies << ProxyData(QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3128), "+proxy", false)
<< ProxyData(QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3129), "+proxyauth", true)
// currently unsupported
// << ProxyData(QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3130), "+proxyauth-ntlm", true);
<< ProxyData(QNetworkProxy(QNetworkProxy::Socks5Proxy, proxyserver, 1080), "+socks", false)
<< ProxyData(QNetworkProxy(QNetworkProxy::Socks5Proxy, proxyserver, 1081), "+socksauth", true);
} else {
printf("==================================================================\n");
printf("Proxy could not be looked up. No proxy will be used while testing!\n");
printf("==================================================================\n");
}
}
tst_QNetworkReply::~tst_QNetworkReply()
{
}
void tst_QNetworkReply::authenticationRequired(QNetworkReply*, QAuthenticator* auth)
{
auth->setUser("httptest");
auth->setPassword("httptest");
}
void tst_QNetworkReply::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator* auth)
{
auth->setUser("qsockstest");
auth->setPassword("password");
}
#ifndef QT_NO_OPENSSL
void tst_QNetworkReply::sslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
{
reply->ignoreSslErrors();
QVERIFY(!errors.isEmpty());
QVERIFY(!reply->sslConfiguration().isNull());
}
void tst_QNetworkReply::storeSslConfiguration()
{
storedSslConfiguration = QSslConfiguration();
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
if (reply)
storedSslConfiguration = reply->sslConfiguration();
}
#endif
QString tst_QNetworkReply::runMultipartRequest(const QNetworkRequest &request,
QNetworkReplyPtr &reply,
QHttpMultiPart *multiPart,
const QByteArray &verb)
{
if (verb == "POST")
reply = manager.post(request, multiPart);
else
reply = manager.put(request, multiPart);
// the code below is copied from tst_QNetworkReply::runSimpleRequest, see below
reply->setParent(this);
connect(reply, SIGNAL(finished()), SLOT(finished()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(gotError()));
multiPart->setParent(reply);
returnCode = Timeout;
loop = new QEventLoop;
QTimer::singleShot(25000, loop, SLOT(quit()));
int code = returnCode == Timeout ? loop->exec() : returnCode;
delete loop;
loop = 0;
switch (code) {
case Failure:
return "Request failed: " + reply->errorString();
case Timeout:
return "Network timeout";
}
return QString();
}
QString tst_QNetworkReply::runSimpleRequest(QNetworkAccessManager::Operation op,
const QNetworkRequest &request,
QNetworkReplyPtr &reply,
const QByteArray &data)
{
switch (op) {
case QNetworkAccessManager::HeadOperation:
reply = manager.head(request);
break;
case QNetworkAccessManager::GetOperation:
reply = manager.get(request);
break;
case QNetworkAccessManager::PutOperation:
reply = manager.put(request, data);
break;
case QNetworkAccessManager::PostOperation:
reply = manager.post(request, data);
break;
case QNetworkAccessManager::DeleteOperation:
reply = manager.deleteResource(request);
break;
default:
Q_ASSERT_X(false, "tst_QNetworkReply", "Invalid/unknown operation requested");
}
reply->setParent(this);
returnCode = Timeout;
int code = Success;
if (request.attribute(QNetworkRequest::SynchronousRequestAttribute).toBool()) {
if (reply->isFinished())
code = reply->error() != QNetworkReply::NoError ? Failure : Success;
else
code = Failure;
} else {
connect(reply, SIGNAL(finished()), SLOT(finished()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(gotError()));
loop = new QEventLoop;
QTimer::singleShot(20000, loop, SLOT(quit()));
code = returnCode == Timeout ? loop->exec() : returnCode;
delete loop;
loop = 0;
}
switch (code) {
case Failure:
return "Request failed: " + reply->errorString();
case Timeout:
return "Network timeout";
}
return QString();
}
QString tst_QNetworkReply::runCustomRequest(const QNetworkRequest &request,
QNetworkReplyPtr &reply,
const QByteArray &verb,
QIODevice *data)
{
reply = manager.sendCustomRequest(request, verb, data);
reply->setParent(this);
connect(reply, SIGNAL(finished()), SLOT(finished()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(gotError()));
returnCode = Timeout;
loop = new QEventLoop;
QTimer::singleShot(20000, loop, SLOT(quit()));
int code = returnCode == Timeout ? loop->exec() : returnCode;
delete loop;
loop = 0;
switch (code) {
case Failure:
return "Request failed: " + reply->errorString();
case Timeout:
return "Network timeout";
}
return QString();
}
void tst_QNetworkReply::finished()
{
loop->exit(returnCode = Success);
}
void tst_QNetworkReply::gotError()
{
loop->exit(returnCode = Failure);
disconnect(QObject::sender(), SIGNAL(finished()), this, 0);
}
void tst_QNetworkReply::initTestCase()
{
#if !defined Q_OS_WIN
wronlyFileName = QDir::currentPath() + "/write-only";
QFile wr(wronlyFileName);
QVERIFY(wr.open(QIODevice::WriteOnly | QIODevice::Truncate));
wr.setPermissions(QFile::WriteOwner | QFile::WriteUser);
wr.close();
#endif
QDir::setSearchPaths("srcdir", QStringList() << SRCDIR);
#ifndef QT_NO_OPENSSL
QSslSocket::defaultCaCertificates(); //preload certificates
#endif
#ifndef QT_NO_BEARERMANAGEMENT
netConfMan = new QNetworkConfigurationManager(this);
networkConfiguration = netConfMan->defaultConfiguration();
networkSession.reset(new QNetworkSession(networkConfiguration));
if (!networkSession->isOpen()) {
networkSession->open();
QVERIFY(networkSession->waitForOpened(30000));
}
#endif
}
void tst_QNetworkReply::cleanupTestCase()
{
#if !defined Q_OS_WIN
QFile::remove(wronlyFileName);
#endif
if (networkSession && networkSession->isOpen()) {
networkSession->close();
}
}
void tst_QNetworkReply::init()
{
cleanup();
}
void tst_QNetworkReply::cleanup()
{
QFile file(testFileName);
QVERIFY(!file.exists() || file.remove());
// clear the internal cache
QNetworkAccessManagerPrivate::clearCache(&manager);
manager.setProxy(QNetworkProxy());
manager.setCache(0);
// clear cookies
cookieJar->setAllCookies(QList<QNetworkCookie>());
}
void tst_QNetworkReply::stateChecking()
{
QUrl url = QUrl("file:///");
QNetworkRequest req(url); // you can't open this file, I know
QNetworkReplyPtr reply = manager.get(req);
QVERIFY(reply.data());
QVERIFY(reply->isOpen());
QVERIFY(reply->isReadable());
QVERIFY(!reply->isWritable());
// both behaviours are OK since we might change underlying behaviour again
if (!reply->isFinished())
QCOMPARE(reply->errorString(), QString("Unknown error"));
else
QVERIFY(!reply->errorString().isEmpty());
QCOMPARE(reply->manager(), &manager);
QCOMPARE(reply->request(), req);
QCOMPARE(int(reply->operation()), int(QNetworkAccessManager::GetOperation));
// error and not error are OK since we might change underlying behaviour again
if (!reply->isFinished())
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->url(), url);
reply->abort();
}
void tst_QNetworkReply::invalidProtocol()
{
QUrl url = QUrl::fromEncoded("not-a-known-protocol://foo/bar");
QNetworkRequest req(url);
QNetworkReplyPtr reply;
QString errorMsg = "Request failed: Protocol \"not-a-known-protocol\" is unknown";
QString result = runSimpleRequest(QNetworkAccessManager::GetOperation, req, reply);
QCOMPARE(result, errorMsg);
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::ProtocolUnknownError);
}
void tst_QNetworkReply::getFromData_data()
{
QTest::addColumn<QString>("request");
QTest::addColumn<QByteArray>("expected");
QTest::addColumn<QString>("mimeType");
const QString defaultMimeType("text/plain;charset=US-ASCII");
//QTest::newRow("empty") << "data:" << QByteArray() << defaultMimeType;
QTest::newRow("empty2") << "data:," << QByteArray() << defaultMimeType;
QTest::newRow("just-charset_1") << "data:charset=iso-8859-1,"
<< QByteArray() << "text/plain;charset=iso-8859-1";
QTest::newRow("just-charset_2") << "data:charset = iso-8859-1 ,"
<< QByteArray() << "text/plain;charset = iso-8859-1";
//QTest::newRow("just-media") << "data:text/xml" << QByteArray() << "text/xml";
QTest::newRow("just-media2") << "data:text/xml," << QByteArray() << "text/xml";
QTest::newRow("plain_1") << "data:,foo" << QByteArray("foo") << defaultMimeType;
QTest::newRow("plain_2") << "data:text/html,Hello World" << QByteArray("Hello World")
<< "text/html";
QTest::newRow("plain_3") << "data:text/html;charset=utf-8,Hello World"
<< QByteArray("Hello World") << "text/html;charset=utf-8";
QTest::newRow("pct_1") << "data:,%3Cbody%20contentEditable%3Dtrue%3E%0D%0A"
<< QByteArray("<body contentEditable=true>\r\n") << defaultMimeType;
QTest::newRow("pct_2") << "data:text/html;charset=utf-8,%3Cbody%20contentEditable%3Dtrue%3E%0D%0A"
<< QByteArray("<body contentEditable=true>\r\n")
<< "text/html;charset=utf-8";
QTest::newRow("base64-empty_1") << "data:;base64," << QByteArray() << defaultMimeType;
QTest::newRow("base64-empty_2") << "data:charset=utf-8;base64," << QByteArray()
<< "text/plain;charset=utf-8";
QTest::newRow("base64-empty_3") << "data:text/html;charset=utf-8;base64,"
<< QByteArray() << "text/html;charset=utf-8";
QTest::newRow("base64_1") << "data:;base64,UXQgaXMgZ3JlYXQh" << QByteArray("Qt is great!")
<< defaultMimeType;
QTest::newRow("base64_2") << "data:charset=utf-8;base64,UXQgaXMgZ3JlYXQh"
<< QByteArray("Qt is great!") << "text/plain;charset=utf-8";
QTest::newRow("base64_3") << "data:text/html;charset=utf-8;base64,UXQgaXMgZ3JlYXQh"
<< QByteArray("Qt is great!") << "text/html;charset=utf-8";
QTest::newRow("pct-nul") << "data:,a%00g" << QByteArray("a\0g", 3) << defaultMimeType;
QTest::newRow("base64-nul") << "data:;base64,YQBn" << QByteArray("a\0g", 3) << defaultMimeType;
QTest::newRow("pct-nonutf8") << "data:,a%E1g" << QByteArray("a\xE1g", 3) << defaultMimeType;
QTest::newRow("base64")
<< QString::fromLatin1("data:application/xml;base64,PGUvPg==")
<< QByteArray("<e/>")
<< "application/xml";
QTest::newRow("base64, no media type")
<< QString::fromLatin1("data:;base64,PGUvPg==")
<< QByteArray("<e/>")
<< defaultMimeType;
QTest::newRow("Percent encoding")
<< QString::fromLatin1("data:application/xml,%3Ce%2F%3E")
<< QByteArray("<e/>")
<< "application/xml";
QTest::newRow("Percent encoding, no media type")
<< QString::fromLatin1("data:,%3Ce%2F%3E")
<< QByteArray("<e/>")
<< defaultMimeType;
QTest::newRow("querychars")
<< QString::fromLatin1("data:,foo?x=0&y=0")
<< QByteArray("foo?x=0&y=0")
<< defaultMimeType;
QTest::newRow("css") << "data:text/css,div%20{%20border-right:%20solid;%20}"
<< QByteArray("div { border-right: solid; }")
<< "text/css";
}
void tst_QNetworkReply::getFromData()
{
QFETCH(QString, request);
QFETCH(QByteArray, expected);
QFETCH(QString, mimeType);
QUrl url = QUrl::fromEncoded(request.toLatin1());
QNetworkRequest req(url);
QNetworkReplyPtr reply;
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, req, reply));
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader).toString(), mimeType);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(expected.size()));
QCOMPARE(reply->readAll(), expected);
}
void tst_QNetworkReply::getFromFile()
{
// create the file:
QTemporaryFile file(QDir::currentPath() + "/temp-XXXXXX");
file.setAutoRemove(true);
QVERIFY(file.open());
QNetworkRequest request(QUrl::fromLocalFile(file.fileName()));
QNetworkReplyPtr reply;
static const char fileData[] = "This is some data that is in the file.\r\n";
QByteArray data = QByteArray::fromRawData(fileData, sizeof fileData - 1);
QVERIFY(file.write(data) == data.size());
file.flush();
QCOMPARE(file.size(), qint64(data.size()));
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
QCOMPARE(reply->url(), request.url());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), file.size());
QCOMPARE(reply->readAll(), data);
// make the file bigger
file.resize(0);
const int multiply = (128 * 1024) / (sizeof fileData - 1);
for (int i = 0; i < multiply; ++i)
file.write(fileData, sizeof fileData - 1);
file.flush();
// run again
reply = 0;
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
QCOMPARE(reply->url(), request.url());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), file.size());
QCOMPARE(qint64(reply->readAll().size()), file.size());
}
void tst_QNetworkReply::getFromFileSpecial_data()
{
QTest::addColumn<QString>("fileName");
QTest::addColumn<QString>("url");
QTest::newRow("resource") << ":/resource" << "qrc:/resource";
QTest::newRow("search-path") << "srcdir:/rfc3252.txt" << "srcdir:/rfc3252.txt";
QTest::newRow("bigfile-path") << "srcdir:/bigfile" << "srcdir:/bigfile";
#ifdef Q_OS_WIN
QTest::newRow("smb-path") << "srcdir:/smb-file.txt" << "file://" + QtNetworkSettings::winServerName() + "/testshare/test.pri";
#endif
}
void tst_QNetworkReply::getFromFileSpecial()
{
QFETCH(QString, fileName);
QFETCH(QString, url);
// open the resource so we can find out its size
QFile resource(fileName);
QVERIFY(resource.open(QIODevice::ReadOnly));
QNetworkRequest request;
QNetworkReplyPtr reply;
request.setUrl(url);
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
QCOMPARE(reply->url(), request.url());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), resource.size());
QCOMPARE(reply->readAll(), resource.readAll());
}
void tst_QNetworkReply::getFromFtp_data()
{
QTest::addColumn<QString>("referenceName");
QTest::addColumn<QString>("url");
QTest::newRow("rfc3252.txt") << SRCDIR "/rfc3252.txt" << "ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt";
QTest::newRow("bigfile") << SRCDIR "/bigfile" << "ftp://" + QtNetworkSettings::serverName() + "/qtest/bigfile";
}
void tst_QNetworkReply::getFromFtp()
{
QFETCH(QString, referenceName);
QFETCH(QString, url);
QFile reference(referenceName);
QVERIFY(reference.open(QIODevice::ReadOnly));
QNetworkRequest request(url);
QNetworkReplyPtr reply;
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
QCOMPARE(reply->url(), request.url());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
QCOMPARE(reply->readAll(), reference.readAll());
}
void tst_QNetworkReply::getFromHttp_data()
{
QTest::addColumn<QString>("referenceName");
QTest::addColumn<QString>("url");
QTest::newRow("success-internal") << SRCDIR "/rfc3252.txt" << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt";
QTest::newRow("success-external") << SRCDIR "/rfc3252.txt" << "http://www.ietf.org/rfc/rfc3252.txt";
QTest::newRow("bigfile-internal") << SRCDIR "/bigfile" << "http://" + QtNetworkSettings::serverName() + "/qtest/bigfile";
}
void tst_QNetworkReply::getFromHttp()
{
QFETCH(QString, referenceName);
QFETCH(QString, url);
QFile reference(referenceName);
QVERIFY(reference.open(QIODevice::ReadOnly));
QNetworkRequest request(url);
QNetworkReplyPtr reply;
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
QCOMPARE(reply->url(), request.url());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reply->size(), reference.size());
// only compare when the header is set.
if (reply->header(QNetworkRequest::ContentLengthHeader).isValid())
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
QCOMPARE(reply->readAll(), reference.readAll());
}
void tst_QNetworkReply::getErrors_data()
{
QTest::addColumn<QString>("url");
QTest::addColumn<int>("error");
QTest::addColumn<int>("httpStatusCode");
QTest::addColumn<bool>("dataIsEmpty");
// empties
QTest::newRow("empty-url") << QString() << int(QNetworkReply::ProtocolUnknownError) << 0 << true;
QTest::newRow("empty-scheme-host") << SRCDIR "/rfc3252.txt" << int(QNetworkReply::ProtocolUnknownError) << 0 << true;
QTest::newRow("empty-scheme") << "//" + QtNetworkSettings::winServerName() + "/testshare/test.pri"
<< int(QNetworkReply::ProtocolUnknownError) << 0 << true;
// file: errors
QTest::newRow("file-host") << "file://this-host-doesnt-exist.troll.no/foo.txt"
#if !defined Q_OS_WIN
<< int(QNetworkReply::ProtocolInvalidOperationError) << 0 << true;
#else
<< int(QNetworkReply::ContentNotFoundError) << 0 << true;
#endif
QTest::newRow("file-no-path") << "file://localhost"
<< int(QNetworkReply::ContentOperationNotPermittedError) << 0 << true;
QTest::newRow("file-is-dir") << QUrl::fromLocalFile(QDir::currentPath()).toString()
<< int(QNetworkReply::ContentOperationNotPermittedError) << 0 << true;
QTest::newRow("file-exist") << QUrl::fromLocalFile(QDir::currentPath() + "/this-file-doesnt-exist.txt").toString()
<< int(QNetworkReply::ContentNotFoundError) << 0 << true;
#if !defined Q_OS_WIN && !defined(Q_OS_SYMBIAN)
QTest::newRow("file-is-wronly") << QUrl::fromLocalFile(wronlyFileName).toString()
<< int(QNetworkReply::ContentAccessDenied) << 0 << true;
#endif
if (QFile::exists("/etc/shadow"))
QTest::newRow("file-permissions") << "file:/etc/shadow"
<< int(QNetworkReply::ContentAccessDenied) << 0 << true;
// ftp: errors
QTest::newRow("ftp-host") << "ftp://this-host-doesnt-exist.troll.no/foo.txt"
<< int(QNetworkReply::HostNotFoundError) << 0 << true;
QTest::newRow("ftp-no-path") << "ftp://" + QtNetworkSettings::serverName()
<< int(QNetworkReply::ContentOperationNotPermittedError) << 0 << true;
QTest::newRow("ftp-is-dir") << "ftp://" + QtNetworkSettings::serverName() + "/qtest"
<< int(QNetworkReply::ContentOperationNotPermittedError) << 0 << true;
QTest::newRow("ftp-dir-not-readable") << "ftp://" + QtNetworkSettings::serverName() + "/pub/dir-not-readable/foo.txt"
<< int(QNetworkReply::ContentAccessDenied) << 0 << true;
QTest::newRow("ftp-file-not-readable") << "ftp://" + QtNetworkSettings::serverName() + "/pub/file-not-readable.txt"
<< int(QNetworkReply::ContentAccessDenied) << 0 << true;
QTest::newRow("ftp-exist") << "ftp://" + QtNetworkSettings::serverName() + "/pub/this-file-doesnt-exist.txt"
<< int(QNetworkReply::ContentNotFoundError) << 0 << true;
// http: errors
QTest::newRow("http-host") << "http://this-host-will-never-exist.troll.no/"
<< int(QNetworkReply::HostNotFoundError) << 0 << true;
QTest::newRow("http-exist") << "http://" + QtNetworkSettings::serverName() + "/this-file-doesnt-exist.txt"
<< int(QNetworkReply::ContentNotFoundError) << 404 << false;
QTest::newRow("http-authentication") << "http://" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth"
<< int(QNetworkReply::AuthenticationRequiredError) << 401 << false;
}
void tst_QNetworkReply::getErrors()
{
QFETCH(QString, url);
QNetworkRequest request(url);
#if defined(Q_OS_WIN) || defined (Q_OS_SYMBIAN)
if (qstrcmp(QTest::currentDataTag(), "empty-scheme-host") == 0 && QFileInfo(url).isAbsolute())
QTest::ignoreMessage(QtWarningMsg, "QNetworkAccessFileBackendFactory: URL has no schema set, use file:// for files");
#endif
QNetworkReplyPtr reply = manager.get(request);
reply->setParent(this); // we have expect-fails
if (!reply->isFinished())
QCOMPARE(reply->error(), QNetworkReply::NoError);
// now run the request:
connect(reply, SIGNAL(finished()),
&QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
//qDebug() << reply->errorString();
QFETCH(int, error);
#if defined(Q_OS_WIN) || defined (Q_OS_SYMBIAN)
if (QFileInfo(url).isAbsolute())
QEXPECT_FAIL("empty-scheme-host", "this is expected to fail on Windows and Symbian, QTBUG-17731", Abort);
#endif
QEXPECT_FAIL("ftp-is-dir", "QFtp cannot provide enough detail", Abort);
// the line below is not necessary
QEXPECT_FAIL("ftp-dir-not-readable", "QFtp cannot provide enough detail", Abort);
QCOMPARE(reply->error(), QNetworkReply::NetworkError(error));
QTEST(reply->readAll().isEmpty(), "dataIsEmpty");
QVERIFY(reply->isFinished());
QVERIFY(!reply->isRunning());
QFETCH(int, httpStatusCode);
if (httpStatusCode != 0) {
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), httpStatusCode);
}
}
static inline QByteArray md5sum(const QByteArray &data)
{
return QCryptographicHash::hash(data, QCryptographicHash::Md5);
}
void tst_QNetworkReply::putToFile_data()
{
QTest::addColumn<QByteArray>("data");
QTest::addColumn<QByteArray>("md5sum");
QByteArray data;
data = "";
QTest::newRow("empty") << data << md5sum(data);
data = "This is a normal message.";
QTest::newRow("generic") << data << md5sum(data);
data = "This is a message to show that Qt rocks!\r\n\n";
QTest::newRow("small") << data << md5sum(data);
data = QByteArray("abcd\0\1\2\abcd",12);
QTest::newRow("with-nul") << data << md5sum(data);
data = QByteArray(4097, '\4');
QTest::newRow("4k+1") << data << md5sum(data);
data = QByteArray(128*1024+1, '\177');
QTest::newRow("128k+1") << data << md5sum(data);
data = QByteArray(2*1024*1024+1, '\177');
QTest::newRow("2MB+1") << data << md5sum(data);
}
void tst_QNetworkReply::putToFile()
{
QFile file(testFileName);
QUrl url = QUrl::fromLocalFile(file.fileName());
QNetworkRequest request(url);
QNetworkReplyPtr reply;
QFETCH(QByteArray, data);
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data));
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
QVERIFY(reply->readAll().isEmpty());
QVERIFY(file.open(QIODevice::ReadOnly));
QCOMPARE(file.size(), qint64(data.size()));
QByteArray contents = file.readAll();
QCOMPARE(contents, data);
}
void tst_QNetworkReply::putToFtp_data()
{
putToFile_data();
}
void tst_QNetworkReply::putToFtp()
{
QUrl url("ftp://" + QtNetworkSettings::serverName());
url.setPath(QString("/qtest/upload/qnetworkaccess-putToFtp-%1-%2")
.arg(QTest::currentDataTag())
.arg(uniqueExtension));
QNetworkRequest request(url);
QNetworkReplyPtr reply;
QFETCH(QByteArray, data);
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data));
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
QVERIFY(reply->readAll().isEmpty());
// download the file again from FTP to make sure it was uploaded
// correctly
QFtp ftp;
ftp.connectToHost(url.host());
ftp.login();
ftp.get(url.path());
QObject::connect(&ftp, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QObject::disconnect(&ftp, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QByteArray uploaded = ftp.readAll();
QCOMPARE(uploaded.size(), data.size());
QCOMPARE(uploaded, data);
ftp.close();
QObject::connect(&ftp, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QObject::disconnect(&ftp, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
}
void tst_QNetworkReply::putToHttp_data()
{
putToFile_data();
}
void tst_QNetworkReply::putToHttp()
{
QUrl url("http://" + QtNetworkSettings::serverName());
url.setPath(QString("/dav/qnetworkaccess-putToHttp-%1-%2")
.arg(QTest::currentDataTag())
.arg(uniqueExtension));
QNetworkRequest request(url);
QNetworkReplyPtr reply;
QFETCH(QByteArray, data);
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data));
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 201); // 201 Created
// download the file again from HTTP to make sure it was uploaded
// correctly. HTTP/0.9 is enough
QTcpSocket socket;
socket.connectToHost(QtNetworkSettings::serverName(), 80);
socket.write("GET " + url.toEncoded(QUrl::RemoveScheme | QUrl::RemoveAuthority) + "\r\n");
if (!socket.waitForDisconnected(10000))
QFAIL("Network timeout");
QByteArray uploadedData = socket.readAll();
QCOMPARE(uploadedData, data);
}
void tst_QNetworkReply::putToHttpSynchronous_data()
{
uniqueExtension = createUniqueExtension();
putToFile_data();
}
void tst_QNetworkReply::putToHttpSynchronous()
{
QUrl url("http://" + QtNetworkSettings::serverName());
url.setPath(QString("/dav/qnetworkaccess-putToHttp-%1-%2")
.arg(QTest::currentDataTag())
.arg(uniqueExtension));
QNetworkRequest request(url);
QNetworkReplyPtr reply;
QFETCH(QByteArray, data);
request.setAttribute(
QNetworkRequest::SynchronousRequestAttribute,
true);
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data));
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 201); // 201 Created
// download the file again from HTTP to make sure it was uploaded
// correctly. HTTP/0.9 is enough
QTcpSocket socket;
socket.connectToHost(QtNetworkSettings::serverName(), 80);
socket.write("GET " + url.toEncoded(QUrl::RemoveScheme | QUrl::RemoveAuthority) + "\r\n");
if (!socket.waitForDisconnected(10000))
QFAIL("Network timeout");
QByteArray uploadedData = socket.readAll();
QCOMPARE(uploadedData, data);
}
void tst_QNetworkReply::postToHttp_data()
{
putToFile_data();
}
void tst_QNetworkReply::postToHttp()
{
QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi");
QNetworkRequest request(url);
request.setRawHeader("Content-Type", "application/octet-stream");
QNetworkReplyPtr reply;
QFETCH(QByteArray, data);
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data));
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
QFETCH(QByteArray, md5sum);
QByteArray uploadedData = reply->readAll().trimmed();
QCOMPARE(uploadedData, md5sum.toHex());
}
void tst_QNetworkReply::postToHttpSynchronous_data()
{
putToFile_data();
}
void tst_QNetworkReply::postToHttpSynchronous()
{
QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi");
QNetworkRequest request(url);
request.setRawHeader("Content-Type", "application/octet-stream");
request.setAttribute(
QNetworkRequest::SynchronousRequestAttribute,
true);
QNetworkReplyPtr reply;
QFETCH(QByteArray, data);
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data));
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
QFETCH(QByteArray, md5sum);
QByteArray uploadedData = reply->readAll().trimmed();
QCOMPARE(uploadedData, md5sum.toHex());
}
void tst_QNetworkReply::postToHttpMultipart_data()
{
QTest::addColumn<QUrl>("url");
QTest::addColumn<QHttpMultiPart *>("multiPart");
QTest::addColumn<QByteArray>("expectedReplyData");
QTest::addColumn<QByteArray>("contentType");
QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/multipart.cgi");
QByteArray expectedData;
// empty parts
QHttpMultiPart *emptyMultiPart = new QHttpMultiPart;
QTest::newRow("empty") << url << emptyMultiPart << expectedData << QByteArray("mixed");
QHttpMultiPart *emptyRelatedMultiPart = new QHttpMultiPart;
emptyRelatedMultiPart->setContentType(QHttpMultiPart::RelatedType);
QTest::newRow("empty-related") << url << emptyRelatedMultiPart << expectedData << QByteArray("related");
QHttpMultiPart *emptyAlternativeMultiPart = new QHttpMultiPart;
emptyAlternativeMultiPart->setContentType(QHttpMultiPart::AlternativeType);
QTest::newRow("empty-alternative") << url << emptyAlternativeMultiPart << expectedData << QByteArray("alternative");
// text-only parts
QHttpPart textPart;
textPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"text\""));
textPart.setBody("7 bytes");
QHttpMultiPart *multiPart1 = new QHttpMultiPart;
multiPart1->setContentType(QHttpMultiPart::FormDataType);
multiPart1->append(textPart);
expectedData = "key: text, value: 7 bytes\n";
QTest::newRow("text") << url << multiPart1 << expectedData << QByteArray("form-data");
QHttpMultiPart *customMultiPart = new QHttpMultiPart;
customMultiPart->append(textPart);
expectedData = "header: Content-Type, value: 'text/plain'\n"
"header: Content-Disposition, value: 'form-data; name=\"text\"'\n"
"content: 7 bytes\n"
"\n";
QTest::newRow("text-custom") << url << customMultiPart << expectedData << QByteArray("custom");
QHttpPart textPart2;
textPart2.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
textPart2.setRawHeader("myRawHeader", "myValue");
textPart2.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"text2\""));
textPart2.setBody("some more bytes");
textPart2.setBodyDevice((QIODevice *) 1); // test whether setting and unsetting of the device works
textPart2.setBodyDevice(0);
QHttpMultiPart *multiPart2 = new QHttpMultiPart;
multiPart2->setContentType(QHttpMultiPart::FormDataType);
multiPart2->append(textPart);
multiPart2->append(textPart2);
expectedData = "key: text2, value: some more bytes\n"
"key: text, value: 7 bytes\n";
QTest::newRow("text-text") << url << multiPart2 << expectedData << QByteArray("form-data");
QHttpPart textPart3;
textPart3.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
textPart3.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"text3\""));
textPart3.setRawHeader("Content-Location", "http://my.test.location.tld");
textPart3.setBody("even more bytes");
QHttpMultiPart *multiPart3 = new QHttpMultiPart;
multiPart3->setContentType(QHttpMultiPart::AlternativeType);
multiPart3->append(textPart);
multiPart3->append(textPart2);
multiPart3->append(textPart3);
expectedData = "header: Content-Type, value: 'text/plain'\n"
"header: Content-Disposition, value: 'form-data; name=\"text\"'\n"
"content: 7 bytes\n"
"\n"
"header: Content-Type, value: 'text/plain'\n"
"header: myRawHeader, value: 'myValue'\n"
"header: Content-Disposition, value: 'form-data; name=\"text2\"'\n"
"content: some more bytes\n"
"\n"
"header: Content-Type, value: 'text/plain'\n"
"header: Content-Disposition, value: 'form-data; name=\"text3\"'\n"
"header: Content-Location, value: 'http://my.test.location.tld'\n"
"content: even more bytes\n\n";
QTest::newRow("text-text-text") << url << multiPart3 << expectedData << QByteArray("alternative");
// text and image parts
QHttpPart imagePart11;
imagePart11.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
imagePart11.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage\""));
imagePart11.setRawHeader("Content-Location", "http://my.test.location.tld");
imagePart11.setRawHeader("Content-ID", "my@id.tld");
QFile *file11 = new QFile(SRCDIR "/image1.jpg");
file11->open(QIODevice::ReadOnly);
imagePart11.setBodyDevice(file11);
QHttpMultiPart *imageMultiPart1 = new QHttpMultiPart(QHttpMultiPart::FormDataType);
imageMultiPart1->append(imagePart11);
file11->setParent(imageMultiPart1);
expectedData = "key: testImage, value: 87ef3bb319b004ba9e5e9c9fa713776e\n"; // md5 sum of file
QTest::newRow("image") << url << imageMultiPart1 << expectedData << QByteArray("form-data");
QHttpPart imagePart21;
imagePart21.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
imagePart21.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage1\""));
imagePart21.setRawHeader("Content-Location", "http://my.test.location.tld");
imagePart21.setRawHeader("Content-ID", "my@id.tld");
QFile *file21 = new QFile(SRCDIR "/image1.jpg");
file21->open(QIODevice::ReadOnly);
imagePart21.setBodyDevice(file21);
QHttpMultiPart *imageMultiPart2 = new QHttpMultiPart();
imageMultiPart2->setContentType(QHttpMultiPart::FormDataType);
imageMultiPart2->append(textPart);
imageMultiPart2->append(imagePart21);
file21->setParent(imageMultiPart2);
QHttpPart imagePart22;
imagePart22.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
imagePart22.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage2\""));
QFile *file22 = new QFile(SRCDIR "/image2.jpg");
file22->open(QIODevice::ReadOnly);
imagePart22.setBodyDevice(file22);
imageMultiPart2->append(imagePart22);
file22->setParent(imageMultiPart2);
expectedData = "key: testImage1, value: 87ef3bb319b004ba9e5e9c9fa713776e\n"
"key: text, value: 7 bytes\n"
"key: testImage2, value: 483761b893f7fb1bd2414344cd1f3dfb\n";
QTest::newRow("text-image-image") << url << imageMultiPart2 << expectedData << QByteArray("form-data");
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(SRCDIR "/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(SRCDIR "/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(SRCDIR "/image3.jpg");
file33->open(QIODevice::ReadOnly);
imagePart33.setBodyDevice(file33);
imageMultiPart3->append(imagePart33);
file33->setParent(imageMultiPart3);
expectedData = "key: testImage1, value: 87ef3bb319b004ba9e5e9c9fa713776e\n"
"key: testImage2, value: 483761b893f7fb1bd2414344cd1f3dfb\n"
"key: testImage3, value: ab0eb6fd4fcf8b4436254870b4513033\n";
QTest::newRow("3-images") << url << imageMultiPart3 << expectedData << QByteArray("form-data");
// note: nesting multiparts is not working currently; for that, the outputDevice would need to be public
// QHttpPart imagePart41;
// imagePart41.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
// QFile *file41 = new QFile(SRCDIR "/image1.jpg");
// file41->open(QIODevice::ReadOnly);
// imagePart41.setBodyDevice(file41);
//
// QHttpMultiPart *innerMultiPart = new QHttpMultiPart();
// innerMultiPart->setContentType(QHttpMultiPart::FormDataType);
// textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant());
// innerMultiPart->append(textPart);
// innerMultiPart->append(imagePart41);
// textPart2.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant());
// innerMultiPart->append(textPart2);
//
// QHttpPart nestedPart;
// nestedPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"nestedMessage"));
// nestedPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("multipart/alternative; boundary=\"" + innerMultiPart->boundary() + "\""));
// innerMultiPart->outputDevice()->open(QIODevice::ReadOnly);
// nestedPart.setBodyDevice(innerMultiPart->outputDevice());
//
// QHttpMultiPart *outerMultiPart = new QHttpMultiPart;
// outerMultiPart->setContentType(QHttpMultiPart::FormDataType);
// outerMultiPart->append(textPart);
// outerMultiPart->append(nestedPart);
// outerMultiPart->append(textPart2);
// expectedData = "nothing"; // the CGI.pm module running on the test server does not understand nested multiparts
// openFiles.clear();
// openFiles << file41;
// QTest::newRow("nested") << url << outerMultiPart << expectedData << openFiles;
// test setting large chunks of content with a byte array instead of a device (DISCOURAGED because of high memory consumption,
// but we need to test that the behavior is correct)
QHttpPart imagePart51;
imagePart51.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
imagePart51.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage\""));
QFile *file51 = new QFile(SRCDIR "/image1.jpg");
file51->open(QIODevice::ReadOnly);
QByteArray imageData = file51->readAll();
file51->close();
delete file51;
imagePart51.setBody("7 bytes"); // check that resetting works
imagePart51.setBody(imageData);
QHttpMultiPart *imageMultiPart5 = new QHttpMultiPart;
imageMultiPart5->setContentType(QHttpMultiPart::FormDataType);
imageMultiPart5->append(imagePart51);
expectedData = "key: testImage, value: 87ef3bb319b004ba9e5e9c9fa713776e\n"; // md5 sum of file
QTest::newRow("image-as-content") << url << imageMultiPart5 << expectedData << QByteArray("form-data");
}
void tst_QNetworkReply::postToHttpMultipart()
{
QFETCH(QUrl, url);
static QSet<QByteArray> boundaries;
QNetworkRequest request(url);
QNetworkReplyPtr reply;
QFETCH(QHttpMultiPart *, multiPart);
QFETCH(QByteArray, expectedReplyData);
QFETCH(QByteArray, contentType);
// hack for testing the setting of the content-type header by hand:
if (contentType == "custom") {
QByteArray contentType("multipart/custom; boundary=\"" + multiPart->boundary() + "\"");
request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
}
QVERIFY2(! boundaries.contains(multiPart->boundary()), "boundary '" + multiPart->boundary() + "' has been created twice");
boundaries.insert(multiPart->boundary());
RUN_REQUEST(runMultipartRequest(request, reply, multiPart, "POST"));
multiPart->deleteLater();
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
QVERIFY(multiPart->boundary().count() > 20); // check that there is randomness after the "boundary_.oOo._" string
QVERIFY(multiPart->boundary().count() < 70);
QByteArray replyData = reply->readAll();
expectedReplyData.prepend("content type: multipart/" + contentType + "; boundary=\"" + multiPart->boundary() + "\"\n");
// QEXPECT_FAIL("nested", "the server does not understand nested multipart messages", Continue); // see above
QCOMPARE(replyData, expectedReplyData);
}
void tst_QNetworkReply::putToHttpMultipart_data()
{
postToHttpMultipart_data();
}
void tst_QNetworkReply::putToHttpMultipart()
{
QSKIP("test server script cannot handle PUT data yet", SkipAll);
QFETCH(QUrl, url);
static QSet<QByteArray> boundaries;
QNetworkRequest request(url);
QNetworkReplyPtr reply;
QFETCH(QHttpMultiPart *, multiPart);
QFETCH(QByteArray, expectedReplyData);
QFETCH(QByteArray, contentType);
// hack for testing the setting of the content-type header by hand:
if (contentType == "custom") {
QByteArray contentType("multipart/custom; boundary=\"" + multiPart->boundary() + "\"");
request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
}
QVERIFY2(! boundaries.contains(multiPart->boundary()), "boundary '" + multiPart->boundary() + "' has been created twice");
boundaries.insert(multiPart->boundary());
RUN_REQUEST(runMultipartRequest(request, reply, multiPart, "PUT"));
multiPart->deleteLater();
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
QVERIFY(multiPart->boundary().count() > 20); // check that there is randomness after the "boundary_.oOo._" string
QVERIFY(multiPart->boundary().count() < 70);
QByteArray replyData = reply->readAll();
expectedReplyData.prepend("content type: multipart/" + contentType + "; boundary=\"" + multiPart->boundary() + "\"\n");
// QEXPECT_FAIL("nested", "the server does not understand nested multipart messages", Continue); // see above
QCOMPARE(replyData, expectedReplyData);
}
void tst_QNetworkReply::deleteFromHttp_data()
{
QTest::addColumn<QUrl>("url");
QTest::addColumn<int>("resultCode");
QTest::addColumn<QNetworkReply::NetworkError>("error");
// for status codes to expect, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
QTest::newRow("405-method-not-allowed") << QUrl("http://" + QtNetworkSettings::serverName() + "/index.html") << 405 << QNetworkReply::ContentOperationNotPermittedError;
QTest::newRow("200-ok") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/http-delete.cgi?200-ok") << 200 << QNetworkReply::NoError;
QTest::newRow("202-accepted") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/http-delete.cgi?202-accepted") << 202 << QNetworkReply::NoError;
QTest::newRow("204-no-content") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/http-delete.cgi?204-no-content") << 204 << QNetworkReply::NoError;
QTest::newRow("404-not-found") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/http-delete.cgi?404-not-found") << 404 << QNetworkReply::ContentNotFoundError;
}
void tst_QNetworkReply::deleteFromHttp()
{
QFETCH(QUrl, url);
QFETCH(int, resultCode);
QFETCH(QNetworkReply::NetworkError, error);
QNetworkRequest request(url);
QNetworkReplyPtr reply;
runSimpleRequest(QNetworkAccessManager::DeleteOperation, request, reply, 0);
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), error);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), resultCode);
}
void tst_QNetworkReply::putGetDeleteGetFromHttp_data()
{
QTest::addColumn<QUrl>("putUrl");
QTest::addColumn<int>("putResultCode");
QTest::addColumn<QNetworkReply::NetworkError>("putError");
QTest::addColumn<QUrl>("deleteUrl");
QTest::addColumn<int>("deleteResultCode");
QTest::addColumn<QNetworkReply::NetworkError>("deleteError");
QTest::addColumn<QUrl>("get2Url");
QTest::addColumn<int>("get2ResultCode");
QTest::addColumn<QNetworkReply::NetworkError>("get2Error");
QUrl url("http://" + QtNetworkSettings::serverName());
url.setPath(QString("/dav/qnetworkaccess-putToHttp-%1-%2")
.arg(QTest::currentDataTag())
.arg(uniqueExtension));
// first use case: put, get (to check it is there), delete, get (to check it is not there anymore)
QTest::newRow("success") << url << 201 << QNetworkReply::NoError << url << 204 << QNetworkReply::NoError << url << 404 << QNetworkReply::ContentNotFoundError;
QUrl wrongUrl("http://" + QtNetworkSettings::serverName());
wrongUrl.setPath(QString("/dav/qnetworkaccess-thisURLisNotAvailable"));
// second use case: put, get (to check it is there), delete wrong URL, get (to check it is still there)
QTest::newRow("delete-error") << url << 201 << QNetworkReply::NoError << wrongUrl << 404 << QNetworkReply::ContentNotFoundError << url << 200 << QNetworkReply::NoError;
}
void tst_QNetworkReply::putGetDeleteGetFromHttp()
{
QFETCH(QUrl, putUrl);
QFETCH(int, putResultCode);
QFETCH(QNetworkReply::NetworkError, putError);
QFETCH(QUrl, deleteUrl);
QFETCH(int, deleteResultCode);
QFETCH(QNetworkReply::NetworkError, deleteError);
QFETCH(QUrl, get2Url);
QFETCH(int, get2ResultCode);
QFETCH(QNetworkReply::NetworkError, get2Error);
QNetworkRequest putRequest(putUrl);
QNetworkRequest deleteRequest(deleteUrl);
QNetworkRequest get2Request(get2Url);
QNetworkReplyPtr reply;
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, putRequest, reply, 0));
QCOMPARE(reply->error(), putError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), putResultCode);
runSimpleRequest(QNetworkAccessManager::GetOperation, putRequest, reply, 0);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
runSimpleRequest(QNetworkAccessManager::DeleteOperation, deleteRequest, reply, 0);
QCOMPARE(reply->error(), deleteError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), deleteResultCode);
runSimpleRequest(QNetworkAccessManager::GetOperation, get2Request, reply, 0);
QCOMPARE(reply->error(), get2Error);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), get2ResultCode);
}
void tst_QNetworkReply::sendCustomRequestToHttp_data()
{
QTest::addColumn<QUrl>("url");
QTest::addColumn<QByteArray>("verb");
QTest::addColumn<QBuffer *>("device");
QTest::addColumn<int>("resultCode");
QTest::addColumn<QNetworkReply::NetworkError>("error");
QTest::addColumn<QByteArray>("expectedContent");
QTest::newRow("options") << QUrl("http://" + QtNetworkSettings::serverName()) <<
QByteArray("OPTIONS") << (QBuffer *) 0 << 200 << QNetworkReply::NoError << QByteArray();
QTest::newRow("trace") << QUrl("http://" + QtNetworkSettings::serverName()) <<
QByteArray("TRACE") << (QBuffer *) 0 << 200 << QNetworkReply::NoError << QByteArray();
QTest::newRow("connect") << QUrl("http://" + QtNetworkSettings::serverName()) <<
QByteArray("CONNECT") << (QBuffer *) 0 << 400 << QNetworkReply::UnknownContentError << QByteArray(); // 400 = Bad Request
QTest::newRow("nonsense") << QUrl("http://" + QtNetworkSettings::serverName()) <<
QByteArray("NONSENSE") << (QBuffer *) 0 << 501 << QNetworkReply::ProtocolUnknownError << QByteArray(); // 501 = Method Not Implemented
QByteArray ba("test");
QBuffer *buffer = new QBuffer;
buffer->setData(ba);
buffer->open(QIODevice::ReadOnly);
QTest::newRow("post") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi") << QByteArray("POST")
<< buffer << 200 << QNetworkReply::NoError << QByteArray("098f6bcd4621d373cade4e832627b4f6\n");
QByteArray ba2("test");
QBuffer *buffer2 = new QBuffer;
buffer2->setData(ba2);
buffer2->open(QIODevice::ReadOnly);
QTest::newRow("put") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi") << QByteArray("PUT")
<< buffer2 << 200 << QNetworkReply::NoError << QByteArray("098f6bcd4621d373cade4e832627b4f6\n");
}
void tst_QNetworkReply::sendCustomRequestToHttp()
{
QFETCH(QUrl, url);
QNetworkRequest request(url);
QNetworkReplyPtr reply;
QFETCH(QByteArray, verb);
QFETCH(QBuffer *, device);
runCustomRequest(request, reply, verb, device);
QCOMPARE(reply->url(), url);
QFETCH(QNetworkReply::NetworkError, error);
QCOMPARE(reply->error(), error);
QFETCH(int, resultCode);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), resultCode);
QFETCH(QByteArray, expectedContent);
if (! expectedContent.isEmpty())
QCOMPARE(reply->readAll(), expectedContent);
}
void tst_QNetworkReply::ioGetFromData_data()
{
QTest::addColumn<QString>("urlStr");
QTest::addColumn<QByteArray>("data");
QTest::newRow("data-empty") << "data:," << QByteArray();
QTest::newRow("data-literal") << "data:,foo" << QByteArray("foo");
QTest::newRow("data-pct") << "data:,%3Cbody%20contentEditable%3Dtrue%3E%0D%0A"
<< QByteArray("<body contentEditable=true>\r\n");
QTest::newRow("data-base64") << "data:;base64,UXQgaXMgZ3JlYXQh" << QByteArray("Qt is great!");
}
void tst_QNetworkReply::ioGetFromData()
{
QFETCH(QString, urlStr);
QUrl url = QUrl::fromEncoded(urlStr.toLatin1());
QNetworkRequest request(url);
QNetworkReplyPtr reply = manager.get(request);
DataReader reader(reply);
connect(reply, SIGNAL(finished()),
&QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), request.url());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QFETCH(QByteArray, data);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toInt(), data.size());
QCOMPARE(reader.data.size(), data.size());
QCOMPARE(reader.data, data);
}
void tst_QNetworkReply::ioGetFromFileSpecial_data()
{
getFromFileSpecial_data();
}
void tst_QNetworkReply::ioGetFromFileSpecial()
{
QFETCH(QString, fileName);
QFETCH(QString, url);
QFile resource(fileName);
QVERIFY(resource.open(QIODevice::ReadOnly));
QNetworkRequest request;
request.setUrl(url);
QNetworkReplyPtr reply = manager.get(request);
DataReader reader(reply);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), request.url());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), resource.size());
QCOMPARE(qint64(reader.data.size()), resource.size());
QCOMPARE(reader.data, resource.readAll());
}
void tst_QNetworkReply::ioGetFromFile_data()
{
putToFile_data();
}
void tst_QNetworkReply::ioGetFromFile()
{
QTemporaryFile file(QDir::currentPath() + "/temp-XXXXXX");
file.setAutoRemove(true);
QVERIFY(file.open());
QFETCH(QByteArray, data);
QVERIFY(file.write(data) == data.size());
file.flush();
QCOMPARE(file.size(), qint64(data.size()));
QNetworkRequest request(QUrl::fromLocalFile(file.fileName()));
QNetworkReplyPtr reply = manager.get(request);
QVERIFY(reply->isFinished()); // a file should immediatly be done
DataReader reader(reply);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), request.url());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), file.size());
QCOMPARE(qint64(reader.data.size()), file.size());
QCOMPARE(reader.data, data);
}
void tst_QNetworkReply::ioGetFromFtp_data()
{
QTest::addColumn<QString>("fileName");
QTest::addColumn<qint64>("expectedSize");
QTest::newRow("bigfile") << "bigfile" << Q_INT64_C(519240);
QFile file(SRCDIR "/rfc3252.txt");
QTest::newRow("rfc3252.txt") << "rfc3252.txt" << file.size();
}
void tst_QNetworkReply::ioGetFromFtp()
{
QFETCH(QString, fileName);
QFile reference(fileName);
reference.open(QIODevice::ReadOnly); // will fail for bigfile
QNetworkRequest request("ftp://" + QtNetworkSettings::serverName() + "/qtest/" + fileName);
QNetworkReplyPtr reply = manager.get(request);
DataReader reader(reply);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), request.url());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QFETCH(qint64, expectedSize);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), expectedSize);
QCOMPARE(qint64(reader.data.size()), expectedSize);
if (reference.isOpen())
QCOMPARE(reader.data, reference.readAll());
}
void tst_QNetworkReply::ioGetFromFtpWithReuse()
{
QString fileName = SRCDIR "/rfc3252.txt";
QFile reference(fileName);
reference.open(QIODevice::ReadOnly);
QNetworkRequest request(QUrl("ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
// two concurrent (actually, consecutive) gets:
QNetworkReplyPtr reply1 = manager.get(request);
DataReader reader1(reply1);
QNetworkReplyPtr reply2 = manager.get(request);
DataReader reader2(reply2);
QSignalSpy spy(reply1, SIGNAL(finished()));
connect(reply2, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
if (spy.count() == 0) {
connect(reply1, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
}
QCOMPARE(reply1->url(), request.url());
QCOMPARE(reply2->url(), request.url());
QCOMPARE(reply1->error(), QNetworkReply::NoError);
QCOMPARE(reply2->error(), QNetworkReply::NoError);
QCOMPARE(qint64(reader1.data.size()), reference.size());
QCOMPARE(qint64(reader2.data.size()), reference.size());
QCOMPARE(reply1->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
QCOMPARE(reply2->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
QByteArray referenceData = reference.readAll();
QCOMPARE(reader1.data, referenceData);
QCOMPARE(reader2.data, referenceData);
}
void tst_QNetworkReply::ioGetFromHttp()
{
QFile reference(SRCDIR "/rfc3252.txt");
QVERIFY(reference.open(QIODevice::ReadOnly));
QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
QNetworkReplyPtr reply = manager.get(request);
DataReader reader(reply);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), request.url());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
QCOMPARE(qint64(reader.data.size()), reference.size());
QCOMPARE(reader.data, reference.readAll());
}
void tst_QNetworkReply::ioGetFromHttpWithReuseParallel()
{
QFile reference(SRCDIR "/rfc3252.txt");
QVERIFY(reference.open(QIODevice::ReadOnly));
QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
QNetworkReplyPtr reply1 = manager.get(request);
QNetworkReplyPtr reply2 = manager.get(request);
DataReader reader1(reply1);
DataReader reader2(reply2);
QSignalSpy spy(reply1, SIGNAL(finished()));
connect(reply2, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
if (spy.count() == 0) {
connect(reply1, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
}
QCOMPARE(reply1->url(), request.url());
QCOMPARE(reply2->url(), request.url());
QCOMPARE(reply1->error(), QNetworkReply::NoError);
QCOMPARE(reply2->error(), QNetworkReply::NoError);
QCOMPARE(reply1->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reply1->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
QCOMPARE(reply2->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
QCOMPARE(qint64(reader1.data.size()), reference.size());
QCOMPARE(qint64(reader2.data.size()), reference.size());
QByteArray referenceData = reference.readAll();
QCOMPARE(reader1.data, referenceData);
QCOMPARE(reader2.data, referenceData);
}
void tst_QNetworkReply::ioGetFromHttpWithReuseSequential()
{
QFile reference(SRCDIR "/rfc3252.txt");
QVERIFY(reference.open(QIODevice::ReadOnly));
QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
{
QNetworkReplyPtr reply = manager.get(request);
DataReader reader(reply);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), request.url());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
QCOMPARE(qint64(reader.data.size()), reference.size());
QCOMPARE(reader.data, reference.readAll());
}
reference.seek(0);
// rinse and repeat:
{
QNetworkReplyPtr reply = manager.get(request);
DataReader reader(reply);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), request.url());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
QCOMPARE(qint64(reader.data.size()), reference.size());
QCOMPARE(reader.data, reference.readAll());
}
}
void tst_QNetworkReply::ioGetFromHttpWithAuth_data()
{
QTest::addColumn<QUrl>("url");
QTest::addColumn<QByteArray>("expectedData");
QFile reference(SRCDIR "/rfc3252.txt");
reference.open(QIODevice::ReadOnly);
QByteArray referenceData = reference.readAll();
QTest::newRow("basic") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth/rfc3252.txt") << referenceData;
QTest::newRow("digest") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/auth-digest/") << QByteArray("digest authentication successful\n");
}
void tst_QNetworkReply::ioGetFromHttpWithAuth()
{
// This test sends three requests
// The first two in parallel
// The third after the first two finished
QFETCH(QUrl, url);
QFETCH(QByteArray, expectedData);
QNetworkRequest request(url);
{
QNetworkReplyPtr reply1 = manager.get(request);
QNetworkReplyPtr reply2 = manager.get(request);
DataReader reader1(reply1);
DataReader reader2(reply2);
QSignalSpy finishedspy(reply1, SIGNAL(finished()));
QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
connect(reply2, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
if (finishedspy.count() == 0) {
connect(reply1, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
}
manager.disconnect(SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
QCOMPARE(reply1->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reader1.data, expectedData);
QCOMPARE(reader2.data, expectedData);
QCOMPARE(authspy.count(), 1);
}
// rinse and repeat:
{
QNetworkReplyPtr reply = manager.get(request);
DataReader reader(reply);
QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
manager.disconnect(SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reader.data, expectedData);
QCOMPARE(authspy.count(), 0);
}
// now check with synchronous calls:
{
request.setAttribute(
QNetworkRequest::SynchronousRequestAttribute,
true);
QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
QNetworkReplyPtr replySync = manager.get(request);
QVERIFY(replySync->isFinished()); // synchronous
QCOMPARE(authspy.count(), 0);
// we cannot use a data reader here, since that connects to the readyRead signal,
// just use readAll()
// the only thing we check here is that the auth cache was used when using synchronous requests
QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(replySync->readAll(), expectedData);
}
}
void tst_QNetworkReply::ioGetFromHttpWithAuthSynchronous()
{
// verify that we do not enter an endless loop with synchronous calls and wrong credentials
// the case when we succed with the login is tested in ioGetFromHttpWithAuth()
QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth/rfc3252.txt"));
request.setAttribute(
QNetworkRequest::SynchronousRequestAttribute,
true);
QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
QNetworkReplyPtr replySync = manager.get(request);
QVERIFY(replySync->isFinished()); // synchronous
QCOMPARE(replySync->error(), QNetworkReply::AuthenticationRequiredError);
QCOMPARE(authspy.count(), 0);
QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 401);
}
void tst_QNetworkReply::ioGetFromHttpWithProxyAuth()
{
qRegisterMetaType<QNetworkProxy>(); // for QSignalSpy
qRegisterMetaType<QAuthenticator *>();
// This test sends three requests
// The first two in parallel
// The third after the first two finished
QFile reference(SRCDIR "/rfc3252.txt");
QVERIFY(reference.open(QIODevice::ReadOnly));
QNetworkProxy proxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129);
QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
{
manager.setProxy(proxy);
QNetworkReplyPtr reply1 = manager.get(request);
QNetworkReplyPtr reply2 = manager.get(request);
manager.setProxy(QNetworkProxy());
DataReader reader1(reply1);
DataReader reader2(reply2);
QSignalSpy finishedspy(reply1, SIGNAL(finished()));
QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
connect(reply2, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
if (finishedspy.count() == 0) {
connect(reply1, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
}
manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
QCOMPARE(reply1->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QByteArray referenceData = reference.readAll();
QCOMPARE(reader1.data, referenceData);
QCOMPARE(reader2.data, referenceData);
QCOMPARE(authspy.count(), 1);
}
reference.seek(0);
// rinse and repeat:
{
manager.setProxy(proxy);
QNetworkReplyPtr reply = manager.get(request);
DataReader reader(reply);
manager.setProxy(QNetworkProxy());
QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reader.data, reference.readAll());
QCOMPARE(authspy.count(), 0);
}
// now check with synchronous calls:
reference.seek(0);
{
request.setAttribute(
QNetworkRequest::SynchronousRequestAttribute,
true);
QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
QNetworkReplyPtr replySync = manager.get(request);
QVERIFY(replySync->isFinished()); // synchronous
QCOMPARE(authspy.count(), 0);
// we cannot use a data reader here, since that connects to the readyRead signal,
// just use readAll()
// the only thing we check here is that the proxy auth cache was used when using synchronous requests
QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(replySync->readAll(), reference.readAll());
}
}
void tst_QNetworkReply::ioGetFromHttpWithProxyAuthSynchronous()
{
// verify that we do not enter an endless loop with synchronous calls and wrong credentials
// the case when we succed with the login is tested in ioGetFromHttpWithAuth()
QNetworkProxy proxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129);
QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
manager.setProxy(proxy);
request.setAttribute(
QNetworkRequest::SynchronousRequestAttribute,
true);
QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
QNetworkReplyPtr replySync = manager.get(request);
manager.setProxy(QNetworkProxy()); // reset
QVERIFY(replySync->isFinished()); // synchronous
QCOMPARE(replySync->error(), QNetworkReply::ProxyAuthenticationRequiredError);
QCOMPARE(authspy.count(), 0);
QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 407);
}
void tst_QNetworkReply::ioGetFromHttpWithSocksProxy()
{
// HTTP caching proxies are tested by the above function
// test SOCKSv5 proxies too
qRegisterMetaType<QNetworkProxy>(); // for QSignalSpy
qRegisterMetaType<QAuthenticator *>();
QFile reference(SRCDIR "/rfc3252.txt");
QVERIFY(reference.open(QIODevice::ReadOnly));
QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080);
QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
{
manager.setProxy(proxy);
QNetworkReplyPtr reply = manager.get(request);
DataReader reader(reply);
manager.setProxy(QNetworkProxy());
QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reader.data, reference.readAll());
QCOMPARE(authspy.count(), 0);
}
// set an invalid proxy just to make sure that we can't load
proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1079);
{
manager.setProxy(proxy);
QNetworkReplyPtr reply = manager.get(request);
DataReader reader(reply);
manager.setProxy(QNetworkProxy());
QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
QVERIFY(!reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).isValid());
QVERIFY(reader.data.isEmpty());
QVERIFY(int(reply->error()) > 0);
QEXPECT_FAIL("", "QTcpSocket doesn't return enough information yet", Continue);
QCOMPARE(int(reply->error()), int(QNetworkReply::ProxyConnectionRefusedError));
QCOMPARE(authspy.count(), 0);
}
}
#ifndef QT_NO_OPENSSL
void tst_QNetworkReply::ioGetFromHttpsWithSslErrors()
{
qRegisterMetaType<QNetworkReply*>(); // for QSignalSpy
qRegisterMetaType<QList<QSslError> >();
QFile reference(SRCDIR "/rfc3252.txt");
QVERIFY(reference.open(QIODevice::ReadOnly));
QNetworkRequest request(QUrl("https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
QNetworkReplyPtr reply = manager.get(request);
DataReader reader(reply);
QSignalSpy sslspy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
connect(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
connect(reply, SIGNAL(metaDataChanged()), SLOT(storeSslConfiguration()));
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
manager.disconnect(SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
this, SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reader.data, reference.readAll());
QCOMPARE(sslspy.count(), 1);
QVERIFY(!storedSslConfiguration.isNull());
QVERIFY(!reply->sslConfiguration().isNull());
}
void tst_QNetworkReply::ioGetFromHttpsWithIgnoreSslErrors()
{
// same as above, except that we call ignoreSslErrors and don't connect
// to the sslErrors() signal (which is *still* emitted)
qRegisterMetaType<QNetworkReply*>(); // for QSignalSpy
qRegisterMetaType<QList<QSslError> >();
QFile reference(SRCDIR "/rfc3252.txt");
QVERIFY(reference.open(QIODevice::ReadOnly));
QNetworkRequest request(QUrl("https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
QNetworkReplyPtr reply = manager.get(request);
reply->ignoreSslErrors();
DataReader reader(reply);
QSignalSpy sslspy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
connect(reply, SIGNAL(metaDataChanged()), SLOT(storeSslConfiguration()));
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reader.data, reference.readAll());
QCOMPARE(sslspy.count(), 1);
QVERIFY(!storedSslConfiguration.isNull());
QVERIFY(!reply->sslConfiguration().isNull());
}
void tst_QNetworkReply::ioGetFromHttpsWithSslHandshakeError()
{
qRegisterMetaType<QNetworkReply*>(); // for QSignalSpy
qRegisterMetaType<QList<QSslError> >();
QFile reference(SRCDIR "/rfc3252.txt");
QVERIFY(reference.open(QIODevice::ReadOnly));
QNetworkRequest request(QUrl("https://" + QtNetworkSettings::serverName() + ":80"));
QNetworkReplyPtr reply = manager.get(request);
reply->ignoreSslErrors();
DataReader reader(reply);
QSignalSpy sslspy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
connect(reply, SIGNAL(metaDataChanged()), SLOT(storeSslConfiguration()));
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->error(), QNetworkReply::SslHandshakeFailedError);
QCOMPARE(sslspy.count(), 0);
}
#endif
void tst_QNetworkReply::ioGetFromHttpBrokenServer_data()
{
QTest::addColumn<QByteArray>("dataToSend");
QTest::addColumn<bool>("doDisconnect");
QTest::newRow("no-newline") << QByteArray("Hello World") << false;
// these are OK now, we just eat the lonely newlines
//QTest::newRow("just-newline") << QByteArray("\r\n") << false;
//QTest::newRow("just-2newline") << QByteArray("\r\n\r\n") << false;
QTest::newRow("with-newlines") << QByteArray("Long first line\r\nLong second line") << false;
QTest::newRow("with-newlines2") << QByteArray("\r\nSecond line") << false;
QTest::newRow("with-newlines3") << QByteArray("ICY\r\nSecond line") << false;
QTest::newRow("invalid-version") << QByteArray("HTTP/123 200 \r\n") << false;
QTest::newRow("invalid-version2") << QByteArray("HTTP/a.\033 200 \r\n") << false;
QTest::newRow("invalid-reply-code") << QByteArray("HTTP/1.0 fuu \r\n") << false;
QTest::newRow("empty+disconnect") << QByteArray() << true;
QTest::newRow("no-newline+disconnect") << QByteArray("Hello World") << true;
QTest::newRow("just-newline+disconnect") << QByteArray("\r\n") << true;
QTest::newRow("just-2newline+disconnect") << QByteArray("\r\n\r\n") << true;
QTest::newRow("with-newlines+disconnect") << QByteArray("Long first line\r\nLong second line") << true;
QTest::newRow("with-newlines2+disconnect") << QByteArray("\r\nSecond line") << true;
QTest::newRow("with-newlines3+disconnect") << QByteArray("ICY\r\nSecond line") << true;
QTest::newRow("invalid-version+disconnect") << QByteArray("HTTP/123 200 ") << true;
QTest::newRow("invalid-version2+disconnect") << QByteArray("HTTP/a.\033 200 ") << true;
QTest::newRow("invalid-reply-code+disconnect") << QByteArray("HTTP/1.0 fuu ") << true;
QTest::newRow("immediate disconnect") << QByteArray("") << true;
QTest::newRow("justHalfStatus+disconnect") << QByteArray("HTTP/1.1") << true;
QTest::newRow("justStatus+disconnect") << QByteArray("HTTP/1.1 200 OK\r\n") << true;
QTest::newRow("justStatusAndHalfHeaders+disconnect") << QByteArray("HTTP/1.1 200 OK\r\nContent-L") << true;
QTest::newRow("halfContent+disconnect") << QByteArray("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nAB") << true;
}
void tst_QNetworkReply::ioGetFromHttpBrokenServer()
{
QFETCH(QByteArray, dataToSend);
QFETCH(bool, doDisconnect);
MiniHttpServer server(dataToSend);
server.doClose = doDisconnect;
QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
QNetworkReplyPtr reply = manager.get(request);
QSignalSpy spy(reply, SIGNAL(error(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), request.url());
QCOMPARE(spy.count(), 1);
QVERIFY(reply->error() != QNetworkReply::NoError);
}
void tst_QNetworkReply::ioGetFromHttpStatus100_data()
{
QTest::addColumn<QByteArray>("dataToSend");
QTest::addColumn<int>("statusCode");
QTest::newRow("normal") << QByteArray("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200;
QTest::newRow("minimal") << QByteArray("HTTP/1.1 100 Continue\n\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200;
QTest::newRow("minimal2") << QByteArray("HTTP/1.1 100 Continue\n\nHTTP/1.0 200 OK\r\n\r\n") << 200;
QTest::newRow("minimal3") << QByteArray("HTTP/1.1 100 Continue\n\nHTTP/1.0 200 OK\n\n") << 200;
QTest::newRow("minimal+404") << QByteArray("HTTP/1.1 100 Continue\n\nHTTP/1.0 204 No Content\r\n\r\n") << 204;
QTest::newRow("with_headers") << QByteArray("HTTP/1.1 100 Continue\r\nBla: x\r\n\r\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200;
QTest::newRow("with_headers2") << QByteArray("HTTP/1.1 100 Continue\nBla: x\n\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200;
}
void tst_QNetworkReply::ioGetFromHttpStatus100()
{
QFETCH(QByteArray, dataToSend);
QFETCH(int, statusCode);
MiniHttpServer server(dataToSend);
server.doClose = true;
QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
QNetworkReplyPtr reply = manager.get(request);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), request.url());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), statusCode);
QVERIFY(reply->rawHeader("bla").isNull());
}
void tst_QNetworkReply::ioGetFromHttpNoHeaders_data()
{
QTest::addColumn<QByteArray>("dataToSend");
QTest::newRow("justStatus+noheaders+disconnect") << QByteArray("HTTP/1.0 200 OK\r\n\r\n");
}
void tst_QNetworkReply::ioGetFromHttpNoHeaders()
{
QFETCH(QByteArray, dataToSend);
MiniHttpServer server(dataToSend);
server.doClose = true;
QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
QNetworkReplyPtr reply = manager.get(request);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), request.url());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
}
void tst_QNetworkReply::ioGetFromHttpWithCache_data()
{
qRegisterMetaType<MyMemoryCache::CachedContent>();
QTest::addColumn<QByteArray>("dataToSend");
QTest::addColumn<QString>("body");
QTest::addColumn<MyMemoryCache::CachedContent>("cachedReply");
QTest::addColumn<int>("cacheMode");
QTest::addColumn<QStringList>("extraHttpHeaders");
QTest::addColumn<bool>("loadedFromCache");
QTest::addColumn<bool>("networkUsed");
QByteArray reply200 =
"HTTP/1.0 200\r\n"
"Connection: keep-alive\r\n"
"Content-Type: text/plain\r\n"
"Cache-control: no-cache\r\n"
"Content-length: 8\r\n"
"\r\n"
"Reloaded";
QByteArray reply304 =
"HTTP/1.0 304 Use Cache\r\n"
"Connection: keep-alive\r\n"
"\r\n";
QTest::newRow("not-cached,always-network")
<< reply200 << "Reloaded" << MyMemoryCache::CachedContent() << int(QNetworkRequest::AlwaysNetwork) << QStringList() << false << true;
QTest::newRow("not-cached,prefer-network")
<< reply200 << "Reloaded" << MyMemoryCache::CachedContent() << int(QNetworkRequest::PreferNetwork) << QStringList() << false << true;
QTest::newRow("not-cached,prefer-cache")
<< reply200 << "Reloaded" << MyMemoryCache::CachedContent() << int(QNetworkRequest::PreferCache) << QStringList() << false << true;
QDateTime present = QDateTime::currentDateTime().toUTC();
QDateTime past = present.addSecs(-3600);
QDateTime future = present.addSecs(3600);
static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'";
QNetworkCacheMetaData::RawHeaderList rawHeaders;
MyMemoryCache::CachedContent content;
content.second = "Not-reloaded";
content.first.setLastModified(past);
//
// Set to expired
//
rawHeaders.clear();
rawHeaders << QNetworkCacheMetaData::RawHeader("Date", QLocale::c().toString(past, dateFormat).toLatin1())
<< QNetworkCacheMetaData::RawHeader("Cache-control", "max-age=0"); // isn't used in cache loading
content.first.setRawHeaders(rawHeaders);
content.first.setLastModified(past);
QTest::newRow("expired,200,prefer-network")
<< reply200 << "Reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << false << true;
QTest::newRow("expired,200,prefer-cache")
<< reply200 << "Reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << false << true;
QTest::newRow("expired,304,prefer-network")
<< reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << true << true;
QTest::newRow("expired,304,prefer-cache")
<< reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << true;
//
// Set to not-expired
//
rawHeaders.clear();
rawHeaders << QNetworkCacheMetaData::RawHeader("Date", QLocale::c().toString(past, dateFormat).toLatin1())
<< QNetworkCacheMetaData::RawHeader("Cache-control", "max-age=7200"); // isn't used in cache loading
content.first.setRawHeaders(rawHeaders);
content.first.setExpirationDate(future);
QTest::newRow("not-expired,200,always-network")
<< reply200 << "Reloaded" << content << int(QNetworkRequest::AlwaysNetwork) << QStringList() << false << true;
QTest::newRow("not-expired,200,prefer-network")
<< reply200 << "Not-reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << true << false;
QTest::newRow("not-expired,200,prefer-cache")
<< reply200 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << false;
QTest::newRow("not-expired,200,always-cache")
<< reply200 << "Not-reloaded" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << true << false;
QTest::newRow("not-expired,304,prefer-network")
<< reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << true << false;
QTest::newRow("not-expired,304,prefer-cache")
<< reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << false;
QTest::newRow("not-expired,304,always-cache")
<< reply304 << "Not-reloaded" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << true << false;
//
// Set must-revalidate now
//
rawHeaders.clear();
rawHeaders << QNetworkCacheMetaData::RawHeader("Date", QLocale::c().toString(past, dateFormat).toLatin1())
<< QNetworkCacheMetaData::RawHeader("Cache-control", "max-age=7200, must-revalidate"); // must-revalidate is used
content.first.setRawHeaders(rawHeaders);
QTest::newRow("must-revalidate,200,always-network")
<< reply200 << "Reloaded" << content << int(QNetworkRequest::AlwaysNetwork) << QStringList() << false << true;
QTest::newRow("must-revalidate,200,prefer-network")
<< reply200 << "Reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << false << true;
QTest::newRow("must-revalidate,200,prefer-cache")
<< reply200 << "Reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << false << true;
QTest::newRow("must-revalidate,200,always-cache")
<< reply200 << "" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << false << false;
QTest::newRow("must-revalidate,304,prefer-network")
<< reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << true << true;
QTest::newRow("must-revalidate,304,prefer-cache")
<< reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << true;
QTest::newRow("must-revalidate,304,always-cache")
<< reply304 << "" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << false << false;
//
// Partial content
//
rawHeaders.clear();
rawHeaders << QNetworkCacheMetaData::RawHeader("Date", QLocale::c().toString(past, dateFormat).toLatin1())
<< QNetworkCacheMetaData::RawHeader("Cache-control", "max-age=7200"); // isn't used in cache loading
content.first.setRawHeaders(rawHeaders);
content.first.setExpirationDate(future);
QByteArray reply206 =
"HTTP/1.0 206\r\n"
"Connection: keep-alive\r\n"
"Content-Type: text/plain\r\n"
"Cache-control: no-cache\r\n"
"Content-Range: bytes 2-6/8\r\n"
"Content-length: 4\r\n"
"\r\n"
"load";
QTest::newRow("partial,dontuse-cache")
<< reply206 << "load" << content << int(QNetworkRequest::PreferCache) << (QStringList() << "Range" << "bytes=2-6") << false << true;
}
void tst_QNetworkReply::ioGetFromHttpWithCache()
{
QFETCH(QByteArray, dataToSend);
MiniHttpServer server(dataToSend);
server.doClose = false;
MyMemoryCache *memoryCache = new MyMemoryCache(&manager);
manager.setCache(memoryCache);
QFETCH(MyMemoryCache::CachedContent, cachedReply);
QUrl url = "http://localhost:" + QString::number(server.serverPort());
cachedReply.first.setUrl(url);
if (!cachedReply.second.isNull())
memoryCache->cache.insert(url.toEncoded(), cachedReply);
QFETCH(int, cacheMode);
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, cacheMode);
request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false);
QFETCH(QStringList, extraHttpHeaders);
QStringListIterator it(extraHttpHeaders);
while (it.hasNext()) {
QString header = it.next();
QString value = it.next();
request.setRawHeader(header.toLatin1(), value.toLatin1()); // To latin1? Deal with it!
}
QNetworkReplyPtr reply = manager.get(request);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QTEST(reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(), "loadedFromCache");
QTEST(server.totalConnections > 0, "networkUsed");
QFETCH(QString, body);
QCOMPARE(reply->readAll().constData(), qPrintable(body));
}
void tst_QNetworkReply::ioGetWithManyProxies_data()
{
QTest::addColumn<QList<QNetworkProxy> >("proxyList");
QTest::addColumn<QNetworkProxy>("proxyUsed");
QTest::addColumn<QString>("url");
QTest::addColumn<QNetworkReply::NetworkError>("expectedError");
QList<QNetworkProxy> proxyList;
// All of the other functions test DefaultProxy
// So let's test something else
// Simple tests that work:
// HTTP request with HTTP caching proxy
proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129);
QTest::newRow("http-on-http")
<< proxyList << proxyList.at(0)
<< "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::NoError;
// HTTP request with HTTP transparent proxy
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3129);
QTest::newRow("http-on-http2")
<< proxyList << proxyList.at(0)
<< "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::NoError;
// HTTP request with SOCKS transparent proxy
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081);
QTest::newRow("http-on-socks")
<< proxyList << proxyList.at(0)
<< "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::NoError;
// FTP request with FTP caching proxy
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121);
QTest::newRow("ftp-on-ftp")
<< proxyList << proxyList.at(0)
<< "ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::NoError;
// The following test doesn't work because QFtp is too limited
// It can only talk to its own kind of proxies
// FTP request with SOCKSv5 transparent proxy
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081);
QTest::newRow("ftp-on-socks")
<< proxyList << proxyList.at(0)
<< "ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::NoError;
#ifndef QT_NO_OPENSSL
// HTTPS with HTTP transparent proxy
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3129);
QTest::newRow("https-on-http")
<< proxyList << proxyList.at(0)
<< "https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::NoError;
// HTTPS request with SOCKS transparent proxy
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081);
QTest::newRow("https-on-socks")
<< proxyList << proxyList.at(0)
<< "https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::NoError;
#endif
// Tests that fail:
// HTTP request with FTP caching proxy
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121);
QTest::newRow("http-on-ftp")
<< proxyList << QNetworkProxy()
<< "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::ProxyNotFoundError;
// FTP request with HTTP caching proxy
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129);
QTest::newRow("ftp-on-http")
<< proxyList << QNetworkProxy()
<< "ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::ProxyNotFoundError;
// FTP request with HTTP caching proxies
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129)
<< QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3130);
QTest::newRow("ftp-on-multiple-http")
<< proxyList << QNetworkProxy()
<< "ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::ProxyNotFoundError;
#ifndef QT_NO_OPENSSL
// HTTPS with HTTP caching proxy
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129);
QTest::newRow("https-on-httptransparent")
<< proxyList << QNetworkProxy()
<< "https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::ProxyNotFoundError;
// HTTPS with FTP caching proxy
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121);
QTest::newRow("https-on-ftp")
<< proxyList << QNetworkProxy()
<< "https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::ProxyNotFoundError;
#endif
// Complex requests:
// HTTP request with more than one HTTP proxy
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129)
<< QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3130);
QTest::newRow("http-on-multiple-http")
<< proxyList << proxyList.at(0)
<< "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::NoError;
// HTTP request with HTTP + SOCKS
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129)
<< QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081);
QTest::newRow("http-on-http+socks")
<< proxyList << proxyList.at(0)
<< "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::NoError;
// HTTP request with FTP + HTTP + SOCKS
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121)
<< QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129)
<< QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081);
QTest::newRow("http-on-ftp+http+socks")
<< proxyList << proxyList.at(1) // second proxy should be used
<< "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::NoError;
// HTTP request with NoProxy + HTTP
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::NoProxy)
<< QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129);
QTest::newRow("http-on-noproxy+http")
<< proxyList << proxyList.at(0)
<< "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::NoError;
// HTTP request with FTP + NoProxy
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121)
<< QNetworkProxy(QNetworkProxy::NoProxy);
QTest::newRow("http-on-ftp+noproxy")
<< proxyList << proxyList.at(1) // second proxy should be used
<< "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::NoError;
// FTP request with HTTP Caching + FTP
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129)
<< QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121);
QTest::newRow("ftp-on-http+ftp")
<< proxyList << proxyList.at(1) // second proxy should be used
<< "ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::NoError;
#ifndef QT_NO_OPENSSL
// HTTPS request with HTTP Caching + HTTP transparent
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129)
<< QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3129);
QTest::newRow("https-on-httpcaching+http")
<< proxyList << proxyList.at(1) // second proxy should be used
<< "https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::NoError;
// HTTPS request with FTP + HTTP C + HTTP T
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121)
<< QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129)
<< QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3129);
QTest::newRow("https-on-ftp+httpcaching+http")
<< proxyList << proxyList.at(2) // skip the first two
<< "https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
<< QNetworkReply::NoError;
#endif
}
void tst_QNetworkReply::ioGetWithManyProxies()
{
// Test proxy factories
qRegisterMetaType<QNetworkProxy>(); // for QSignalSpy
qRegisterMetaType<QAuthenticator *>();
QFile reference(SRCDIR "/rfc3252.txt");
QVERIFY(reference.open(QIODevice::ReadOnly));
// set the proxy factory:
QFETCH(QList<QNetworkProxy>, proxyList);
MyProxyFactory *proxyFactory = new MyProxyFactory;
proxyFactory->toReturn = proxyList;
manager.setProxyFactory(proxyFactory);
QFETCH(QString, url);
QUrl theUrl(url);
QNetworkRequest request(theUrl);
QNetworkReplyPtr reply = manager.get(request);
DataReader reader(reply);
QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
#ifndef QT_NO_OPENSSL
connect(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
#endif
QTestEventLoop::instance().enterLoop(15);
QVERIFY(!QTestEventLoop::instance().timeout());
manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
#ifndef QT_NO_OPENSSL
manager.disconnect(SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
this, SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
#endif
QFETCH(QNetworkReply::NetworkError, expectedError);
QEXPECT_FAIL("ftp-on-socks", "QFtp is too limited and won't accept non-FTP proxies", Abort);
QCOMPARE(reply->error(), expectedError);
// Verify that the factory was called properly
QCOMPARE(proxyFactory->callCount, 1);
QCOMPARE(proxyFactory->lastQuery, QNetworkProxyQuery(theUrl));
if (expectedError == QNetworkReply::NoError) {
// request succeeded
QCOMPARE(reader.data, reference.readAll());
// now verify that the proxies worked:
QFETCH(QNetworkProxy, proxyUsed);
if (proxyUsed.type() == QNetworkProxy::NoProxy) {
QCOMPARE(authspy.count(), 0);
} else {
if (QByteArray(QTest::currentDataTag()).startsWith("ftp-"))
return; // No authentication with current FTP or with FTP proxies
QCOMPARE(authspy.count(), 1);
QCOMPARE(qvariant_cast<QNetworkProxy>(authspy.at(0).at(0)), proxyUsed);
}
} else {
// request failed
QCOMPARE(authspy.count(), 0);
}
}
void tst_QNetworkReply::ioPutToFileFromFile_data()
{
QTest::addColumn<QString>("fileName");
QTest::newRow("empty") << SRCDIR "/empty";
QTest::newRow("real-file") << SRCDIR "/rfc3252.txt";
QTest::newRow("resource") << ":/resource";
QTest::newRow("search-path") << "srcdir:/rfc3252.txt";
}
void tst_QNetworkReply::ioPutToFileFromFile()
{
QFETCH(QString, fileName);
QFile sourceFile(fileName);
QFile targetFile(testFileName);
QVERIFY(sourceFile.open(QIODevice::ReadOnly));
QUrl url = QUrl::fromLocalFile(targetFile.fileName());
QNetworkRequest request(url);
QNetworkReplyPtr reply = manager.put(request, &sourceFile);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
QVERIFY(reply->readAll().isEmpty());
QVERIFY(sourceFile.atEnd());
sourceFile.seek(0); // reset it to the beginning
QVERIFY(targetFile.open(QIODevice::ReadOnly));
QCOMPARE(targetFile.size(), sourceFile.size());
QCOMPARE(targetFile.readAll(), sourceFile.readAll());
}
void tst_QNetworkReply::ioPutToFileFromSocket_data()
{
putToFile_data();
}
void tst_QNetworkReply::ioPutToFileFromSocket()
{
QFile file(testFileName);
QUrl url = QUrl::fromLocalFile(file.fileName());
QNetworkRequest request(url);
QFETCH(QByteArray, data);
SocketPair socketpair;
socketpair.create();
QVERIFY(socketpair.endPoints[0] && socketpair.endPoints[1]);
socketpair.endPoints[0]->write(data);
QNetworkReplyPtr reply = manager.put(QNetworkRequest(url), socketpair.endPoints[1]);
socketpair.endPoints[0]->close();
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
QVERIFY(reply->readAll().isEmpty());
QVERIFY(file.open(QIODevice::ReadOnly));
QCOMPARE(file.size(), qint64(data.size()));
QByteArray contents = file.readAll();
QCOMPARE(contents, data);
}
void tst_QNetworkReply::ioPutToFileFromLocalSocket_data()
{
putToFile_data();
}
void tst_QNetworkReply::ioPutToFileFromLocalSocket()
{
QString socketname = "networkreplytest";
QLocalServer server;
if (!server.listen(socketname)) {
QLocalServer::removeServer(socketname);
QVERIFY(server.listen(socketname));
}
QLocalSocket active;
active.connectToServer(socketname);
QVERIFY2(server.waitForNewConnection(10), server.errorString().toLatin1().constData());
QVERIFY2(active.waitForConnected(10), active.errorString().toLatin1().constData());
QVERIFY2(server.hasPendingConnections(), server.errorString().toLatin1().constData());
QLocalSocket *passive = server.nextPendingConnection();
QFile file(testFileName);
QUrl url = QUrl::fromLocalFile(file.fileName());
QNetworkRequest request(url);
QFETCH(QByteArray, data);
active.write(data);
active.close();
QNetworkReplyPtr reply = manager.put(QNetworkRequest(url), passive);
passive->setParent(reply);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
QVERIFY(reply->readAll().isEmpty());
QVERIFY(file.open(QIODevice::ReadOnly));
QCOMPARE(file.size(), qint64(data.size()));
QByteArray contents = file.readAll();
QCOMPARE(contents, data);
}
void tst_QNetworkReply::ioPutToFileFromProcess_data()
{
putToFile_data();
}
void tst_QNetworkReply::ioPutToFileFromProcess()
{
#if defined(Q_OS_WINCE) || defined (Q_OS_SYMBIAN)
QSKIP("Currently no stdin/out supported for Windows CE / Symbian OS", SkipAll);
#else
#ifdef Q_OS_WIN
if (qstrcmp(QTest::currentDataTag(), "small") == 0)
QSKIP("When passing a CR-LF-LF sequence through Windows stdio, it gets converted, "
"so this test fails. Disabled on Windows", SkipSingle);
#endif
#if defined(QT_NO_PROCESS)
QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll);
#else
QFile file(testFileName);
QUrl url = QUrl::fromLocalFile(file.fileName());
QNetworkRequest request(url);
QFETCH(QByteArray, data);
QProcess process;
process.start("echo/echo all");
process.write(data);
process.closeWriteChannel();
QNetworkReplyPtr reply = manager.put(QNetworkRequest(url), &process);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
QVERIFY(reply->readAll().isEmpty());
QVERIFY(file.open(QIODevice::ReadOnly));
QCOMPARE(file.size(), qint64(data.size()));
QByteArray contents = file.readAll();
QCOMPARE(contents, data);
#endif
#endif
}
void tst_QNetworkReply::ioPutToFtpFromFile_data()
{
ioPutToFileFromFile_data();
}
void tst_QNetworkReply::ioPutToFtpFromFile()
{
QFETCH(QString, fileName);
QFile sourceFile(fileName);
QVERIFY(sourceFile.open(QIODevice::ReadOnly));
QUrl url("ftp://" + QtNetworkSettings::serverName());
url.setPath(QString("/qtest/upload/qnetworkaccess-ioPutToFtpFromFile-%1-%2")
.arg(QTest::currentDataTag())
.arg(uniqueExtension));
QNetworkRequest request(url);
QNetworkReplyPtr reply = manager.put(request, &sourceFile);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
QVERIFY(reply->readAll().isEmpty());
QVERIFY(sourceFile.atEnd());
sourceFile.seek(0); // reset it to the beginning
// download the file again from FTP to make sure it was uploaded
// correctly
QFtp ftp;
ftp.connectToHost(url.host());
ftp.login();
ftp.get(url.path());
QObject::connect(&ftp, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(3);
QObject::disconnect(&ftp, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QByteArray uploaded = ftp.readAll();
QCOMPARE(qint64(uploaded.size()), sourceFile.size());
QCOMPARE(uploaded, sourceFile.readAll());
ftp.close();
QObject::connect(&ftp, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QObject::disconnect(&ftp, SIGNAL(done(bool)), &QTestEventLoop::instance(), SLOT(exitLoop()));
}
void tst_QNetworkReply::ioPutToHttpFromFile_data()
{
ioPutToFileFromFile_data();
}
void tst_QNetworkReply::ioPutToHttpFromFile()
{
QFETCH(QString, fileName);
QFile sourceFile(fileName);
QVERIFY(sourceFile.open(QIODevice::ReadOnly));
QUrl url("http://" + QtNetworkSettings::serverName());
url.setPath(QString("/dav/qnetworkaccess-ioPutToHttpFromFile-%1-%2")
.arg(QTest::currentDataTag())
.arg(uniqueExtension));
QNetworkRequest request(url);
QNetworkReplyPtr reply = manager.put(request, &sourceFile);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
// verify that the HTTP status code is 201 Created
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 201);
QVERIFY(sourceFile.atEnd());
sourceFile.seek(0); // reset it to the beginning
// download the file again from HTTP to make sure it was uploaded
// correctly
reply = manager.get(request);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
QCOMPARE(reply->readAll(), sourceFile.readAll());
}
void tst_QNetworkReply::ioPostToHttpFromFile_data()
{
ioPutToFileFromFile_data();
}
void tst_QNetworkReply::ioPostToHttpFromFile()
{
QFETCH(QString, fileName);
QFile sourceFile(fileName);
QVERIFY(sourceFile.open(QIODevice::ReadOnly));
QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi");
QNetworkRequest request(url);
request.setRawHeader("Content-Type", "application/octet-stream");
QNetworkReplyPtr reply = manager.post(request, &sourceFile);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
// verify that the HTTP status code is 200 Ok
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QVERIFY(sourceFile.atEnd());
sourceFile.seek(0); // reset it to the beginning
QCOMPARE(reply->readAll().trimmed(), md5sum(sourceFile.readAll()).toHex());
}
void tst_QNetworkReply::ioPostToHttpFromSocket_data()
{
QTest::addColumn<QByteArray>("data");
QTest::addColumn<QByteArray>("md5sum");
QTest::addColumn<QUrl>("url");
QTest::addColumn<QNetworkProxy>("proxy");
QTest::addColumn<int>("authenticationRequiredCount");
QTest::addColumn<int>("proxyAuthenticationRequiredCount");
for (int i = 0; i < proxies.count(); ++i)
for (int auth = 0; auth < 2; ++auth) {
QUrl url;
if (auth)
url = "http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi";
else
url = "http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi";
QNetworkProxy proxy = proxies.at(i).proxy;
QByteArray testsuffix = QByteArray(auth ? "+auth" : "") + proxies.at(i).tag;
int proxyauthcount = proxies.at(i).requiresAuthentication;
QByteArray data;
data = "";
QTest::newRow("empty" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount;
data = "This is a normal message.";
QTest::newRow("generic" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount;
data = "This is a message to show that Qt rocks!\r\n\n";
QTest::newRow("small" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount;
data = QByteArray("abcd\0\1\2\abcd",12);
QTest::newRow("with-nul" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount;
data = QByteArray(4097, '\4');
QTest::newRow("4k+1" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount;
data = QByteArray(128*1024+1, '\177');
QTest::newRow("128k+1" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount;
}
}
void tst_QNetworkReply::ioPostToHttpFromSocket()
{
qRegisterMetaType<QNetworkProxy>(); // for QSignalSpy
qRegisterMetaType<QAuthenticator *>();
qRegisterMetaType<QNetworkReply *>();
QFETCH(QByteArray, data);
QFETCH(QUrl, url);
QFETCH(QNetworkProxy, proxy);
SocketPair socketpair;
socketpair.create();
QVERIFY(socketpair.endPoints[0] && socketpair.endPoints[1]);
socketpair.endPoints[0]->write(data);
QNetworkRequest request(url);
request.setRawHeader("Content-Type", "application/octet-stream");
manager.setProxy(proxy);
QNetworkReplyPtr reply = manager.post(request, socketpair.endPoints[1]);
socketpair.endPoints[0]->close();
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
QSignalSpy authenticationRequiredSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
QSignalSpy proxyAuthenticationRequiredSpy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
QTestEventLoop::instance().enterLoop(12);
disconnect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
disconnect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
QCOMPARE(reply->error(), QNetworkReply::NoError);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
// verify that the HTTP status code is 200 Ok
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
QTEST(authenticationRequiredSpy.count(), "authenticationRequiredCount");
QTEST(proxyAuthenticationRequiredSpy.count(), "proxyAuthenticationRequiredCount");
}
void tst_QNetworkReply::ioPostToHttpFromSocketSynchronous_data()
{
QTest::addColumn<QByteArray>("data");
QTest::addColumn<QByteArray>("md5sum");
QByteArray data;
data = "";
QTest::newRow("empty") << data << md5sum(data);
data = "This is a normal message.";
QTest::newRow("generic") << data << md5sum(data);
data = "This is a message to show that Qt rocks!\r\n\n";
QTest::newRow("small") << data << md5sum(data);
data = QByteArray("abcd\0\1\2\abcd",12);
QTest::newRow("with-nul") << data << md5sum(data);
data = QByteArray(4097, '\4');
QTest::newRow("4k+1") << data << md5sum(data);
data = QByteArray(128*1024+1, '\177');
QTest::newRow("128k+1") << data << md5sum(data);
data = QByteArray(2*1024*1024+1, '\177');
QTest::newRow("2MB+1") << data << md5sum(data);
}
void tst_QNetworkReply::ioPostToHttpFromSocketSynchronous()
{
QFETCH(QByteArray, data);
SocketPair socketpair;
QVERIFY(socketpair.create());
QVERIFY(socketpair.endPoints[0] && socketpair.endPoints[1]);
socketpair.endPoints[0]->write(data);
socketpair.endPoints[0]->waitForBytesWritten(5000);
// ### for 4.8: make the socket pair unbuffered, to not read everything in one go in QNetworkReplyImplPrivate::setup()
QTestEventLoop::instance().enterLoop(3);
QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi");
QNetworkRequest request(url);
request.setRawHeader("Content-Type", "application/octet-stream");
request.setAttribute(
QNetworkRequest::SynchronousRequestAttribute,
true);
QNetworkReplyPtr reply = manager.post(request, socketpair.endPoints[1]);
QVERIFY(reply->isFinished());
socketpair.endPoints[0]->close();
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
// verify that the HTTP status code is 200 Ok
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
}
// this tests checks if rewinding the POST-data to some place in the middle
// worked.
void tst_QNetworkReply::ioPostToHttpFromMiddleOfFileToEnd()
{
QFile sourceFile(SRCDIR "/rfc3252.txt");
QVERIFY(sourceFile.open(QIODevice::ReadOnly));
// seeking to the middle
sourceFile.seek(sourceFile.size() / 2);
QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi";
QNetworkRequest request(url);
request.setRawHeader("Content-Type", "application/octet-stream");
QNetworkReplyPtr reply = manager.post(request, &sourceFile);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
QTestEventLoop::instance().enterLoop(2);
disconnect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
QVERIFY(!QTestEventLoop::instance().timeout());
// compare half data
sourceFile.seek(sourceFile.size() / 2);
QByteArray data = sourceFile.readAll();
QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
}
void tst_QNetworkReply::ioPostToHttpFromMiddleOfFileFiveBytes()
{
QFile sourceFile(SRCDIR "/rfc3252.txt");
QVERIFY(sourceFile.open(QIODevice::ReadOnly));
// seeking to the middle
sourceFile.seek(sourceFile.size() / 2);
QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi";
QNetworkRequest request(url);
request.setRawHeader("Content-Type", "application/octet-stream");
// only send 5 bytes
request.setHeader(QNetworkRequest::ContentLengthHeader, 5);
QVERIFY(request.header(QNetworkRequest::ContentLengthHeader).isValid());
QNetworkReplyPtr reply = manager.post(request, &sourceFile);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
QTestEventLoop::instance().enterLoop(2);
disconnect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
QVERIFY(!QTestEventLoop::instance().timeout());
// compare half data
sourceFile.seek(sourceFile.size() / 2);
QByteArray data = sourceFile.read(5);
QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
}
void tst_QNetworkReply::ioPostToHttpFromMiddleOfQBufferFiveBytes()
{
// test needed since a QBuffer goes with a different codepath than the QFile
// tested in ioPostToHttpFromMiddleOfFileFiveBytes
QBuffer uploadBuffer;
uploadBuffer.open(QIODevice::ReadWrite);
uploadBuffer.write("1234567890");
uploadBuffer.seek(5);
QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi";
QNetworkRequest request(url);
request.setRawHeader("Content-Type", "application/octet-stream");
QNetworkReplyPtr reply = manager.post(request, &uploadBuffer);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
QTestEventLoop::instance().enterLoop(2);
disconnect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
QVERIFY(!QTestEventLoop::instance().timeout());
// compare half data
uploadBuffer.seek(5);
QByteArray data = uploadBuffer.read(5);
QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
}
void tst_QNetworkReply::ioPostToHttpNoBufferFlag()
{
QByteArray data = QByteArray("daaaaaaataaaaaaa");
// create a sequential QIODevice by feeding the data into a local TCP server
SocketPair socketpair;
socketpair.create();
QVERIFY(socketpair.endPoints[0] && socketpair.endPoints[1]);
socketpair.endPoints[0]->write(data);
QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi";
QNetworkRequest request(url);
request.setRawHeader("Content-Type", "application/octet-stream");
// disallow buffering
request.setAttribute(QNetworkRequest::DoNotBufferUploadDataAttribute, true);
request.setHeader(QNetworkRequest::ContentLengthHeader, data.size());
QNetworkReplyPtr reply = manager.post(request, socketpair.endPoints[1]);
socketpair.endPoints[0]->close();
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
QTestEventLoop::instance().enterLoop(2);
disconnect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
// verify: error code is QNetworkReply::ContentReSendError
QCOMPARE(reply->error(), QNetworkReply::ContentReSendError);
}
#ifndef QT_NO_OPENSSL
class SslServer : public QTcpServer {
Q_OBJECT
public:
SslServer() : socket(0) {};
void incomingConnection(int socketDescriptor) {
QSslSocket *serverSocket = new QSslSocket;
serverSocket->setParent(this);
if (serverSocket->setSocketDescriptor(socketDescriptor)) {
connect(serverSocket, SIGNAL(encrypted()), this, SLOT(encryptedSlot()));
connect(serverSocket, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
serverSocket->setProtocol(QSsl::AnyProtocol);
connect(serverSocket, SIGNAL(sslErrors(const QList<QSslError>&)), serverSocket, SLOT(ignoreSslErrors()));
serverSocket->setLocalCertificate(SRCDIR "/certs/server.pem");
serverSocket->setPrivateKey(SRCDIR "/certs/server.key");
serverSocket->startServerEncryption();
} else {
delete serverSocket;
}
}
signals:
void newEncryptedConnection();
public slots:
void encryptedSlot() {
socket = (QSslSocket*) sender();
emit newEncryptedConnection();
}
void readyReadSlot() {
// for the incoming sockets, not the server socket
//qDebug() << static_cast<QSslSocket*>(sender())->bytesAvailable() << static_cast<QSslSocket*>(sender())->encryptedBytesAvailable();
}
public:
QSslSocket *socket;
};
// very similar to ioPostToHttpUploadProgress but for SSL
void tst_QNetworkReply::ioPostToHttpsUploadProgress()
{
//QFile sourceFile(SRCDIR "/bigfile");
//QVERIFY(sourceFile.open(QIODevice::ReadOnly));
qint64 wantedSize = 2*1024*1024; // 2 MB
QByteArray sourceFile;
// And in the case of SSL, the compression can fool us and let the
// server send the data much faster than expected.
// So better provide random data that cannot be compressed.
for (int i = 0; i < wantedSize; ++i)
sourceFile += (char)qrand();
// emulate a minimal https server
SslServer server;
server.listen(QHostAddress(QHostAddress::LocalHost), 0);
// create the request
QUrl url = QUrl(QString("https://127.0.0.1:%1/").arg(server.serverPort()));
QNetworkRequest request(url);
request.setRawHeader("Content-Type", "application/octet-stream");
QNetworkReplyPtr reply = manager.post(request, sourceFile);
QSignalSpy spy(reply, SIGNAL(uploadProgress(qint64,qint64)));
connect(&server, SIGNAL(newEncryptedConnection()), &QTestEventLoop::instance(), SLOT(exitLoop()));
connect(reply, SIGNAL(sslErrors(const QList<QSslError>&)), reply, SLOT(ignoreSslErrors()));
// get the request started and the incoming socket connected
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QTcpSocket *incomingSocket = server.socket;
QVERIFY(incomingSocket);
disconnect(&server, SIGNAL(newEncryptedConnection()), &QTestEventLoop::instance(), SLOT(exitLoop()));
incomingSocket->setReadBufferSize(1*1024);
QTestEventLoop::instance().enterLoop(2);
// some progress should have been made
QVERIFY(!spy.isEmpty());
QList<QVariant> args = spy.last();
QVERIFY(args.at(0).toLongLong() > 0);
// but not everything!
QVERIFY(args.at(0).toLongLong() != sourceFile.size());
// set the read buffer to unlimited
incomingSocket->setReadBufferSize(0);
QTestEventLoop::instance().enterLoop(10);
// progress should be finished
QVERIFY(!spy.isEmpty());
QList<QVariant> args3 = spy.last();
QCOMPARE(args3.at(0).toLongLong(), args3.at(1).toLongLong());
QCOMPARE(args3.at(0).toLongLong(), qint64(sourceFile.size()));
// after sending this, the QNAM should emit finished()
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
incomingSocket->write("HTTP/1.0 200 OK\r\n");
incomingSocket->write("Content-Length: 0\r\n");
incomingSocket->write("\r\n");
QTestEventLoop::instance().enterLoop(10);
// not timeouted -> finished() was emitted
QVERIFY(!QTestEventLoop::instance().timeout());
incomingSocket->close();
server.close();
}
#endif
void tst_QNetworkReply::ioGetFromBuiltinHttp_data()
{
QTest::addColumn<bool>("https");
QTest::addColumn<int>("bufferSize");
QTest::newRow("http+unlimited") << false << 0;
QTest::newRow("http+limited") << false << 4096;
#ifndef QT_NO_OPENSSL
QTest::newRow("https+unlimited") << true << 0;
QTest::newRow("https+limited") << true << 4096;
#endif
}
void tst_QNetworkReply::ioGetFromBuiltinHttp()
{
QSKIP("Limiting is broken right now, check QTBUG-15065", SkipAll);
QFETCH(bool, https);
QFETCH(int, bufferSize);
QByteArray testData;
// Make the data big enough so that it can fill the kernel buffer
// (which seems to hold 202 KB here)
const int wantedSize = 1200 * 1000;
testData.reserve(wantedSize);
// And in the case of SSL, the compression can fool us and let the
// server send the data much faster than expected.
// So better provide random data that cannot be compressed.
for (int i = 0; i < wantedSize; ++i)
testData += (char)qrand();
QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: ");
httpResponse += QByteArray::number(testData.size());
httpResponse += "\r\n\r\n";
httpResponse += testData;
qDebug() << "Server will send" << (httpResponse.size()-testData.size()) << "bytes of header and"
<< testData.size() << "bytes of data";
const bool fillKernelBuffer = bufferSize > 0;
FastSender server(httpResponse, https, fillKernelBuffer);
QUrl url(QString("%1://127.0.0.1:%2/qtest/rfc3252.txt")
.arg(https?"https":"http")
.arg(server.serverPort()));
QNetworkRequest request(url);
QNetworkReplyPtr reply = manager.get(request);
reply->setReadBufferSize(bufferSize);
reply->ignoreSslErrors();
const int rate = 200; // in kB per sec
RateControlledReader reader(server, reply, rate, bufferSize);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTime loopTime;
loopTime.start();
QTestEventLoop::instance().enterLoop(30);
const int elapsedTime = loopTime.elapsed();
server.wait();
reader.wrapUp();
qDebug() << "send rate:" << server.transferRate << "B/s";
qDebug() << "receive rate:" << reader.totalBytesRead * 1000 / elapsedTime
<< "(it received" << reader.totalBytesRead << "bytes in" << elapsedTime << "ms)";
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), request.url());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), (qint64)testData.size());
if (reader.data.size() < testData.size()) { // oops?
QCOMPARE(reader.data, testData.mid(0, reader.data.size()));
qDebug() << "The data is incomplete, the last" << testData.size() - reader.data.size() << "bytes are missing";
QEXPECT_FAIL("http+limited", "Limiting is broken right now, check QTBUG-15065", Abort);
QEXPECT_FAIL("https+limited", "Limiting is broken right now, check QTBUG-15065", Abort);
}
QCOMPARE(reader.data.size(), testData.size());
QCOMPARE(reader.data, testData);
// OK we got the file alright, but did setReadBufferSize work?
QVERIFY(server.transferRate != -1);
if (bufferSize > 0) {
const int allowedDeviation = 16; // TODO find out why the send rate is 13% faster currently
const int minRate = rate * 1024 * (100-allowedDeviation) / 100;
const int maxRate = rate * 1024 * (100+allowedDeviation) / 100;
qDebug() << minRate << "<="<< server.transferRate << "<=" << maxRate << "?";
QEXPECT_FAIL("http+limited", "Limiting is broken right now, check QTBUG-15065", Continue);
QEXPECT_FAIL("https+limited", "Limiting is broken right now, check QTBUG-15065", Continue);
QVERIFY(server.transferRate >= minRate && server.transferRate <= maxRate);
}
}
void tst_QNetworkReply::ioPostToHttpUploadProgress()
{
QFile sourceFile(SRCDIR "/bigfile");
QVERIFY(sourceFile.open(QIODevice::ReadOnly));
// emulate a minimal http server
QTcpServer server;
server.listen(QHostAddress(QHostAddress::LocalHost), 0);
// create the request
QUrl url = QUrl(QString("http://127.0.0.1:%1/").arg(server.serverPort()));
QNetworkRequest request(url);
request.setRawHeader("Content-Type", "application/octet-stream");
QNetworkReplyPtr reply = manager.post(request, &sourceFile);
QSignalSpy spy(reply, SIGNAL(uploadProgress(qint64,qint64)));
connect(&server, SIGNAL(newConnection()), &QTestEventLoop::instance(), SLOT(exitLoop()));
// get the request started and the incoming socket connected
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QTcpSocket *incomingSocket = server.nextPendingConnection();
QVERIFY(incomingSocket);
disconnect(&server, SIGNAL(newConnection()), &QTestEventLoop::instance(), SLOT(exitLoop()));
incomingSocket->setReadBufferSize(1*1024);
QTestEventLoop::instance().enterLoop(5);
// some progress should have been made
QList<QVariant> args = spy.last();
QVERIFY(!args.isEmpty());
QVERIFY(args.at(0).toLongLong() > 0);
// but not everything!
QVERIFY(args.at(0).toLongLong() != sourceFile.size());
// set the read buffer to unlimited
incomingSocket->setReadBufferSize(0);
QTestEventLoop::instance().enterLoop(10);
// progress should be finished
QList<QVariant> args3 = spy.last();
QVERIFY(!args3.isEmpty());
// More progress than before
QVERIFY(args3.at(0).toLongLong() > args.at(0).toLongLong());
QCOMPARE(args3.at(0).toLongLong(), args3.at(1).toLongLong());
// And actually finished..
QCOMPARE(args3.at(0).toLongLong(), sourceFile.size());
// after sending this, the QNAM should emit finished()
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
incomingSocket->write("HTTP/1.0 200 OK\r\n");
incomingSocket->write("Content-Length: 0\r\n");
incomingSocket->write("\r\n");
QTestEventLoop::instance().enterLoop(10);
// not timeouted -> finished() was emitted
QVERIFY(!QTestEventLoop::instance().timeout());
incomingSocket->close();
server.close();
}
void tst_QNetworkReply::ioPostToHttpEmptyUploadProgress()
{
QByteArray ba;
ba.resize(0);
QBuffer buffer(&ba,0);
QVERIFY(buffer.open(QIODevice::ReadOnly));
// emulate a minimal http server
QTcpServer server;
server.listen(QHostAddress(QHostAddress::LocalHost), 0);
// create the request
QUrl url = QUrl(QString("http://127.0.0.1:%1/").arg(server.serverPort()));
QNetworkRequest request(url);
request.setRawHeader("Content-Type", "application/octet-stream");
QNetworkReplyPtr reply = manager.post(request, &buffer);
QSignalSpy spy(reply, SIGNAL(uploadProgress(qint64,qint64)));
connect(&server, SIGNAL(newConnection()), &QTestEventLoop::instance(), SLOT(exitLoop()));
// get the request started and the incoming socket connected
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QTcpSocket *incomingSocket = server.nextPendingConnection();
QVERIFY(incomingSocket);
// after sending this, the QNAM should emit finished()
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
incomingSocket->write("HTTP/1.0 200 OK\r\n");
incomingSocket->write("Content-Length: 0\r\n");
incomingSocket->write("\r\n");
incomingSocket->flush();
QTestEventLoop::instance().enterLoop(10);
// not timeouted -> finished() was emitted
QVERIFY(!QTestEventLoop::instance().timeout());
// final check: only 1 uploadProgress has been emitted
QVERIFY(spy.length() == 1);
QList<QVariant> args = spy.last();
QVERIFY(!args.isEmpty());
QCOMPARE(args.at(0).toLongLong(), buffer.size());
QCOMPARE(args.at(0).toLongLong(), buffer.size());
incomingSocket->close();
server.close();
}
void tst_QNetworkReply::lastModifiedHeaderForFile()
{
QFileInfo fileInfo(SRCDIR "/bigfile");
QVERIFY(fileInfo.exists());
QUrl url = QUrl::fromLocalFile(fileInfo.filePath());
QNetworkRequest request(url);
QNetworkReplyPtr reply = manager.head(request);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QDateTime header = reply->header(QNetworkRequest::LastModifiedHeader).toDateTime();
QCOMPARE(header, fileInfo.lastModified());
}
void tst_QNetworkReply::lastModifiedHeaderForHttp()
{
// Tue, 22 May 2007 12:04:57 GMT according to webserver
QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/fluke.gif";
QNetworkRequest request(url);
QNetworkReplyPtr reply = manager.head(request);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QDateTime header = reply->header(QNetworkRequest::LastModifiedHeader).toDateTime();
QDateTime realDate = QDateTime::fromString("2007-05-22T12:04:57", Qt::ISODate);
realDate.setTimeSpec(Qt::UTC);
QCOMPARE(header, realDate);
}
void tst_QNetworkReply::httpCanReadLine()
{
QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
QNetworkReplyPtr reply = manager.get(request);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QVERIFY(reply->canReadLine());
QVERIFY(!reply->readAll().isEmpty());
QVERIFY(!reply->canReadLine());
}
void tst_QNetworkReply::rateControl_data()
{
QTest::addColumn<int>("rate");
QTest::newRow("15") << 15;
QTest::newRow("40") << 40;
QTest::newRow("73") << 73;
QTest::newRow("80") << 80;
QTest::newRow("125") << 125;
QTest::newRow("250") << 250;
QTest::newRow("1024") << 1024;
}
void tst_QNetworkReply::rateControl()
{
QSKIP("Test disabled -- only for manual purposes", SkipAll);
// this function tests that we aren't reading from the network
// faster than the data is being consumed.
QFETCH(int, rate);
// ask for 20 seconds worth of data
FastSender sender(20 * rate * 1024);
QNetworkRequest request("debugpipe://localhost:" + QString::number(sender.serverPort()));
QNetworkReplyPtr reply = manager.get(request);
reply->setReadBufferSize(32768);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
QSignalSpy errorSpy(reply, SIGNAL(error(QNetworkReply::NetworkError)));
RateControlledReader reader(sender, reply, rate, 20);
// this test is designed to run for 25 seconds at most
QTime loopTime;
loopTime.start();
QTestEventLoop::instance().enterLoop(40);
int elapsedTime = loopTime.elapsed();
if (!errorSpy.isEmpty()) {
qDebug() << "ERROR!" << errorSpy[0][0] << reply->errorString();
}
qDebug() << "tst_QNetworkReply::rateControl" << "send rate:" << sender.transferRate;
qDebug() << "tst_QNetworkReply::rateControl" << "receive rate:" << reader.totalBytesRead * 1000 / elapsedTime
<< "(it received" << reader.totalBytesRead << "bytes in" << elapsedTime << "ms)";
sender.wait();
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), request.url());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QVERIFY(sender.transferRate != -1);
int minRate = rate * 1024 * 9 / 10;
int maxRate = rate * 1024 * 11 / 10;
QVERIFY(sender.transferRate >= minRate);
QVERIFY(sender.transferRate <= maxRate);
}
void tst_QNetworkReply::downloadProgress_data()
{
QTest::addColumn<int>("loopCount");
QTest::newRow("empty") << 0;
QTest::newRow("small") << 4;
#ifndef Q_OS_SYMBIAN
QTest::newRow("big") << 4096;
#else
// it can run even with 4096
// but it takes lot time
//especially on emulator
QTest::newRow("big") << 1024;
#endif
}
void tst_QNetworkReply::downloadProgress()
{
QTcpServer server;
QVERIFY(server.listen());
QNetworkRequest request("debugpipe://127.0.0.1:" + QString::number(server.serverPort()) + "/?bare=1");
QNetworkReplyPtr reply = manager.get(request);
QSignalSpy spy(reply, SIGNAL(downloadProgress(qint64,qint64)));
connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
&QTestEventLoop::instance(), SLOT(exitLoop()));
QVERIFY(spy.isValid());
QVERIFY(!reply->isFinished());
QVERIFY(reply->isRunning());
QCoreApplication::instance()->processEvents();
if (!server.hasPendingConnections())
server.waitForNewConnection(1000);
QVERIFY(server.hasPendingConnections());
QCOMPARE(spy.count(), 0);
QByteArray data(128, 'a');
QTcpSocket *sender = server.nextPendingConnection();
QVERIFY(sender);
QFETCH(int, loopCount);
for (int i = 1; i <= loopCount; ++i) {
sender->write(data);
QVERIFY2(sender->waitForBytesWritten(2000), "Network timeout");
spy.clear();
QTestEventLoop::instance().enterLoop(2);
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(spy.count() > 0);
QVERIFY(!reply->isFinished());
QVERIFY(reply->isRunning());
QList<QVariant> args = spy.last();
QCOMPARE(args.at(0).toInt(), i*data.size());
QCOMPARE(args.at(1).toInt(), -1);
}
// close the connection:
delete sender;
spy.clear();
QTestEventLoop::instance().enterLoop(2);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(spy.count() > 0);
QVERIFY(!reply->isRunning());
QVERIFY(reply->isFinished());
QList<QVariant> args = spy.last();
QCOMPARE(args.at(0).toInt(), loopCount * data.size());
QCOMPARE(args.at(1).toInt(), loopCount * data.size());
}
void tst_QNetworkReply::uploadProgress_data()
{
putToFile_data();
}
void tst_QNetworkReply::uploadProgress()
{
QFETCH(QByteArray, data);
QTcpServer server;
QVERIFY(server.listen());
QNetworkRequest request("debugpipe://127.0.0.1:" + QString::number(server.serverPort()) + "/?bare=1");
QNetworkReplyPtr reply = manager.put(request, data);
QSignalSpy spy(reply, SIGNAL(uploadProgress(qint64,qint64)));
QSignalSpy finished(reply, SIGNAL(finished()));
QVERIFY(spy.isValid());
QVERIFY(finished.isValid());
QCoreApplication::instance()->processEvents();
if (!server.hasPendingConnections())
server.waitForNewConnection(1000);
QVERIFY(server.hasPendingConnections());
QTcpSocket *receiver = server.nextPendingConnection();
if (finished.count() == 0) {
// it's not finished yet, so wait for it to be
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(2);
QVERIFY(!QTestEventLoop::instance().timeout());
}
delete receiver;
QVERIFY(finished.count() > 0);
QVERIFY(spy.count() > 0);
QList<QVariant> args = spy.last();
QCOMPARE(args.at(0).toInt(), data.size());
QCOMPARE(args.at(1).toInt(), data.size());
}
void tst_QNetworkReply::chaining_data()
{
putToFile_data();
}
void tst_QNetworkReply::chaining()
{
QTemporaryFile sourceFile(QDir::currentPath() + "/temp-XXXXXX");
sourceFile.setAutoRemove(true);
QVERIFY(sourceFile.open());
QFETCH(QByteArray, data);
QVERIFY(sourceFile.write(data) == data.size());
sourceFile.flush();
QCOMPARE(sourceFile.size(), qint64(data.size()));
QNetworkRequest request(QUrl::fromLocalFile(sourceFile.fileName()));
QNetworkReplyPtr getReply = manager.get(request);
QFile targetFile(testFileName);
QUrl url = QUrl::fromLocalFile(targetFile.fileName());
request.setUrl(url);
QNetworkReplyPtr putReply = manager.put(request, getReply);
connect(putReply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(getReply->url(), QUrl::fromLocalFile(sourceFile.fileName()));
QCOMPARE(getReply->error(), QNetworkReply::NoError);
QCOMPARE(getReply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), sourceFile.size());
QCOMPARE(putReply->url(), url);
QCOMPARE(putReply->error(), QNetworkReply::NoError);
QCOMPARE(putReply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
QVERIFY(putReply->readAll().isEmpty());
QVERIFY(sourceFile.atEnd());
sourceFile.seek(0); // reset it to the beginning
QVERIFY(targetFile.open(QIODevice::ReadOnly));
QCOMPARE(targetFile.size(), sourceFile.size());
QCOMPARE(targetFile.readAll(), sourceFile.readAll());
}
void tst_QNetworkReply::receiveCookiesFromHttp_data()
{
QTest::addColumn<QString>("cookieString");
QTest::addColumn<QList<QNetworkCookie> >("expectedCookiesFromHttp");
QTest::addColumn<QList<QNetworkCookie> >("expectedCookiesInJar");
QTest::newRow("empty") << "" << QList<QNetworkCookie>() << QList<QNetworkCookie>();
QList<QNetworkCookie> header, jar;
QNetworkCookie cookie("a", "b");
header << cookie;
cookie.setDomain(QtNetworkSettings::serverName());
cookie.setPath("/qtest/cgi-bin/");
jar << cookie;
QTest::newRow("simple-cookie") << "a=b" << header << jar;
header << QNetworkCookie("c", "d");
cookie.setName("c");
cookie.setValue("d");
jar << cookie;
QTest::newRow("two-cookies") << "a=b, c=d" << header << jar;
QTest::newRow("two-cookies-2") << "a=b\nc=d" << header << jar;
header.clear();
jar.clear();
cookie = QNetworkCookie("a", "b");
cookie.setPath("/not/part-of-path");
header << cookie;
cookie.setDomain(QtNetworkSettings::serverName());
jar << cookie;
QTest::newRow("invalid-cookie-path") << "a=b; path=/not/part-of-path" << header << jar;
jar.clear();
cookie = QNetworkCookie("a", "b");
cookie.setDomain(".example.com");
header.clear();
header << cookie;
QTest::newRow("invalid-cookie-domain") << "a=b; domain=.example.com" << header << jar;
}
void tst_QNetworkReply::receiveCookiesFromHttp()
{
QFETCH(QString, cookieString);
QByteArray data = cookieString.toLatin1() + '\n';
QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/set-cookie.cgi");
QNetworkRequest request(url);
request.setRawHeader("Content-Type", "application/octet-stream");
QNetworkReplyPtr reply;
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data));
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
QList<QNetworkCookie> setCookies =
qvariant_cast<QList<QNetworkCookie> >(reply->header(QNetworkRequest::SetCookieHeader));
QTEST(setCookies, "expectedCookiesFromHttp");
QTEST(cookieJar->allCookies(), "expectedCookiesInJar");
}
void tst_QNetworkReply::receiveCookiesFromHttpSynchronous_data()
{
tst_QNetworkReply::receiveCookiesFromHttp_data();
}
void tst_QNetworkReply::receiveCookiesFromHttpSynchronous()
{
QFETCH(QString, cookieString);
QByteArray data = cookieString.toLatin1() + '\n';
QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/set-cookie.cgi");
QNetworkRequest request(url);
request.setRawHeader("Content-Type", "application/octet-stream");
request.setAttribute(
QNetworkRequest::SynchronousRequestAttribute,
true);
QNetworkReplyPtr reply;
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data));
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
QList<QNetworkCookie> setCookies =
qvariant_cast<QList<QNetworkCookie> >(reply->header(QNetworkRequest::SetCookieHeader));
QTEST(setCookies, "expectedCookiesFromHttp");
QTEST(cookieJar->allCookies(), "expectedCookiesInJar");
}
void tst_QNetworkReply::sendCookies_data()
{
QTest::addColumn<QList<QNetworkCookie> >("cookiesToSet");
QTest::addColumn<QString>("expectedCookieString");
QList<QNetworkCookie> list;
QTest::newRow("empty") << list << "";
QNetworkCookie cookie("a", "b");
cookie.setPath("/");
cookie.setDomain("example.com");
list << cookie;
QTest::newRow("no-match-domain") << list << "";
cookie.setDomain(QtNetworkSettings::serverName());
cookie.setPath("/something/else");
list << cookie;
QTest::newRow("no-match-path") << list << "";
cookie.setPath("/");
list << cookie;
QTest::newRow("simple-cookie") << list << "a=b";
cookie.setPath("/qtest");
cookie.setValue("longer");
list << cookie;
QTest::newRow("two-cookies") << list << "a=longer; a=b";
list.clear();
cookie = QNetworkCookie("a", "b");
cookie.setPath("/");
cookie.setDomain("." + QtNetworkSettings::serverDomainName());
list << cookie;
QTest::newRow("domain-match") << list << "a=b";
// but it shouldn't match this:
cookie.setDomain(QtNetworkSettings::serverDomainName());
list << cookie;
QTest::newRow("domain-match-2") << list << "a=b";
}
void tst_QNetworkReply::sendCookies()
{
QFETCH(QString, expectedCookieString);
QFETCH(QList<QNetworkCookie>, cookiesToSet);
cookieJar->setAllCookies(cookiesToSet);
QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/get-cookie.cgi");
QNetworkRequest request(url);
QNetworkReplyPtr reply;
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
QCOMPARE(QString::fromLatin1(reply->readAll()).trimmed(), expectedCookieString);
}
void tst_QNetworkReply::sendCookiesSynchronous_data()
{
tst_QNetworkReply::sendCookies_data();
}
void tst_QNetworkReply::sendCookiesSynchronous()
{
QFETCH(QString, expectedCookieString);
QFETCH(QList<QNetworkCookie>, cookiesToSet);
cookieJar->setAllCookies(cookiesToSet);
QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/get-cookie.cgi");
QNetworkRequest request(url);
request.setAttribute(
QNetworkRequest::SynchronousRequestAttribute,
true);
QNetworkReplyPtr reply;
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
QCOMPARE(QString::fromLatin1(reply->readAll()).trimmed(), expectedCookieString);
}
void tst_QNetworkReply::nestedEventLoops_slot()
{
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_QNetworkReply::nestedEventLoops()
{
// Slightly fragile test, it may not be testing anything
// This is certifying that we're not running into the same issue
// that QHttp had (task 200432): the QTcpSocket connection is
// closed by the remote end because of the kept-alive HTTP
// connection timed out.
//
// The exact time required for this to happen is not exactly
// defined. Our server (Apache httpd) times out after 15
// seconds. (see above)
qDebug("Takes 16 seconds to run, please wait");
qRegisterMetaType<QNetworkReply::NetworkError>();
QUrl url("http://" + QtNetworkSettings::serverName());
QNetworkRequest request(url);
QNetworkReplyPtr reply = manager.get(request);
QSignalSpy finishedspy(reply, SIGNAL(finished()));
QSignalSpy errorspy(reply, SIGNAL(error(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(finished()), SLOT(nestedEventLoops_slot()));
QTestEventLoop::instance().enterLoop(20);
QVERIFY2(!QTestEventLoop::instance().timeout(), "Network timeout");
QCOMPARE(finishedspy.count(), 1);
QCOMPARE(errorspy.count(), 0);
}
void tst_QNetworkReply::httpProxyCommands_data()
{
QTest::addColumn<QUrl>("url");
QTest::addColumn<QByteArray>("responseToSend");
QTest::addColumn<QString>("expectedCommand");
QTest::newRow("http")
<< QUrl("http://0.0.0.0:4443/http-request")
<< QByteArray("HTTP/1.0 200 OK\r\nProxy-Connection: close\r\nContent-Length: 1\r\n\r\n1")
<< "GET http://0.0.0.0:4443/http-request HTTP/1.";
#ifndef QT_NO_OPENSSL
QTest::newRow("https")
<< QUrl("https://0.0.0.0:4443/https-request")
<< QByteArray("HTTP/1.0 200 Connection Established\r\n\r\n")
<< "CONNECT 0.0.0.0:4443 HTTP/1.";
#endif
}
void tst_QNetworkReply::httpProxyCommands()
{
QFETCH(QUrl, url);
QFETCH(QByteArray, responseToSend);
QFETCH(QString, expectedCommand);
MiniHttpServer proxyServer(responseToSend);
QNetworkProxy proxy(QNetworkProxy::HttpProxy, "127.0.0.1", proxyServer.serverPort());
manager.setProxy(proxy);
QNetworkRequest request(url);
request.setRawHeader("User-Agent", "QNetworkReplyAutoTest/1.0");
QNetworkReplyPtr reply = manager.get(request);
//clearing the proxy here causes the test to fail.
//the proxy isn't used until after the bearer has been started
//which is correct in general, because system proxy isn't known until that time.
//removing this line is safe, as the proxy is also reset by the cleanup() function
//manager.setProxy(QNetworkProxy());
// wait for the finished signal
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(15);
QVERIFY(!QTestEventLoop::instance().timeout());
//qDebug() << reply->error() << reply->errorString();
//qDebug() << proxyServer.receivedData;
// we don't really care if the request succeeded
// especially since it won't succeed in the HTTPS case
// so just check that the command was correct
QString receivedHeader = proxyServer.receivedData.left(expectedCommand.length());
QCOMPARE(receivedHeader, expectedCommand);
//QTBUG-17223 - make sure the user agent from the request is sent to proxy server even for CONNECT
int uapos = proxyServer.receivedData.indexOf("User-Agent");
int uaend = proxyServer.receivedData.indexOf("\r\n", uapos);
QByteArray uaheader = proxyServer.receivedData.mid(uapos, uaend - uapos);
QCOMPARE(uaheader, QByteArray("User-Agent: QNetworkReplyAutoTest/1.0"));
}
class ProxyChangeHelper : public QObject {
Q_OBJECT
public:
ProxyChangeHelper() : QObject(), signalCount(0) {};
public slots:
void finishedSlot() {
signalCount++;
if (signalCount == 2)
QMetaObject::invokeMethod(&QTestEventLoop::instance(), "exitLoop", Qt::QueuedConnection);
}
private:
int signalCount;
};
void tst_QNetworkReply::httpProxyCommandsSynchronous_data()
{
httpProxyCommands_data();
}
struct QThreadCleanup
{
static inline void cleanup(QThread *thread)
{
thread->quit();
if (thread->wait(3000))
delete thread;
else
qWarning("thread hung, leaking memory so test can finish");
}
};
struct QDeleteLaterCleanup
{
static inline void cleanup(QObject *o)
{
o->deleteLater();
}
};
void tst_QNetworkReply::httpProxyCommandsSynchronous()
{
QFETCH(QUrl, url);
QFETCH(QByteArray, responseToSend);
QFETCH(QString, expectedCommand);
// when using synchronous commands, we need a different event loop for
// the server thread, because the client is never returning to the
// event loop
QScopedPointer<QThread, QThreadCleanup> serverThread(new QThread);
QScopedPointer<MiniHttpServer, QDeleteLaterCleanup> proxyServer(new MiniHttpServer(responseToSend, false, serverThread.data()));
QNetworkProxy proxy(QNetworkProxy::HttpProxy, "127.0.0.1", proxyServer->serverPort());
manager.setProxy(proxy);
QNetworkRequest request(url);
// send synchronous request
request.setAttribute(
QNetworkRequest::SynchronousRequestAttribute,
true);
QNetworkReplyPtr reply = manager.get(request);
QVERIFY(reply->isFinished()); // synchronous
manager.setProxy(QNetworkProxy());
//qDebug() << reply->error() << reply->errorString();
// we don't really care if the request succeeded
// especially since it won't succeed in the HTTPS case
// so just check that the command was correct
QString receivedHeader = proxyServer->receivedData.left(expectedCommand.length());
QCOMPARE(receivedHeader, expectedCommand);
}
void tst_QNetworkReply::proxyChange()
{
ProxyChangeHelper helper;
MiniHttpServer proxyServer(
"HTTP/1.0 200 OK\r\nProxy-Connection: keep-alive\r\n"
"Content-Length: 1\r\n\r\n1");
QNetworkProxy dummyProxy(QNetworkProxy::HttpProxy, "127.0.0.1", proxyServer.serverPort());
QNetworkRequest req(QUrl("http://" + QtNetworkSettings::serverName()));
proxyServer.doClose = false;
manager.setProxy(dummyProxy);
QNetworkReplyPtr reply1 = manager.get(req);
connect(reply1, SIGNAL(finished()), &helper, SLOT(finishedSlot()));
manager.setProxy(QNetworkProxy());
QNetworkReplyPtr reply2 = manager.get(req);
connect(reply2, SIGNAL(finished()), &helper, SLOT(finishedSlot()));
QTestEventLoop::instance().enterLoop(20);
QVERIFY(!QTestEventLoop::instance().timeout());
// verify that the replies succeeded
QCOMPARE(reply1->error(), QNetworkReply::NoError);
QCOMPARE(reply1->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QVERIFY(reply1->size() == 1);
QCOMPARE(reply2->error(), QNetworkReply::NoError);
QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QVERIFY(reply2->size() > 1);
// now try again and get an error
// this verifies that we reuse the already-open connection
proxyServer.doClose = true;
proxyServer.dataToTransmit =
"HTTP/1.0 403 Forbidden\r\nProxy-Connection: close\r\n"
"Content-Length: 1\r\n\r\n1";
manager.setProxy(dummyProxy);
QNetworkReplyPtr reply3 = manager.get(req);
connect(reply3, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(5);
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(int(reply3->error()) > 0);
}
void tst_QNetworkReply::authorizationError_data()
{
QTest::addColumn<QString>("url");
QTest::addColumn<int>("errorSignalCount");
QTest::addColumn<int>("finishedSignalCount");
QTest::addColumn<int>("error");
QTest::addColumn<int>("httpStatusCode");
QTest::addColumn<QString>("httpBody");
QTest::newRow("unknown-authorization-method") << "http://" + QtNetworkSettings::serverName() +
"/qtest/cgi-bin/http-unknown-authentication-method.cgi?401-authorization-required" << 1 << 1
<< int(QNetworkReply::AuthenticationRequiredError) << 401 << "authorization required";
QTest::newRow("unknown-proxy-authorization-method") << "http://" + QtNetworkSettings::serverName() +
"/qtest/cgi-bin/http-unknown-authentication-method.cgi?407-proxy-authorization-required" << 1 << 1
<< int(QNetworkReply::ProxyAuthenticationRequiredError) << 407
<< "authorization required";
}
void tst_QNetworkReply::authorizationError()
{
QFETCH(QString, url);
QNetworkRequest request(url);
QNetworkReplyPtr reply = manager.get(request);
QCOMPARE(reply->error(), QNetworkReply::NoError);
qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
QSignalSpy errorSpy(reply, SIGNAL(error(QNetworkReply::NetworkError)));
QSignalSpy finishedSpy(reply, SIGNAL(finished()));
// now run the request:
connect(reply, SIGNAL(finished()),
&QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QFETCH(int, errorSignalCount);
QCOMPARE(errorSpy.count(), errorSignalCount);
QFETCH(int, finishedSignalCount);
QCOMPARE(finishedSpy.count(), finishedSignalCount);
QFETCH(int, error);
QCOMPARE(reply->error(), QNetworkReply::NetworkError(error));
QFETCH(int, httpStatusCode);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), httpStatusCode);
QFETCH(QString, httpBody);
QCOMPARE(qint64(reply->size()), qint64(httpBody.size()));
QCOMPARE(QString(reply->readAll()), httpBody);
}
void tst_QNetworkReply::httpConnectionCount()
{
QTcpServer server;
QVERIFY(server.listen());
QCoreApplication::instance()->processEvents();
for (int i = 0; i < 10; i++) {
QNetworkRequest request (QUrl("http://127.0.0.1:" + QString::number(server.serverPort()) + "/" + QString::number(i)));
QNetworkReply* reply = manager.get(request);
reply->setParent(&server);
}
int pendingConnectionCount = 0;
QTime time;
time.start();
while(pendingConnectionCount <= 20) {
QTestEventLoop::instance().enterLoop(1);
QTcpSocket *socket = server.nextPendingConnection();
while (socket != 0) {
pendingConnectionCount++;
socket->setParent(&server);
socket = server.nextPendingConnection();
}
// at max. wait 10 sec
if (time.elapsed() > 10000)
break;
}
#ifdef Q_OS_SYMBIAN
// see in qhttpnetworkconnection.cpp
// hardcoded defaultChannelCount = 3
QCOMPARE(pendingConnectionCount, 3);
#else
QCOMPARE(pendingConnectionCount, 6);
#endif
}
void tst_QNetworkReply::httpReUsingConnectionSequential_data()
{
QTest::addColumn<bool>("doDeleteLater");
QTest::newRow("deleteLater") << true;
QTest::newRow("noDeleteLater") << false;
}
void tst_QNetworkReply::httpReUsingConnectionSequential()
{
QFETCH(bool, doDeleteLater);
QByteArray response("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
MiniHttpServer server(response);
server.multiple = true;
server.doClose = false;
QUrl url;
url.setScheme("http");
url.setPort(server.serverPort());
url.setHost("127.0.0.1");
// first request
QNetworkReply* reply1 = manager.get(QNetworkRequest(url));
connect(reply1, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(2);
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(!reply1->error());
int reply1port = server.client->peerPort();
if (doDeleteLater)
reply1->deleteLater();
// finished received, send the next one
QNetworkReply*reply2 = manager.get(QNetworkRequest(url));
connect(reply2, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(2);
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(!reply2->error());
int reply2port = server.client->peerPort(); // should still be the same object
QVERIFY(reply1port > 0);
QCOMPARE(server.totalConnections, 1);
QCOMPARE(reply2port, reply1port);
if (!doDeleteLater)
reply1->deleteLater(); // only do it if it was not done earlier
reply2->deleteLater();
}
class HttpReUsingConnectionFromFinishedSlot : public QObject {
Q_OBJECT
public:
QNetworkReply* reply1;
QNetworkReply* reply2;
QUrl url;
QNetworkAccessManager manager;
public slots:
void finishedSlot() {
QVERIFY(!reply1->error());
QFETCH(bool, doDeleteLater);
if (doDeleteLater) {
reply1->deleteLater();
reply1 = 0;
}
// kick off 2nd request and exit the loop when it is done
reply2 = manager.get(QNetworkRequest(url));
reply2->setParent(this);
connect(reply2, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
}
};
void tst_QNetworkReply::httpReUsingConnectionFromFinishedSlot_data()
{
httpReUsingConnectionSequential_data();
}
void tst_QNetworkReply::httpReUsingConnectionFromFinishedSlot()
{
QByteArray response("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
MiniHttpServer server(response);
server.multiple = true;
server.doClose = false;
HttpReUsingConnectionFromFinishedSlot helper;
helper.reply1 = 0;
helper.reply2 = 0;
helper.url.setScheme("http");
helper.url.setPort(server.serverPort());
helper.url.setHost("127.0.0.1");
// first request
helper.reply1 = helper.manager.get(QNetworkRequest(helper.url));
helper.reply1->setParent(&helper);
connect(helper.reply1, SIGNAL(finished()), &helper, SLOT(finishedSlot()));
QTestEventLoop::instance().enterLoop(4);
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(helper.reply2);
QVERIFY(!helper.reply2->error());
QCOMPARE(server.totalConnections, 1);
}
class HttpRecursiveCreationHelper : public QObject {
Q_OBJECT
public:
HttpRecursiveCreationHelper():
QObject(0),
requestsStartedCount_finished(0),
requestsStartedCount_readyRead(0),
requestsFinishedCount(0)
{
}
QNetworkAccessManager manager;
int requestsStartedCount_finished;
int requestsStartedCount_readyRead;
int requestsFinishedCount;
public slots:
void finishedSlot() {
requestsFinishedCount++;
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
QVERIFY(!reply->error());
QVERIFY(reply->bytesAvailable() == 27906);
if (requestsFinishedCount == 60) {
QTestEventLoop::instance().exitLoop();
return;
}
if (requestsStartedCount_finished < 30) {
startOne();
requestsStartedCount_finished++;
}
reply->deleteLater();
}
void readyReadSlot() {
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
QVERIFY(!reply->error());
if (requestsStartedCount_readyRead < 30 && reply->bytesAvailable() > 27906/2) {
startOne();
requestsStartedCount_readyRead++;
}
}
void startOne() {
QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/fluke.gif";
QNetworkRequest request(url);
QNetworkReply *reply = manager.get(request);
reply->setParent(this);
connect(reply, SIGNAL(finished()), this, SLOT(finishedSlot()));
connect(reply, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
}
};
void tst_QNetworkReply::httpRecursiveCreation()
{
// this test checks if creation of new requests to the same host properly works
// from readyRead() and finished() signals
HttpRecursiveCreationHelper helper;
helper.startOne();
QTestEventLoop::instance().enterLoop(30);
QVERIFY(!QTestEventLoop::instance().timeout());
}
#ifndef QT_NO_OPENSSL
void tst_QNetworkReply::ignoreSslErrorsList_data()
{
QTest::addColumn<QString>("url");
QTest::addColumn<QList<QSslError> >("expectedSslErrors");
QTest::addColumn<QNetworkReply::NetworkError>("expectedNetworkError");
QList<QSslError> expectedSslErrors;
// apparently, because of some weird behaviour of SRCDIR, the file name below needs to start with a slash
QList<QSslCertificate> certs = QSslCertificate::fromPath(QLatin1String(SRCDIR "/certs/qt-test-server-cacert.pem"));
QSslError rightError(QSslError::SelfSignedCertificate, certs.at(0));
QSslError wrongError(QSslError::SelfSignedCertificate);
QTest::newRow("SSL-failure-empty-list") << "https://" + QtNetworkSettings::serverName() + "/index.html" << expectedSslErrors << QNetworkReply::SslHandshakeFailedError;
expectedSslErrors.append(wrongError);
QTest::newRow("SSL-failure-wrong-error") << "https://" + QtNetworkSettings::serverName() + "/index.html" << expectedSslErrors << QNetworkReply::SslHandshakeFailedError;
expectedSslErrors.append(rightError);
QTest::newRow("allErrorsInExpectedList1") << "https://" + QtNetworkSettings::serverName() + "/index.html" << expectedSslErrors << QNetworkReply::NoError;
expectedSslErrors.removeAll(wrongError);
QTest::newRow("allErrorsInExpectedList2") << "https://" + QtNetworkSettings::serverName() + "/index.html" << expectedSslErrors << QNetworkReply::NoError;
expectedSslErrors.removeAll(rightError);
QTest::newRow("SSL-failure-empty-list-again") << "https://" + QtNetworkSettings::serverName() + "/index.html" << expectedSslErrors << QNetworkReply::SslHandshakeFailedError;
}
void tst_QNetworkReply::ignoreSslErrorsList()
{
QFETCH(QString, url);
QNetworkRequest request(url);
QNetworkReplyPtr reply = manager.get(request);
QFETCH(QList<QSslError>, expectedSslErrors);
reply->ignoreSslErrors(expectedSslErrors);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QFETCH(QNetworkReply::NetworkError, expectedNetworkError);
QCOMPARE(reply->error(), expectedNetworkError);
}
void tst_QNetworkReply::ignoreSslErrorsListWithSlot_data()
{
ignoreSslErrorsList_data();
}
// this is not a test, just a slot called in the test below
void tst_QNetworkReply::ignoreSslErrorListSlot(QNetworkReply *reply, const QList<QSslError> &)
{
reply->ignoreSslErrors(storedExpectedSslErrors);
}
// do the same as in ignoreSslErrorsList, but ignore the errors in the slot
void tst_QNetworkReply::ignoreSslErrorsListWithSlot()
{
QFETCH(QString, url);
QNetworkRequest request(url);
QNetworkReplyPtr reply = manager.get(request);
QFETCH(QList<QSslError>, expectedSslErrors);
// store the errors to ignore them later in the slot connected below
storedExpectedSslErrors = expectedSslErrors;
connect(&manager, SIGNAL(sslErrors(QNetworkReply *, const QList<QSslError> &)),
this, SLOT(ignoreSslErrorListSlot(QNetworkReply *, const QList<QSslError> &)));
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QFETCH(QNetworkReply::NetworkError, expectedNetworkError);
QCOMPARE(reply->error(), expectedNetworkError);
}
void tst_QNetworkReply::sslConfiguration_data()
{
QTest::addColumn<QSslConfiguration>("configuration");
QTest::addColumn<bool>("works");
QTest::newRow("empty") << QSslConfiguration() << false;
QSslConfiguration conf = QSslConfiguration::defaultConfiguration();
QTest::newRow("default") << conf << false; // does not contain test server cert
QList<QSslCertificate> testServerCert = QSslCertificate::fromPath(SRCDIR "/certs/qt-test-server-cacert.pem");
conf.setCaCertificates(testServerCert);
QTest::newRow("set-root-cert") << conf << true;
conf.setProtocol(QSsl::SecureProtocols);
QTest::newRow("secure") << conf << true;
}
void tst_QNetworkReply::sslConfiguration()
{
QNetworkRequest request(QUrl("https://" + QtNetworkSettings::serverName() + "/index.html"));
QFETCH(QSslConfiguration, configuration);
request.setSslConfiguration(configuration);
QNetworkReplyPtr reply = manager.get(request);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QFETCH(bool, works);
QNetworkReply::NetworkError expectedError = works ? QNetworkReply::NoError : QNetworkReply::SslHandshakeFailedError;
QCOMPARE(reply->error(), expectedError);
}
#endif // QT_NO_OPENSSL
void tst_QNetworkReply::getAndThenDeleteObject_data()
{
QTest::addColumn<bool>("replyFirst");
QTest::newRow("delete-reply-first") << true;
QTest::newRow("delete-qnam-first") << false;
}
void tst_QNetworkReply::getAndThenDeleteObject()
{
// yes, this will leak if the testcase fails. I don't care. It must not fail then :P
QNetworkAccessManager *manager = new QNetworkAccessManager();
QNetworkRequest request("http://" + QtNetworkSettings::serverName() + "/qtest/bigfile");
QNetworkReply *reply = manager->get(request);
reply->setReadBufferSize(1);
reply->setParent((QObject*)0); // must be 0 because else it is the manager
QTime stopWatch;
stopWatch.start();
forever {
QCoreApplication::instance()->processEvents();
if (reply->bytesAvailable())
break;
if (stopWatch.elapsed() >= 30000)
break;
}
QVERIFY(reply->bytesAvailable());
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QVERIFY(!reply->isFinished()); // must not be finished
QFETCH(bool, replyFirst);
if (replyFirst) {
delete reply;
delete manager;
} else {
delete manager;
delete reply;
}
}
// see https://bugs.webkit.org/show_bug.cgi?id=38935
void tst_QNetworkReply::symbianOpenCDataUrlCrash()
{
QString requestUrl("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAWCAYAAAA1vze2AAAAB3RJTUUH2AUSEgolrgBvVQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAARnQU1BAACxjwv8YQUAAAHlSURBVHja5VbNShxBEK6ZaXtnHTebQPA1gngNmfaeq+QNPIlIXkC9iQdJxJNvEHLN3VkxhxxE8gTmEhAVddXZ6Z3f9Ndriz89/sHmkBQUVVT1fB9d9c3uOERUKTunIdn3HzstxGpYBDS4wZk7TAJj/wlJ90J+jnuygqs8svSj+/rGHBos3rE18XBvfU3no7NzlJfUaY/5whAwl8Lr/WDUv4ODxTMb+P5xLExe5LmO559WqTX/MQR4WZYEAtSePS4pE0qSnuhnRUcBU5Gm2k9XljU4Z26I3NRxBrd80rj2fh+KNE0FY4xevRgTjREvPFpasAK8Xli6MUbbuKw3afAGgSBXozo5u4hkmncAlkl5wx8iMGbdyQjnCFEiEwGiosj1UQA/x2rVddiVoi+l4IxE0PTDnx+mrQBvvnx9cFz3krhVvuhzFn579/aq/n5rW8fbtTqiWhIQZEo17YBvbkxOXNVndnYpTvod7AtiuN2re0+siwcB9oH8VxxrNwQQAhzyRs30n7wTI2HIN2g2QtQwjjhJIQatOq7E8bIVCLwzpl83Lvtvl+NohWWlE8UZTWEMAGCcR77fHKhPnZF5tYie6dfdxCphACmLPM+j8bYfmTryg64kV9Vh3mV8jP0b/4wO/YUPiT/8i0MLf55lSQAAAABJRU5ErkJggg==");
QUrl url = QUrl::fromEncoded(requestUrl.toLatin1());
QNetworkRequest req(url);
QNetworkReplyPtr reply;
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, req, reply));
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(598));
}
void tst_QNetworkReply::getFromHttpIntoBuffer_data()
{
QTest::addColumn<QUrl>("url");
QTest::newRow("rfc-internal") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt");
}
// Please note that the whole "zero copy" download buffer API is private right now. Do not use it.
void tst_QNetworkReply::getFromHttpIntoBuffer()
{
QFETCH(QUrl, url);
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute, 1024*128); // 128 kB
QNetworkAccessManager manager;
QNetworkReply *reply = manager.get(request);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(reply->isFinished());
QFile reference(SRCDIR "/rfc3252.txt");
QVERIFY(reference.open(QIODevice::ReadOnly));
QCOMPARE(reference.bytesAvailable(), reply->bytesAvailable());
QCOMPARE(reference.size(), reply->size());
// Compare the memory buffer
QVariant downloadBufferAttribute = reply->attribute(QNetworkRequest::DownloadBufferAttribute);
QVERIFY(downloadBufferAttribute.isValid());
QSharedPointer<char> sharedPointer = downloadBufferAttribute.value<QSharedPointer<char> >();
bool memoryComparison =
(0 == memcmp(static_cast<void*>(reference.readAll().data()),
sharedPointer.data(), reference.size()));
QVERIFY(memoryComparison);
// Make sure the normal reading works
reference.seek(0);
QCOMPARE(reply->read(42), reference.read(42));
QCOMPARE(reply->getChar(0), reference.getChar(0));
QCOMPARE(reply->peek(23), reference.peek(23));
QCOMPARE(reply->readLine(), reference.readLine());
QCOMPARE(reference.bytesAvailable(), reply->bytesAvailable());
QCOMPARE(reply->readAll(), reference.readAll());
QVERIFY(reply->atEnd());
}
// FIXME we really need to consolidate all those server implementations
class GetFromHttpIntoBuffer2Server : QObject {
Q_OBJECT
qint64 dataSize;
qint64 dataSent;
QTcpServer server;
QTcpSocket *client;
bool serverSendsContentLength;
bool chunkedEncoding;
public:
GetFromHttpIntoBuffer2Server (qint64 ds, bool sscl, bool ce) : dataSize(ds), dataSent(0),
client(0), serverSendsContentLength(sscl), chunkedEncoding(ce) {
server.listen();
connect(&server, SIGNAL(newConnection()), this, SLOT(newConnectionSlot()));
}
int serverPort() {
return server.serverPort();
}
public slots:
void newConnectionSlot() {
client = server.nextPendingConnection();
client->setParent(this);
connect(client, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
connect(client, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWrittenSlot(qint64)));
}
void readyReadSlot() {
client->readAll();
client->write("HTTP/1.0 200 OK\n");
if (serverSendsContentLength)
client->write(QString("Content-Length: " + QString::number(dataSize) + "\n").toAscii());
if (chunkedEncoding)
client->write(QString("Transfer-Encoding: chunked\n").toAscii());
client->write("Connection: close\n\n");
}
void bytesWrittenSlot(qint64 amount) {
Q_UNUSED(amount);
if (dataSent == dataSize && client) {
// close eventually
// chunked encoding: we have to send a last "empty" chunk
if (chunkedEncoding)
client->write(QString("0\r\n\r\n").toAscii());
client->disconnectFromHost();
server.close();
client = 0;
return;
}
// send data
if (client && client->bytesToWrite() < 100*1024 && dataSent < dataSize) {
qint64 amount = qMin(qint64(16*1024), dataSize - dataSent);
QByteArray data(amount, '@');
if (chunkedEncoding) {
client->write(QString(QString("%1").arg(amount,0,16).toUpper() + "\r\n").toAscii());
client->write(data.constData(), amount);
client->write(QString("\r\n").toAscii());
} else {
client->write(data.constData(), amount);
}
dataSent += amount;
}
}
};
class GetFromHttpIntoBuffer2Client : QObject {
Q_OBJECT
private:
bool useDownloadBuffer;
QNetworkReply *reply;
qint64 uploadSize;
QList<qint64> bytesAvailableList;
public:
GetFromHttpIntoBuffer2Client (QNetworkReply *reply, bool useDownloadBuffer, qint64 uploadSize)
: useDownloadBuffer(useDownloadBuffer), reply(reply), uploadSize(uploadSize)
{
connect(reply, SIGNAL(metaDataChanged()), this, SLOT(metaDataChangedSlot()));
connect(reply, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
connect(reply, SIGNAL(finished()), this, SLOT(finishedSlot()));
}
public slots:
void metaDataChangedSlot() {
if (useDownloadBuffer) {
QSharedPointer<char> sharedPointer = qvariant_cast<QSharedPointer<char> >(reply->attribute(QNetworkRequest::DownloadBufferAttribute));
QVERIFY(!sharedPointer.isNull()); // It will be 0 if it failed
}
// metaDataChanged needs to come before everything else
QVERIFY(bytesAvailableList.isEmpty());
}
void readyReadSlot() {
QVERIFY(!reply->isFinished());
qint64 bytesAvailable = reply->bytesAvailable();
// bytesAvailable must never be 0
QVERIFY(bytesAvailable != 0);
if (bytesAvailableList.length() < 5) {
// We assume that the first few times the bytes available must be less than the complete size, e.g.
// the bytesAvailable() function works correctly in case of a downloadBuffer.
QVERIFY(bytesAvailable < uploadSize);
}
if (!bytesAvailableList.isEmpty()) {
// Also check that the same bytesAvailable is not coming twice in a row
QVERIFY(bytesAvailableList.last() != bytesAvailable);
}
bytesAvailableList.append(bytesAvailable);
// Add bytesAvailable to a list an parse
}
void finishedSlot() {
// We should have already received all readyRead
QVERIFY(!bytesAvailableList.isEmpty());
QVERIFY(bytesAvailableList.last() == uploadSize);
}
};
void tst_QNetworkReply::getFromHttpIntoBuffer2_data()
{
QTest::addColumn<bool>("useDownloadBuffer");
QTest::newRow("use-download-buffer") << true;
QTest::newRow("do-not-use-download-buffer") << false;
}
// This test checks mostly that signal emissions are in correct order
// Please note that the whole "zero copy" download buffer API is private right now. Do not use it.
void tst_QNetworkReply::getFromHttpIntoBuffer2()
{
QFETCH(bool, useDownloadBuffer);
// On my Linux Desktop the results are already visible with 128 kB, however we use this to have good results.
#if defined(Q_OS_SYMBIAN) || defined(Q_WS_WINCE_WM)
// Show some mercy to non-desktop platform/s
enum {UploadSize = 4*1024*1024}; // 4 MB
#else
enum {UploadSize = 32*1024*1024}; // 32 MB
#endif
GetFromHttpIntoBuffer2Server server(UploadSize, true, false);
QNetworkRequest request(QUrl("http://127.0.0.1:" + QString::number(server.serverPort()) + "/?bare=1"));
if (useDownloadBuffer)
request.setAttribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute, 1024*1024*128); // 128 MB is max allowed
QNetworkAccessManager manager;
QNetworkReplyPtr reply = manager.get(request);
GetFromHttpIntoBuffer2Client client(reply, useDownloadBuffer, UploadSize);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
QTestEventLoop::instance().enterLoop(40);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QVERIFY(!QTestEventLoop::instance().timeout());
}
// Is handled somewhere else too, introduced this special test to have it more accessible
void tst_QNetworkReply::ioGetFromHttpWithoutContentLength()
{
QByteArray dataToSend("HTTP/1.0 200 OK\r\n\r\nHALLO! 123!");
MiniHttpServer server(dataToSend);
server.doClose = true;
QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
QNetworkReplyPtr reply = manager.get(request);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->url(), request.url());
QVERIFY(reply->isFinished());
QVERIFY(reply->error() == QNetworkReply::NoError);
}
// Is handled somewhere else too, introduced this special test to have it more accessible
void tst_QNetworkReply::ioGetFromHttpBrokenChunkedEncoding()
{
// This is wrong chunked encoding because of the X. What actually has to follow is \r\n
// and then the declaration of the final 0 chunk
QByteArray dataToSend("HTTP/1.0 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n3\r\nABCX");
MiniHttpServer server(dataToSend);
server.doClose = false; // FIXME
QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
QNetworkReplyPtr reply = manager.get(request);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QEXPECT_FAIL(0, "We should close the socket and not just do nothing", Continue);
QVERIFY(!QTestEventLoop::instance().timeout());
QEXPECT_FAIL(0, "We should close the socket and not just do nothing", Continue);
QVERIFY(reply->isFinished());
QCOMPARE(reply->error(), QNetworkReply::NoError);
}
// TODO:
// Prepare a gzip that has one chunk that expands to the size mentioned in the bugreport.
// Then have a custom HTTP server that waits after this chunk so the returning gets
// triggered.
void tst_QNetworkReply::qtbug12908compressedHttpReply()
{
QString header("HTTP/1.0 200 OK\r\nContent-Encoding: gzip\r\nContent-Length: 63\r\n\r\n");
// dd if=/dev/zero of=qtbug-12908 bs=16384 count=1 && gzip qtbug-12908 && base64 -w 0 qtbug-12908.gz
QString encodedFile("H4sICDdDaUwAA3F0YnVnLTEyOTA4AO3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAIC3AYbSVKsAQAAA");
QByteArray decodedFile = QByteArray::fromBase64(encodedFile.toAscii());
QCOMPARE(decodedFile.size(), 63);
MiniHttpServer server(header.toAscii() + decodedFile);
server.doClose = true;
QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
QNetworkReplyPtr reply = manager.get(request);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->size(), qint64(16384));
QCOMPARE(reply->readAll(), QByteArray(16384, '\0'));
}
void tst_QNetworkReply::compressedHttpReplyBrokenGzip()
{
QString header("HTTP/1.0 200 OK\r\nContent-Encoding: gzip\r\nContent-Length: 63\r\n\r\n");
// dd if=/dev/zero of=qtbug-12908 bs=16384 count=1 && gzip qtbug-12908 && base64 -w 0 qtbug-12908.gz
// Then change "BMQ" to "BMX"
QString encodedFile("H4sICDdDaUwAA3F0YnVnLTEyOTA4AO3BMXEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAIC3AYbSVKsAQAAA");
QByteArray decodedFile = QByteArray::fromBase64(encodedFile.toAscii());
QCOMPARE(decodedFile.size(), 63);
MiniHttpServer server(header.toAscii() + decodedFile);
server.doClose = true;
QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
QNetworkReplyPtr reply = manager.get(request);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->error(), QNetworkReply::ProtocolFailure);
}
// TODO add similar test for FTP
void tst_QNetworkReply::getFromUnreachableIp()
{
QNetworkAccessManager manager;
QNetworkRequest request(QUrl("http://255.255.255.255/42/23/narf/narf/narf"));
QNetworkReplyPtr reply = manager.get(request);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(reply->error() != QNetworkReply::NoError);
}
void tst_QNetworkReply::qtbug4121unknownAuthentication()
{
MiniHttpServer server(QByteArray("HTTP/1.1 401 bla\r\nWWW-Authenticate: crap\r\nContent-Length: 0\r\n\r\n"));
server.doClose = false;
QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
QNetworkAccessManager manager;
QNetworkReplyPtr reply = manager.get(request);
qRegisterMetaType<QNetworkReply*>("QNetworkReply*");
qRegisterMetaType<QAuthenticator*>("QAuthenticator*");
QSignalSpy authSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
QSignalSpy finishedSpy(&manager, SIGNAL(finished(QNetworkReply*)));
qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
QSignalSpy errorSpy(reply, SIGNAL(error(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(authSpy.count(), 0);
QCOMPARE(finishedSpy.count(), 1);
QCOMPARE(errorSpy.count(), 1);
QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
}
class QtBug13431Helper : public QObject {
Q_OBJECT
public:
QNetworkReply* m_reply;
QTimer m_dlTimer;
public slots:
void replyFinished(QNetworkReply*) {
QTestEventLoop::instance().exitLoop();
}
void onReadAndReschedule() {
const qint64 bytesReceived = m_reply->bytesAvailable();
if (bytesReceived) {
QByteArray data = m_reply->read(bytesReceived);
// reschedule read
const int millisecDelay = static_cast<int>(bytesReceived * 1000 / m_reply->readBufferSize());
m_dlTimer.start(millisecDelay);
}
else {
// reschedule read
m_dlTimer.start(200);
}
}
};
void tst_QNetworkReply::qtbug13431replyThrottling()
{
QtBug13431Helper helper;
QNetworkAccessManager nam;
connect(&nam, SIGNAL(finished(QNetworkReply*)), &helper, SLOT(replyFinished(QNetworkReply*)));
// Download a bigger file
QNetworkRequest netRequest(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/bigfile"));
helper.m_reply = nam.get(netRequest);
// Set the throttle
helper.m_reply->setReadBufferSize(36000);
// Schedule a timer that tries to read
connect(&helper.m_dlTimer, SIGNAL(timeout()), &helper, SLOT(onReadAndReschedule()));
helper.m_dlTimer.setSingleShot(true);
helper.m_dlTimer.start(0);
QTestEventLoop::instance().enterLoop(30);
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(helper.m_reply->isFinished());
QCOMPARE(helper.m_reply->error(), QNetworkReply::NoError);
}
void tst_QNetworkReply::httpWithNoCredentialUsage()
{
QNetworkRequest request(QUrl("http://httptest:httptest@" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi"));
// Do not use credentials
request.setAttribute(QNetworkRequest::AuthenticationReuseAttribute, QNetworkRequest::Manual);
QNetworkAccessManager manager;
QNetworkReplyPtr reply = manager.get(request);
qRegisterMetaType<QNetworkReply*>("QNetworkReply*");
qRegisterMetaType<QAuthenticator*>("QAuthenticator*");
QSignalSpy authSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
QSignalSpy finishedSpy(&manager, SIGNAL(finished(QNetworkReply*)));
qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
QSignalSpy errorSpy(reply, SIGNAL(error(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
// We check if authenticationRequired was emitted, however we do not anything in it so it should be 401
QCOMPARE(authSpy.count(), 1);
QCOMPARE(finishedSpy.count(), 1);
QCOMPARE(errorSpy.count(), 1);
QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
}
void tst_QNetworkReply::qtbug15311doubleContentLength()
{
QByteArray response("HTTP/1.0 200 OK\r\nContent-Length: 3\r\nServer: bogus\r\nContent-Length: 3\r\n\r\nABC");
MiniHttpServer server(response);
server.doClose = true;
QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
QNetworkReplyPtr reply = manager.get(request);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(reply->isFinished());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->size(), qint64(3));
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(3));
QCOMPARE(reply->rawHeader("Content-length"), QByteArray("3, 3"));
QCOMPARE(reply->readAll(), QByteArray("ABC"));
}
void tst_QNetworkReply::qtbug18232gzipContentLengthZero()
{
QByteArray response("HTTP/1.0 200 OK\r\nContent-Encoding: gzip\r\nContent-Length: 0\r\n\r\n");
MiniHttpServer server(response);
server.doClose = true;
QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
QNetworkReplyPtr reply = manager.get(request);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(reply->isFinished());
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->size(), qint64(0));
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(0));
QCOMPARE(reply->readAll(), QByteArray());
}
void tst_QNetworkReply::synchronousRequest_data()
{
QTest::addColumn<QUrl>("url");
QTest::addColumn<QString>("expected");
QTest::addColumn<bool>("checkContentLength");
QTest::addColumn<QString>("mimeType");
// ### cache, auth, proxies
QTest::newRow("http")
<< QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt")
<< QString("file:" SRCDIR "/rfc3252.txt")
<< true
<< QString("text/plain");
QTest::newRow("http-gzip")
<< QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/deflate/rfc3252.txt")
<< QString("file:" SRCDIR "/rfc3252.txt")
<< false // don't check content length, because it's gzip encoded
// ### we would need to enflate (un-deflate) the file content and compare the sizes
<< QString("text/plain");
#ifndef QT_NO_OPENSSL
QTest::newRow("https")
<< QUrl("https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt")
<< QString("file:" SRCDIR "/rfc3252.txt")
<< true
<< QString("text/plain");
#endif
QTest::newRow("data")
<< QUrl(QString::fromLatin1("data:text/plain,hello world"))
<< QString("data:hello world")
<< true // check content length
<< QString("text/plain");
QTest::newRow("simple-file")
<< QUrl::fromLocalFile(SRCDIR "/rfc3252.txt")
<< QString("file:" SRCDIR "/rfc3252.txt")
<< true
<< QString();
}
// FIXME add testcase for failing network etc
void tst_QNetworkReply::synchronousRequest()
{
QFETCH(QUrl, url);
QFETCH(QString, expected);
QFETCH(bool, checkContentLength);
QFETCH(QString, mimeType);
QNetworkRequest request(url);
#ifndef QT_NO_OPENSSL
// workaround for HTTPS requests: add self-signed server cert to list of CA certs,
// since we cannot react to the sslErrors() signal
// to fix this properly we would need to have an ignoreSslErrors() method in the
// QNetworkRequest, see http://bugreports.qt.nokia.com/browse/QTBUG-14774
if (url.scheme() == "https") {
QSslConfiguration sslConf;
QList<QSslCertificate> certs = QSslCertificate::fromPath(SRCDIR "/certs/qt-test-server-cacert.pem");
sslConf.setCaCertificates(certs);
request.setSslConfiguration(sslConf);
}
#endif
request.setAttribute(
QNetworkRequest::SynchronousRequestAttribute,
true);
QNetworkReplyPtr reply;
QSignalSpy finishedSpy(&manager, SIGNAL(finished(QNetworkReply*)));
QSignalSpy sslErrorsSpy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply, 0));
QVERIFY(reply->isFinished());
QCOMPARE(finishedSpy.count(), 0);
QCOMPARE(sslErrorsSpy.count(), 0);
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader).toString(), mimeType);
QByteArray expectedContent;
if (expected.startsWith("file:")) {
QString path = expected.mid(5);
QFile file(path);
file.open(QIODevice::ReadOnly);
expectedContent = file.readAll();
} else if (expected.startsWith("data:")) {
expectedContent = expected.mid(5).toUtf8();
}
if (checkContentLength)
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(expectedContent.size()));
QCOMPARE(reply->readAll(), expectedContent);
reply->deleteLater();
}
#ifndef QT_NO_OPENSSL
void tst_QNetworkReply::synchronousRequestSslFailure()
{
// test that SSL won't be accepted with self-signed certificate,
// and that we do not emit the sslError signal (in the manager that is,
// in the reply we don't care)
QUrl url("https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt");
QNetworkRequest request(url);
request.setAttribute(
QNetworkRequest::SynchronousRequestAttribute,
true);
QNetworkReplyPtr reply;
QSignalSpy sslErrorsSpy(&manager, SIGNAL(sslErrors(QNetworkReply *, const QList<QSslError> &)));
runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply, 0);
QVERIFY(reply->isFinished());
QCOMPARE(reply->error(), QNetworkReply::SslHandshakeFailedError);
QCOMPARE(sslErrorsSpy.count(), 0);
}
#endif
void tst_QNetworkReply::httpAbort()
{
// FIXME: Implement a test that aborts a big HTTP reply
// a) after the first readyRead()
// b) immediatly after the get()
// c) after the finished()
// The goal is no crash and no irrelevant signals after the abort
// FIXME Also implement one where we do a big upload and then abort().
// It must not crash either.
}
void tst_QNetworkReply::dontInsertPartialContentIntoTheCache()
{
QByteArray reply206 =
"HTTP/1.0 206\r\n"
"Connection: keep-alive\r\n"
"Content-Type: text/plain\r\n"
"Cache-control: no-cache\r\n"
"Content-Range: bytes 2-6/8\r\n"
"Content-length: 4\r\n"
"\r\n"
"load";
MiniHttpServer server(reply206);
server.doClose = false;
MySpyMemoryCache *memoryCache = new MySpyMemoryCache(&manager);
manager.setCache(memoryCache);
QUrl url = "http://localhost:" + QString::number(server.serverPort());
QNetworkRequest request(url);
request.setRawHeader("Range", "bytes=2-6");
QNetworkReplyPtr reply = manager.get(request);
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(server.totalConnections > 0);
QCOMPARE(reply->readAll().constData(), "load");
QCOMPARE(memoryCache->m_insertedUrls.count(), 0);
}
// NOTE: This test must be last testcase in tst_qnetworkreply!
void tst_QNetworkReply::parentingRepliesToTheApp()
{
QNetworkRequest request (QUrl("http://" + QtNetworkSettings::serverName()));
manager.get(request)->setParent(this); // parent to this object
manager.get(request)->setParent(qApp); // parent to the app
}
QTEST_MAIN(tst_QNetworkReply)
#include "tst_qnetworkreply.moc"