a4e3b2bd5e
The header field "Cache-Control: must-revalidate" is a strict requirement for loading the resource from the server, and not reading it from the cache without revalidating first. With this patch, PreferCache will load such from the network instead of loading them from the cache, and AlwaysCache will throw a ContentNotFound error. Reviewed-by: Markus Goetz Task-number: QTBUG-18983
6179 lines
230 KiB
C++
6179 lines
230 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);
|
|
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);
|
|
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);
|
|
QNetworkReplyPtr reply = manager.get(QNetworkRequest(url));
|
|
manager.setProxy(QNetworkProxy());
|
|
|
|
// wait for the finished signal
|
|
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
|
|
|
|
QTestEventLoop::instance().enterLoop(1);
|
|
|
|
QVERIFY(!QTestEventLoop::instance().timeout());
|
|
|
|
//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);
|
|
}
|
|
|
|
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"
|