Add unit test for BackgroundRequestAttribute
[profile/ivi/qtbase.git] / tests / auto / network / access / qnetworkreply / tst_qnetworkreply.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42
43 #include <QtTest/QtTest>
44 #include <QtCore/QCryptographicHash>
45 #include <QtCore/QDataStream>
46 #include <QtCore/QUrl>
47 #include <QtCore/QEventLoop>
48 #include <QtCore/QFile>
49 #include <QtCore/QSharedPointer>
50 #include <QtCore/QScopedPointer>
51 #include <QtCore/QTemporaryFile>
52 #include <QtNetwork/QTcpServer>
53 #include <QtNetwork/QTcpSocket>
54 #include <QtNetwork/QLocalSocket>
55 #include <QtNetwork/QLocalServer>
56 #include <QtNetwork/QHostInfo>
57 #include <QtNetwork/QNetworkAccessManager>
58 #include <QtNetwork/QNetworkRequest>
59 #include <QtNetwork/QNetworkReply>
60 #include <QtNetwork/QAbstractNetworkCache>
61 #include <QtNetwork/qauthenticator.h>
62 #include <QtNetwork/qnetworkaccessmanager.h>
63 #include <QtNetwork/qnetworkrequest.h>
64 #include <QtNetwork/qnetworkreply.h>
65 #include <QtNetwork/qnetworkcookie.h>
66 #include <QtNetwork/QNetworkCookieJar>
67 #include <QtNetwork/QHttpPart>
68 #include <QtNetwork/QHttpMultiPart>
69 #include <QtNetwork/QNetworkProxyQuery>
70 #ifndef QT_NO_SSL
71 #include <QtNetwork/qsslerror.h>
72 #include <QtNetwork/qsslconfiguration.h>
73 #endif
74 #ifndef QT_NO_BEARERMANAGEMENT
75 #include <QtNetwork/qnetworkconfigmanager.h>
76 #include <QtNetwork/qnetworkconfiguration.h>
77 #include <QtNetwork/qnetworksession.h>
78 #include <QtNetwork/private/qnetworksession_p.h>
79 #endif
80 #ifdef QT_BUILD_INTERNAL
81 #include <QtNetwork/private/qnetworkaccessmanager_p.h>
82 #endif
83
84 #ifdef Q_OS_UNIX
85 # include <sys/types.h>
86 # include <unistd.h> // for getuid()
87 #endif
88 #include <time.h>
89
90 #include "../../../network-settings.h"
91
92 Q_DECLARE_METATYPE(QSharedPointer<char>)
93 Q_DECLARE_METATYPE(QNetworkReply*)
94 Q_DECLARE_METATYPE(QAuthenticator*)
95 Q_DECLARE_METATYPE(QNetworkProxy)
96 Q_DECLARE_METATYPE(QNetworkProxyQuery)
97 Q_DECLARE_METATYPE(QList<QNetworkProxy>)
98 Q_DECLARE_METATYPE(QNetworkReply::NetworkError)
99 Q_DECLARE_METATYPE(QBuffer*)
100 Q_DECLARE_METATYPE(QHttpMultiPart *)
101 Q_DECLARE_METATYPE(QList<QFile*>) // for multiparts
102 #ifndef QT_NO_SSL
103 Q_DECLARE_METATYPE(QSslConfiguration)
104 #endif
105
106 typedef QSharedPointer<QNetworkReply> QNetworkReplyPtr;
107
108 class MyCookieJar;
109 class tst_QNetworkReply: public QObject
110 {
111     Q_OBJECT
112
113     struct ProxyData {
114         ProxyData(const QNetworkProxy &p, const QByteArray &t, bool auth)
115             : tag(t), proxy(p), requiresAuthentication(auth)
116         { }
117         QByteArray tag;
118         QNetworkProxy proxy;
119         bool requiresAuthentication;
120     };
121
122     static bool seedCreated;
123     static QString createUniqueExtension() {
124         if (!seedCreated) {
125             qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()) + QCoreApplication::applicationPid());
126             seedCreated = true; // not thread-safe, but who cares
127         }
128         QString s = QString("%1-%2-%3").arg(QTime(0,0,0).msecsTo(QTime::currentTime())).arg(QCoreApplication::applicationPid()).arg(qrand());
129         return s;
130     };
131
132     QEventLoop *loop;
133     enum RunSimpleRequestReturn { Timeout = 0, Success, Failure };
134     int returnCode;
135     QString testFileName;
136     QString echoProcessDir;
137 #if !defined Q_OS_WIN
138     QString wronlyFileName;
139 #endif
140     QString uniqueExtension;
141     QList<ProxyData> proxies;
142     QNetworkAccessManager manager;
143     MyCookieJar *cookieJar;
144 #ifndef QT_NO_SSL
145     QSslConfiguration storedSslConfiguration;
146     QList<QSslError> storedExpectedSslErrors;
147 #endif
148 #ifndef QT_NO_BEARERMANAGEMENT
149     QNetworkConfigurationManager *netConfMan;
150     QNetworkConfiguration networkConfiguration;
151     QScopedPointer<QNetworkSession> networkSession;
152 #endif
153
154     using QObject::connect;
155     static bool connect(const QNetworkReplyPtr &ptr, const char *signal, const QObject *receiver, const char *slot, Qt::ConnectionType ct = Qt::AutoConnection)
156     { return connect(ptr.data(), signal, receiver, slot, ct); }
157     bool connect(const QNetworkReplyPtr &ptr, const char *signal, const char *slot, Qt::ConnectionType ct = Qt::AutoConnection)
158     { return connect(ptr.data(), signal, slot, ct); }
159
160 public:
161     tst_QNetworkReply();
162     ~tst_QNetworkReply();
163     QString runSimpleRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request,
164                              QNetworkReplyPtr &reply, const QByteArray &data = QByteArray());
165     QString runMultipartRequest(const QNetworkRequest &request, QNetworkReplyPtr &reply,
166                                     QHttpMultiPart *multiPart, const QByteArray &verb);
167
168     QString runCustomRequest(const QNetworkRequest &request, QNetworkReplyPtr &reply,
169                              const QByteArray &verb, QIODevice *data);
170     int waitForFinish(QNetworkReplyPtr &reply);
171
172 public Q_SLOTS:
173     void finished();
174     void gotError();
175     void authenticationRequired(QNetworkReply*,QAuthenticator*);
176     void proxyAuthenticationRequired(const QNetworkProxy &,QAuthenticator*);
177     void pipeliningHelperSlot();
178
179 #ifndef QT_NO_SSL
180     void sslErrors(QNetworkReply*,const QList<QSslError> &);
181     void storeSslConfiguration();
182     void ignoreSslErrorListSlot(QNetworkReply *reply, const QList<QSslError> &);
183 #endif
184
185 protected Q_SLOTS:
186     void nestedEventLoops_slot();
187
188 private Q_SLOTS:
189     void init();
190     void cleanup();
191     void initTestCase();
192     void cleanupTestCase();
193
194     void stateChecking();
195     void invalidProtocol();
196     void getFromData_data();
197     void getFromData();
198     void getFromFile();
199     void getFromFileSpecial_data();
200     void getFromFileSpecial();
201     void getFromFtp_data();
202     void getFromFtp();
203     void getFromHttp_data();
204     void getFromHttp();
205     void getErrors_data();
206     void getErrors();
207     void headFromHttp_data();
208     void headFromHttp();
209     void putToFile_data();
210     void putToFile();
211     void putToFtp_data();
212     void putToFtp();
213     void putToHttp_data();
214     void putToHttp();
215     void putToHttpSynchronous_data();
216     void putToHttpSynchronous();
217     void putToHttpMultipart_data();
218     void putToHttpMultipart();
219     void postToHttp_data();
220     void postToHttp();
221     void postToHttpSynchronous_data();
222     void postToHttpSynchronous();
223     void postToHttpMultipart_data();
224     void postToHttpMultipart();
225     void deleteFromHttp_data();
226     void deleteFromHttp();
227     void putGetDeleteGetFromHttp_data();
228     void putGetDeleteGetFromHttp();
229     void sendCustomRequestToHttp_data();
230     void sendCustomRequestToHttp();
231     void connectToIPv6Address_data();
232     void connectToIPv6Address();
233
234     void ioGetFromData_data();
235     void ioGetFromData();
236     void ioGetFromFileSpecial_data();
237     void ioGetFromFileSpecial();
238     void ioGetFromFile_data();
239     void ioGetFromFile();
240     void ioGetFromFtp_data();
241     void ioGetFromFtp();
242     void ioGetFromFtpWithReuse();
243     void ioGetFromHttp();
244
245     void ioGetFromBuiltinHttp_data();
246     void ioGetFromBuiltinHttp();
247     void ioGetFromHttpWithReuseParallel();
248     void ioGetFromHttpWithReuseSequential();
249     void ioGetFromHttpWithAuth_data();
250     void ioGetFromHttpWithAuth();
251     void ioGetFromHttpWithAuthSynchronous();
252     void ioGetFromHttpWithProxyAuth();
253     void ioGetFromHttpWithProxyAuthSynchronous();
254     void ioGetFromHttpWithSocksProxy();
255 #ifndef QT_NO_SSL
256     void ioGetFromHttpsWithSslErrors();
257     void ioGetFromHttpsWithIgnoreSslErrors();
258     void ioGetFromHttpsWithSslHandshakeError();
259 #endif
260     void ioGetFromHttpBrokenServer_data();
261     void ioGetFromHttpBrokenServer();
262     void ioGetFromHttpStatus100_data();
263     void ioGetFromHttpStatus100();
264     void ioGetFromHttpNoHeaders_data();
265     void ioGetFromHttpNoHeaders();
266     void ioGetFromHttpWithCache_data();
267     void ioGetFromHttpWithCache();
268
269     void ioGetWithManyProxies_data();
270     void ioGetWithManyProxies();
271
272     void ioPutToFileFromFile_data();
273     void ioPutToFileFromFile();
274     void ioPutToFileFromSocket_data();
275     void ioPutToFileFromSocket();
276     void ioPutToFileFromLocalSocket_data();
277     void ioPutToFileFromLocalSocket();
278 #ifndef QT_NO_PROCESS
279     void ioPutToFileFromProcess_data();
280     void ioPutToFileFromProcess();
281 #endif
282     void ioPutToFtpFromFile_data();
283     void ioPutToFtpFromFile();
284     void ioPutToHttpFromFile_data();
285     void ioPutToHttpFromFile();
286     void ioPostToHttpFromFile_data();
287     void ioPostToHttpFromFile();
288     void ioPostToHttpFromSocket_data();
289     void ioPostToHttpFromSocket();
290     void ioPostToHttpFromSocketSynchronous();
291     void ioPostToHttpFromSocketSynchronous_data();
292     void ioPostToHttpFromMiddleOfFileToEnd();
293     void ioPostToHttpFromMiddleOfFileFiveBytes();
294     void ioPostToHttpFromMiddleOfQBufferFiveBytes();
295     void ioPostToHttpNoBufferFlag();
296     void ioPostToHttpUploadProgress();
297     void ioPostToHttpEmptyUploadProgress();
298
299     void lastModifiedHeaderForFile();
300     void lastModifiedHeaderForHttp();
301
302     void httpCanReadLine();
303
304     void rateControl_data();
305     void rateControl();
306
307     void downloadProgress_data();
308     void downloadProgress();
309     void uploadProgress_data();
310     void uploadProgress();
311
312     void chaining_data();
313     void chaining();
314
315     void receiveCookiesFromHttp_data();
316     void receiveCookiesFromHttp();
317     void receiveCookiesFromHttpSynchronous_data();
318     void receiveCookiesFromHttpSynchronous();
319     void sendCookies_data();
320     void sendCookies();
321     void sendCookiesSynchronous_data();
322     void sendCookiesSynchronous();
323
324     void nestedEventLoops();
325
326     void httpProxyCommands_data();
327     void httpProxyCommands();
328     void httpProxyCommandsSynchronous_data();
329     void httpProxyCommandsSynchronous();
330     void proxyChange();
331     void authorizationError_data();
332     void authorizationError();
333
334     void httpConnectionCount();
335
336     void httpReUsingConnectionSequential_data();
337     void httpReUsingConnectionSequential();
338     void httpReUsingConnectionFromFinishedSlot_data();
339     void httpReUsingConnectionFromFinishedSlot();
340
341     void httpRecursiveCreation();
342
343 #ifndef QT_NO_SSL
344     void ioPostToHttpsUploadProgress();
345     void ignoreSslErrorsList_data();
346     void ignoreSslErrorsList();
347     void ignoreSslErrorsListWithSlot_data();
348     void ignoreSslErrorsListWithSlot();
349     void sslConfiguration_data();
350     void sslConfiguration();
351 #endif
352
353     void getAndThenDeleteObject_data();
354     void getAndThenDeleteObject();
355
356     void symbianOpenCDataUrlCrash();
357
358     void getFromHttpIntoBuffer_data();
359     void getFromHttpIntoBuffer();
360     void getFromHttpIntoBuffer2_data();
361     void getFromHttpIntoBuffer2();
362     void getFromHttpIntoBufferCanReadLine();
363
364     void ioGetFromHttpWithoutContentLength();
365
366     void ioGetFromHttpBrokenChunkedEncoding();
367     void qtbug12908compressedHttpReply();
368     void compressedHttpReplyBrokenGzip();
369
370     void getFromUnreachableIp();
371
372     void qtbug4121unknownAuthentication();
373
374     void qtbug13431replyThrottling();
375
376     void httpWithNoCredentialUsage();
377
378     void qtbug15311doubleContentLength();
379
380     void qtbug18232gzipContentLengthZero();
381     void qtbug22660gzipNoContentLengthEmptyContent();
382
383     void synchronousRequest_data();
384     void synchronousRequest();
385 #ifndef QT_NO_SSL
386     void synchronousRequestSslFailure();
387 #endif
388
389     void httpAbort();
390
391     void dontInsertPartialContentIntoTheCache();
392
393     void httpUserAgent();
394     void authenticationCacheAfterCancel_data();
395     void authenticationCacheAfterCancel();
396     void authenticationWithDifferentRealm();
397     void synchronousAuthenticationCache();
398     void pipelining();
399
400     void closeDuringDownload_data();
401     void closeDuringDownload();
402
403     void ftpAuthentication_data();
404     void ftpAuthentication();
405
406     void backgroundRequest_data();
407     void backgroundRequest();
408
409     // NOTE: This test must be last!
410     void parentingRepliesToTheApp();
411 private:
412     QString testDataDir;
413 };
414
415 bool tst_QNetworkReply::seedCreated = false;
416
417 QT_BEGIN_NAMESPACE
418
419 namespace QTest {
420     template<>
421     char *toString(const QNetworkReply::NetworkError& code)
422     {
423         const QMetaObject *mo = &QNetworkReply::staticMetaObject;
424         int index = mo->indexOfEnumerator("NetworkError");
425         if (index == -1)
426             return qstrdup("");
427
428         QMetaEnum qme = mo->enumerator(index);
429         return qstrdup(qme.valueToKey(code));
430     }
431
432     template<>
433     char *toString(const QNetworkCookie &cookie)
434     {
435         return qstrdup(cookie.toRawForm());
436     }
437
438     template<>
439     char *toString(const QList<QNetworkCookie> &list)
440     {
441         QString result = "QList(";
442         bool first = true;
443         foreach (QNetworkCookie cookie, list) {
444             if (!first)
445                 result += ", ";
446             first = false;
447             result += QString::fromLatin1("QNetworkCookie(%1)").arg(QLatin1String(cookie.toRawForm()));
448         }
449
450         return qstrdup(result.append(')').toLocal8Bit());
451     }
452 }
453
454 QT_END_NAMESPACE
455
456 #define RUN_REQUEST(call)                       \
457     do {                                        \
458         QString errorMsg = call;                \
459         if (!errorMsg.isEmpty())                \
460             QFAIL(qPrintable(errorMsg));        \
461     } while (0);
462
463 #ifndef QT_NO_SSL
464 static void setupSslServer(QSslSocket* serverSocket)
465 {
466     QString testDataDir = QFileInfo(QFINDTESTDATA("rfc3252.txt")).absolutePath();
467     if (testDataDir.isEmpty())
468         testDataDir = QCoreApplication::applicationDirPath();
469
470     serverSocket->setProtocol(QSsl::AnyProtocol);
471     serverSocket->setLocalCertificate(testDataDir + "/certs/server.pem");
472     serverSocket->setPrivateKey(testDataDir + "/certs/server.key");
473 }
474 #endif
475
476 // Does not work for POST/PUT!
477 class MiniHttpServer: public QTcpServer
478 {
479     Q_OBJECT
480 public:
481     QTcpSocket *client; // always the last one that was received
482     QByteArray dataToTransmit;
483     QByteArray receivedData;
484     QSemaphore ready;
485     bool doClose;
486     bool doSsl;
487     bool ipv6;
488     bool multiple;
489     int totalConnections;
490
491     MiniHttpServer(const QByteArray &data, bool ssl = false, QThread *thread = 0, bool useipv6 = false)
492         : client(0), dataToTransmit(data), doClose(true), doSsl(ssl), ipv6(useipv6),
493           multiple(false), totalConnections(0)
494     {
495         if (useipv6) {
496             listen(QHostAddress::AnyIPv6);
497         } else {
498             listen(QHostAddress::AnyIPv4);
499         }
500         if (thread) {
501             connect(thread, SIGNAL(started()), this, SLOT(threadStartedSlot()));
502             moveToThread(thread);
503             thread->start();
504             ready.acquire();
505         }
506     }
507
508 protected:
509     void incomingConnection(qintptr socketDescriptor)
510     {
511         //qDebug() << "incomingConnection" << socketDescriptor << "doSsl:" << doSsl << "ipv6:" << ipv6;
512         if (!doSsl) {
513             client = new QTcpSocket;
514             client->setSocketDescriptor(socketDescriptor);
515             connectSocketSignals();
516         } else {
517 #ifndef QT_NO_SSL
518             QSslSocket *serverSocket = new QSslSocket;
519             serverSocket->setParent(this);
520             if (serverSocket->setSocketDescriptor(socketDescriptor)) {
521                 connect(serverSocket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(slotSslErrors(QList<QSslError>)));
522                 setupSslServer(serverSocket);
523                 serverSocket->startServerEncryption();
524                 client = serverSocket;
525                 connectSocketSignals();
526             } else {
527                 delete serverSocket;
528                 return;
529             }
530 #endif
531         }
532         client->setParent(this);
533         ++totalConnections;
534     }
535
536     virtual void reply() {
537         // we need to emulate the bytesWrittenSlot call if the data is empty.
538         if (dataToTransmit.size() == 0)
539             QMetaObject::invokeMethod(this, "bytesWrittenSlot", Qt::QueuedConnection);
540         else
541             client->write(dataToTransmit);
542     }
543 private:
544     void connectSocketSignals()
545     {
546         //qDebug() << "connectSocketSignals" << client;
547         connect(client, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
548         connect(client, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWrittenSlot()));
549         connect(client, SIGNAL(error(QAbstractSocket::SocketError)),
550                 this, SLOT(slotError(QAbstractSocket::SocketError)));
551     }
552
553 private slots:
554 #ifndef QT_NO_SSL
555     void slotSslErrors(const QList<QSslError>& errors)
556     {
557         qDebug() << "slotSslErrors" << client->errorString() << errors;
558     }
559 #endif
560     void slotError(QAbstractSocket::SocketError err)
561     {
562         qDebug() << "slotError" << err << client->errorString();
563     }
564
565 public slots:
566     void readyReadSlot()
567     {
568         receivedData += client->readAll();
569         int doubleEndlPos = receivedData.indexOf("\r\n\r\n");
570
571         if (doubleEndlPos != -1) {
572             // multiple requests incoming. remove the bytes of the current one
573             if (multiple)
574                 receivedData.remove(0, doubleEndlPos+4);
575
576             reply();
577         }
578     }
579
580     void bytesWrittenSlot() {
581         if (doClose && client->bytesToWrite() == 0) {
582             client->disconnectFromHost();
583             disconnect(client, 0, this, 0);
584         }
585     }
586
587     void threadStartedSlot()
588     {
589         ready.release();
590     }
591 };
592
593 class MyCookieJar: public QNetworkCookieJar
594 {
595 public:
596     inline QList<QNetworkCookie> allCookies() const
597         { return QNetworkCookieJar::allCookies(); }
598     inline void setAllCookies(const QList<QNetworkCookie> &cookieList)
599         { QNetworkCookieJar::setAllCookies(cookieList); }
600 };
601
602 class MyProxyFactory: public QNetworkProxyFactory
603 {
604 public:
605     int callCount;
606     QList<QNetworkProxy> toReturn;
607     QNetworkProxyQuery lastQuery;
608     inline MyProxyFactory() { clear(); }
609
610     inline void clear()
611     {
612         callCount = 0;
613         toReturn = QList<QNetworkProxy>() << QNetworkProxy::DefaultProxy;
614         lastQuery = QNetworkProxyQuery();
615     }
616
617     virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query)
618     {
619         lastQuery = query;
620         ++callCount;
621         return toReturn;
622     }
623 };
624
625 class MyMemoryCache: public QAbstractNetworkCache
626 {
627 public:
628     typedef QPair<QNetworkCacheMetaData, QByteArray> CachedContent;
629     typedef QHash<QByteArray, CachedContent> CacheData;
630     CacheData cache;
631
632     MyMemoryCache(QObject *parent) : QAbstractNetworkCache(parent) {}
633
634     QNetworkCacheMetaData metaData(const QUrl &url)
635     {
636         return cache.value(url.toEncoded()).first;
637     }
638
639     void updateMetaData(const QNetworkCacheMetaData &metaData)
640     {
641         cache[metaData.url().toEncoded()].first = metaData;
642     }
643
644     QIODevice *data(const QUrl &url)
645     {
646         CacheData::ConstIterator it = cache.find(url.toEncoded());
647         if (it == cache.constEnd())
648             return 0;
649         QBuffer *io = new QBuffer(this);
650         io->setData(it->second);
651         io->open(QIODevice::ReadOnly);
652         io->seek(0);
653         return io;
654     }
655
656     bool remove(const QUrl &url)
657     {
658         cache.remove(url.toEncoded());
659         return true;
660     }
661
662     qint64 cacheSize() const
663     {
664         qint64 total = 0;
665         foreach (const CachedContent &entry, cache)
666             total += entry.second.size();
667         return total;
668     }
669
670     QIODevice *prepare(const QNetworkCacheMetaData &)
671     {
672         qFatal("%s: Should not have tried to add to the cache", Q_FUNC_INFO);
673         return 0;
674     }
675     void insert(QIODevice *)
676     {
677         qFatal("%s: Should not have tried to add to the cache", Q_FUNC_INFO);
678     }
679
680     void clear() { cache.clear(); }
681 };
682 Q_DECLARE_METATYPE(MyMemoryCache::CachedContent)
683 Q_DECLARE_METATYPE(MyMemoryCache::CacheData)
684
685 class MySpyMemoryCache: public QAbstractNetworkCache
686 {
687 public:
688     MySpyMemoryCache(QObject *parent) : QAbstractNetworkCache(parent) {}
689     ~MySpyMemoryCache()
690     {
691         qDeleteAll(m_buffers);
692         m_buffers.clear();
693     }
694
695     QHash<QUrl, QIODevice*> m_buffers;
696     QList<QUrl> m_insertedUrls;
697
698     QNetworkCacheMetaData metaData(const QUrl &)
699     {
700         return QNetworkCacheMetaData();
701     }
702
703     void updateMetaData(const QNetworkCacheMetaData &)
704     {
705     }
706
707     QIODevice *data(const QUrl &)
708     {
709         return 0;
710     }
711
712     bool remove(const QUrl &url)
713     {
714         delete m_buffers.take(url);
715         return m_insertedUrls.removeAll(url) > 0;
716     }
717
718     qint64 cacheSize() const
719     {
720         return 0;
721     }
722
723     QIODevice *prepare(const QNetworkCacheMetaData &metaData)
724     {
725         QBuffer* buffer = new QBuffer;
726         buffer->open(QIODevice::ReadWrite);
727         buffer->setProperty("url", metaData.url());
728         m_buffers.insert(metaData.url(), buffer);
729         return buffer;
730     }
731
732     void insert(QIODevice *buffer)
733     {
734         QUrl url = buffer->property("url").toUrl();
735         m_insertedUrls << url;
736         delete m_buffers.take(url);
737     }
738
739     void clear() { m_insertedUrls.clear(); }
740 };
741
742 class DataReader: public QObject
743 {
744     Q_OBJECT
745 public:
746     qint64 totalBytes;
747     QByteArray data;
748     QIODevice *device;
749     bool accumulate;
750     DataReader(const QNetworkReplyPtr &dev, bool acc = true) : totalBytes(0), device(dev.data()), accumulate(acc)
751     { connect(device, SIGNAL(readyRead()), SLOT(doRead()) ); }
752     DataReader(QIODevice *dev, bool acc = true) : totalBytes(0), device(dev), accumulate(acc)
753     {
754         connect(device, SIGNAL(readyRead()), SLOT(doRead()));
755     }
756
757 public slots:
758     void doRead()
759     {
760         QByteArray buffer;
761         buffer.resize(device->bytesAvailable());
762         qint64 bytesRead = device->read(buffer.data(), device->bytesAvailable());
763         if (bytesRead == -1) {
764             QTestEventLoop::instance().exitLoop();
765             return;
766         }
767         buffer.truncate(bytesRead);
768         totalBytes += bytesRead;
769
770         if (accumulate)
771             data += buffer;
772     }
773 };
774
775
776 class SocketPair: public QObject
777 {
778     Q_OBJECT
779 public:
780     QIODevice *endPoints[2];
781
782     SocketPair(QObject *parent = 0)
783         : QObject(parent)
784     {
785         endPoints[0] = endPoints[1] = 0;
786     }
787
788     bool create()
789     {
790         QTcpServer server;
791         server.listen();
792
793         QTcpSocket *active = new QTcpSocket(this);
794         active->connectToHost("127.0.0.1", server.serverPort());
795
796         // need more time as working with embedded
797         // device and testing from emualtor
798         // things tend to get slower
799         if (!active->waitForConnected(1000))
800             return false;
801
802         if (!server.waitForNewConnection(1000))
803             return false;
804
805         QTcpSocket *passive = server.nextPendingConnection();
806         passive->setParent(this);
807
808         endPoints[0] = active;
809         endPoints[1] = passive;
810         return true;
811     }
812 };
813
814 // A blocking tcp server (must be used in a thread) which supports SSL.
815 class BlockingTcpServer : public QTcpServer
816 {
817     Q_OBJECT
818 public:
819     BlockingTcpServer(bool ssl) : doSsl(ssl), sslSocket(0) {}
820
821     QTcpSocket* waitForNextConnectionSocket() {
822         waitForNewConnection(-1);
823         if (doSsl) {
824             if (!sslSocket)
825                 qFatal("%s: sslSocket should not be null after calling waitForNewConnection()",
826                        Q_FUNC_INFO);
827             return sslSocket;
828         } else {
829             //qDebug() << "returning nextPendingConnection";
830             return nextPendingConnection();
831         }
832     }
833     virtual void incomingConnection(qintptr socketDescriptor)
834     {
835 #ifndef QT_NO_SSL
836         if (doSsl) {
837             QSslSocket *serverSocket = new QSslSocket;
838             serverSocket->setParent(this);
839             serverSocket->setSocketDescriptor(socketDescriptor);
840             connect(serverSocket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(slotSslErrors(QList<QSslError>)));
841             setupSslServer(serverSocket);
842             serverSocket->startServerEncryption();
843             sslSocket = serverSocket;
844         } else
845 #endif
846         {
847             QTcpServer::incomingConnection(socketDescriptor);
848         }
849     }
850 private slots:
851
852 #ifndef QT_NO_SSL
853     void slotSslErrors(const QList<QSslError>& errors)
854     {
855         qDebug() << "slotSslErrors" << sslSocket->errorString() << errors;
856     }
857 #endif
858
859 private:
860     const bool doSsl;
861     QTcpSocket* sslSocket;
862 };
863
864 // This server tries to send data as fast as possible (like most servers)
865 // but it measures how fast it was able to send it, which shows at which
866 // rate the reader is processing the data.
867 class FastSender: public QThread
868 {
869     Q_OBJECT
870     QSemaphore ready;
871     qint64 wantedSize;
872     int port;
873     enum Protocol { DebugPipe, ProvidedData };
874     const Protocol protocol;
875     const bool doSsl;
876     const bool fillKernelBuffer;
877 public:
878     int transferRate;
879     QWaitCondition cond;
880
881     QByteArray dataToTransmit;
882     int dataIndex;
883
884     // a server that sends debugpipe data
885     FastSender(qint64 size)
886         : wantedSize(size), port(-1), protocol(DebugPipe),
887           doSsl(false), fillKernelBuffer(true), transferRate(-1),
888           dataIndex(0)
889     {
890         start();
891         ready.acquire();
892     }
893
894     // a server that sends the data provided at construction time, useful for HTTP
895     FastSender(const QByteArray& data, bool https, bool fillBuffer)
896         : wantedSize(data.size()), port(-1), protocol(ProvidedData),
897           doSsl(https), fillKernelBuffer(fillBuffer), transferRate(-1),
898           dataToTransmit(data), dataIndex(0)
899     {
900         start();
901         ready.acquire();
902     }
903
904     inline int serverPort() const { return port; }
905
906     int writeNextData(QTcpSocket* socket, qint32 size)
907     {
908         if (protocol == DebugPipe) {
909             QByteArray data;
910             QDataStream stream(&data, QIODevice::WriteOnly);
911             stream << QVariantMap() << QByteArray(size, 'a');
912             socket->write((char*)&size, sizeof size);
913             socket->write(data);
914             dataIndex += size;
915             return size;
916         } else {
917             const QByteArray data = dataToTransmit.mid(dataIndex, size);
918             socket->write(data);
919             dataIndex += data.size();
920             //qDebug() << "wrote" << dataIndex << "/" << dataToTransmit.size();
921             return data.size();
922         }
923     }
924     void writeLastData(QTcpSocket* socket)
925     {
926         if (protocol == DebugPipe) {
927             QByteArray data;
928             QDataStream stream(&data, QIODevice::WriteOnly);
929             stream << QVariantMap() << QByteArray();
930             const qint32 size = data.size();
931             socket->write((char*)&size, sizeof size);
932             socket->write(data);
933         }
934     }
935
936 protected:
937     void run()
938     {
939         BlockingTcpServer server(doSsl);
940         server.listen();
941         port = server.serverPort();
942         ready.release();
943
944         QTcpSocket *client = server.waitForNextConnectionSocket();
945
946         // get the "request" packet
947         if (!client->waitForReadyRead(2000)) {
948             qDebug() << "FastSender:" << client->error() << "waiting for \"request\" packet";
949             return;
950         }
951         client->readAll();      // we're not interested in the actual contents (e.g. HTTP request)
952
953         enum { BlockSize = 1024 };
954
955         if (fillKernelBuffer) {
956
957             // write a bunch of bytes to fill up the buffers
958             bool done = false;
959             do {
960                 if (writeNextData(client, BlockSize) < BlockSize) {
961                     qDebug() << "ERROR: FastSender: not enough data to write in order to fill buffers; or client is reading too fast";
962                     return;
963                 }
964                 while (client->bytesToWrite() > 0) {
965                     if (!client->waitForBytesWritten(0)) {
966                         done = true;
967                         break;
968                     }
969                 }
970                 //qDebug() << "Filling kernel buffer: wrote" << dataIndex << "bytes";
971             } while (!done);
972
973             qDebug() << "FastSender: ok, kernel buffer is full after writing" << dataIndex << "bytes";
974         }
975
976         // Tell the client to start reading
977         emit dataReady();
978
979         // the kernel buffer is full
980         // clean up QAbstractSocket's residue:
981         while (client->bytesToWrite() > 0) {
982             qDebug() << "Still having" << client->bytesToWrite() << "bytes to write, doing that now";
983             if (!client->waitForBytesWritten(2000)) {
984                 qDebug() << "ERROR: FastSender:" << client->error() << "cleaning up residue";
985                 return;
986             }
987         }
988
989         // now write in "blocking mode", this is where the rate measuring starts
990         QTime timer;
991         timer.start();
992         //const qint64 writtenBefore = dataIndex;
993         //qint64 measuredTotalBytes = wantedSize - writtenBefore;
994         qint64 measuredSentBytes = 0;
995         while (dataIndex < wantedSize) {
996             const int remainingBytes = wantedSize - measuredSentBytes;
997             const int bytesToWrite = qMin(remainingBytes, static_cast<int>(BlockSize));
998             if (bytesToWrite <= 0)
999                 qFatal("%s: attempt to write %d bytes", Q_FUNC_INFO, bytesToWrite);
1000             measuredSentBytes += writeNextData(client, bytesToWrite);
1001
1002             while (client->bytesToWrite() > 0) {
1003                 if (!client->waitForBytesWritten(2000)) {
1004                     qDebug() << "ERROR: FastSender:" << client->error() << "during blocking write";
1005                     return;
1006                 }
1007             }
1008             /*qDebug() << "FastSender:" << bytesToWrite << "bytes written now;"
1009                      << measuredSentBytes << "measured bytes" << measuredSentBytes + writtenBefore << "total ("
1010                      << measuredSentBytes*100/measuredTotalBytes << "% complete);"
1011                      << timer.elapsed() << "ms elapsed";*/
1012         }
1013
1014         transferRate = measuredSentBytes * 1000 / timer.elapsed();
1015         qDebug() << "FastSender: flushed" << measuredSentBytes << "bytes in" << timer.elapsed() << "ms: rate =" << transferRate << "B/s";
1016
1017         // write a "close connection" packet, if the protocol needs it
1018         writeLastData(client);
1019     }
1020 signals:
1021     void dataReady();
1022 };
1023
1024 class RateControlledReader: public QObject
1025 {
1026     Q_OBJECT
1027     QIODevice *device;
1028     int bytesToRead;
1029     int interval;
1030     int readBufferSize;
1031 public:
1032     QByteArray data;
1033     qint64 totalBytesRead;
1034     RateControlledReader(QObject& senderObj, QIODevice *dev, int kbPerSec, int maxBufferSize = 0)
1035         : device(dev), readBufferSize(maxBufferSize), totalBytesRead(0)
1036     {
1037         // determine how often we have to wake up
1038         int timesPerSecond;
1039         if (readBufferSize == 0) {
1040             // The requirement is simply "N KB per seconds"
1041             timesPerSecond = 20;
1042             bytesToRead = kbPerSec * 1024 / timesPerSecond;
1043         } else {
1044             // The requirement also includes "<readBufferSize> bytes at a time"
1045             bytesToRead = readBufferSize;
1046             timesPerSecond = kbPerSec * 1024 / readBufferSize;
1047         }
1048         interval = 1000 / timesPerSecond; // in ms
1049
1050         qDebug() << "RateControlledReader: going to read" << bytesToRead
1051                  << "bytes every" << interval << "ms";
1052         qDebug() << "actual read rate will be"
1053                  << (bytesToRead * 1000 / interval) << "bytes/sec (wanted"
1054                  << kbPerSec * 1024 << "bytes/sec)";
1055
1056         // Wait for data to be readyRead
1057         bool ok = connect(&senderObj, SIGNAL(dataReady()), this, SLOT(slotDataReady()));
1058         if (!ok)
1059             qFatal("%s: Cannot connect dataReady signal", Q_FUNC_INFO);
1060     }
1061
1062     void wrapUp()
1063     {
1064         QByteArray someData = device->read(device->bytesAvailable());
1065         data += someData;
1066         totalBytesRead += someData.size();
1067         qDebug() << "wrapUp: found" << someData.size() << "bytes left. progress" << data.size();
1068         //qDebug() << "wrapUp: now bytesAvailable=" << device->bytesAvailable();
1069     }
1070
1071 private slots:
1072     void slotDataReady()
1073     {
1074         //qDebug() << "RateControlledReader: ready to go";
1075         startTimer(interval);
1076     }
1077
1078 protected:
1079     void timerEvent(QTimerEvent *)
1080     {
1081         //qDebug() << "RateControlledReader: timerEvent bytesAvailable=" << device->bytesAvailable();
1082         if (readBufferSize > 0 && device->bytesAvailable() > readBufferSize) {
1083             // This passes all the time, except in the final flush.
1084             //qFatal("%s: Too many bytes available", Q_FUNC_INFO);
1085         }
1086
1087         qint64 bytesRead = 0;
1088         QTime stopWatch;
1089         stopWatch.start();
1090         do {
1091             if (device->bytesAvailable() == 0) {
1092                 if (stopWatch.elapsed() > 20) {
1093                     qDebug() << "RateControlledReader: Not enough data available for reading, waited too much, timing out";
1094                     break;
1095                 }
1096                 if (!device->waitForReadyRead(5)) {
1097                     qDebug() << "RateControlledReader: Not enough data available for reading, even after waiting 5ms, bailing out";
1098                     break;
1099                 }
1100             }
1101             QByteArray someData = device->read(bytesToRead - bytesRead);
1102             data += someData;
1103             bytesRead += someData.size();
1104             //qDebug() << "RateControlledReader: successfully read" << someData.size() << "progress:" << data.size();
1105         } while (bytesRead < bytesToRead);
1106         totalBytesRead += bytesRead;
1107
1108         if (bytesRead < bytesToRead)
1109             qWarning() << "RateControlledReader: WARNING:" << bytesToRead - bytesRead << "bytes not read";
1110     }
1111 };
1112
1113
1114 tst_QNetworkReply::tst_QNetworkReply()
1115 {
1116     qRegisterMetaType<QNetworkReply *>(); // for QSignalSpy
1117     qRegisterMetaType<QAuthenticator *>();
1118     qRegisterMetaType<QNetworkProxy>();
1119 #ifndef QT_NO_SSL
1120     qRegisterMetaType<QList<QSslError> >();
1121 #endif
1122     qRegisterMetaType<QNetworkReply::NetworkError>();
1123
1124     testFileName = QDir::currentPath() + "/testfile";
1125     uniqueExtension = createUniqueExtension();
1126     cookieJar = new MyCookieJar;
1127     manager.setCookieJar(cookieJar);
1128
1129     QHostInfo hostInfo = QHostInfo::fromName(QtNetworkSettings::serverName());
1130
1131     proxies << ProxyData(QNetworkProxy::NoProxy, "", false);
1132
1133     if (hostInfo.error() == QHostInfo::NoError && !hostInfo.addresses().isEmpty()) {
1134         QString proxyserver = hostInfo.addresses().first().toString();
1135         proxies << ProxyData(QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3128), "+proxy", false)
1136                 << ProxyData(QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3129), "+proxyauth", true)
1137                 // currently unsupported
1138                 // << ProxyData(QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3130), "+proxyauth-ntlm", true);
1139                 << ProxyData(QNetworkProxy(QNetworkProxy::Socks5Proxy, proxyserver, 1080), "+socks", false)
1140                 << ProxyData(QNetworkProxy(QNetworkProxy::Socks5Proxy, proxyserver, 1081), "+socksauth", true);
1141     } else {
1142         printf("==================================================================\n");
1143         printf("Proxy could not be looked up. No proxy will be used while testing!\n");
1144         printf("==================================================================\n");
1145     }
1146 }
1147
1148 tst_QNetworkReply::~tst_QNetworkReply()
1149 {
1150 }
1151
1152
1153 void tst_QNetworkReply::authenticationRequired(QNetworkReply*, QAuthenticator* auth)
1154 {
1155     auth->setUser("httptest");
1156     auth->setPassword("httptest");
1157 }
1158
1159 void tst_QNetworkReply::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator* auth)
1160 {
1161     auth->setUser("qsockstest");
1162     auth->setPassword("password");
1163 }
1164
1165 #ifndef QT_NO_SSL
1166 void tst_QNetworkReply::sslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
1167 {
1168     reply->ignoreSslErrors();
1169     QVERIFY(!errors.isEmpty());
1170     QVERIFY(!reply->sslConfiguration().isNull());
1171 }
1172
1173 void tst_QNetworkReply::storeSslConfiguration()
1174 {
1175     storedSslConfiguration = QSslConfiguration();
1176     QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
1177     if (reply)
1178         storedSslConfiguration = reply->sslConfiguration();
1179 }
1180 #endif
1181
1182 QString tst_QNetworkReply::runMultipartRequest(const QNetworkRequest &request,
1183                                                    QNetworkReplyPtr &reply,
1184                                                    QHttpMultiPart *multiPart,
1185                                                    const QByteArray &verb)
1186 {
1187     if (verb == "POST")
1188         reply.reset(manager.post(request, multiPart));
1189     else
1190         reply.reset(manager.put(request, multiPart));
1191
1192     // the code below is copied from tst_QNetworkReply::runSimpleRequest, see below
1193     reply->setParent(this);
1194     connect(reply, SIGNAL(finished()), SLOT(finished()));
1195     connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(gotError()));
1196     multiPart->setParent(reply.data());
1197
1198     returnCode = Timeout;
1199     loop = new QEventLoop;
1200     QTimer::singleShot(25000, loop, SLOT(quit()));
1201     int code = returnCode == Timeout ? loop->exec() : returnCode;
1202     delete loop;
1203     loop = 0;
1204
1205     switch (code) {
1206     case Failure:
1207         return "Request failed: " + reply->errorString();
1208     case Timeout:
1209         return "Network timeout";
1210     }
1211     return QString();
1212 }
1213
1214 QString tst_QNetworkReply::runSimpleRequest(QNetworkAccessManager::Operation op,
1215                                             const QNetworkRequest &request,
1216                                             QNetworkReplyPtr &reply,
1217                                             const QByteArray &data)
1218 {
1219     switch (op) {
1220     case QNetworkAccessManager::HeadOperation:
1221         reply.reset(manager.head(request));
1222         break;
1223
1224     case QNetworkAccessManager::GetOperation:
1225         reply.reset(manager.get(request));
1226         break;
1227
1228     case QNetworkAccessManager::PutOperation:
1229         reply.reset(manager.put(request, data));
1230         break;
1231
1232     case QNetworkAccessManager::PostOperation:
1233         reply.reset(manager.post(request, data));
1234         break;
1235
1236     case QNetworkAccessManager::DeleteOperation:
1237         reply.reset(manager.deleteResource(request));
1238         break;
1239
1240     default:
1241         qFatal("%s: Invalid/unknown operation requested", Q_FUNC_INFO);
1242     }
1243     reply->setParent(this);
1244
1245     returnCode = Timeout;
1246     int code = Success;
1247
1248     if (request.attribute(QNetworkRequest::SynchronousRequestAttribute).toBool()) {
1249         if (reply->isFinished())
1250             code = reply->error() != QNetworkReply::NoError ? Failure : Success;
1251         else
1252             code = Failure;
1253     } else {
1254         connect(reply, SIGNAL(finished()), SLOT(finished()));
1255         connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(gotError()));
1256
1257         int count = 0;
1258         loop = new QEventLoop;
1259         QSignalSpy spy(reply.data(), SIGNAL(downloadProgress(qint64,qint64)));
1260         while (!reply->isFinished()) {
1261             QTimer::singleShot(20000, loop, SLOT(quit()));
1262             code = loop->exec();
1263             if (count == spy.count() && !reply->isFinished()) {
1264                 code = Timeout;
1265                 break;
1266             }
1267             count = spy.count();
1268         }
1269         delete loop;
1270         loop = 0;
1271     }
1272
1273     switch (code) {
1274     case Failure:
1275         return "Request failed: " + reply->errorString();
1276     case Timeout:
1277         return "Network timeout";
1278     }
1279     return QString();
1280 }
1281
1282 QString tst_QNetworkReply::runCustomRequest(const QNetworkRequest &request,
1283                                             QNetworkReplyPtr &reply,
1284                                             const QByteArray &verb,
1285                                             QIODevice *data)
1286 {
1287     reply.reset(manager.sendCustomRequest(request, verb, data));
1288     reply->setParent(this);
1289     connect(reply, SIGNAL(finished()), SLOT(finished()));
1290     connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(gotError()));
1291
1292     returnCode = Timeout;
1293     loop = new QEventLoop;
1294     QTimer::singleShot(20000, loop, SLOT(quit()));
1295     int code = returnCode == Timeout ? loop->exec() : returnCode;
1296     delete loop;
1297     loop = 0;
1298
1299     switch (code) {
1300     case Failure:
1301         return "Request failed: " + reply->errorString();
1302     case Timeout:
1303         return "Network timeout";
1304     }
1305     return QString();
1306 }
1307
1308 int tst_QNetworkReply::waitForFinish(QNetworkReplyPtr &reply)
1309 {
1310     int count = 0;
1311
1312     connect(reply, SIGNAL(finished()), SLOT(finished()));
1313     connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(gotError()));
1314     returnCode = Success;
1315     loop = new QEventLoop;
1316     QSignalSpy spy(reply.data(), SIGNAL(downloadProgress(qint64,qint64)));
1317     while (!reply->isFinished()) {
1318         QTimer::singleShot(5000, loop, SLOT(quit()));
1319         if ( loop->exec() == Timeout && count == spy.count() && !reply->isFinished()) {
1320             returnCode = Timeout;
1321             break;
1322         }
1323         count = spy.count();
1324     }
1325     delete loop;
1326     loop = 0;
1327
1328     return returnCode;
1329 }
1330
1331 void tst_QNetworkReply::finished()
1332 {
1333     loop->exit(returnCode = Success);
1334 }
1335
1336 void tst_QNetworkReply::gotError()
1337 {
1338     loop->exit(returnCode = Failure);
1339     disconnect(QObject::sender(), SIGNAL(finished()), this, 0);
1340 }
1341
1342 void tst_QNetworkReply::initTestCase()
1343 {
1344     testDataDir = QFileInfo(QFINDTESTDATA("rfc3252.txt")).absolutePath();
1345     if (testDataDir.isEmpty())
1346         testDataDir = QCoreApplication::applicationDirPath();
1347
1348     QVERIFY(QtNetworkSettings::verifyTestNetworkSettings());
1349 #if !defined Q_OS_WIN
1350     wronlyFileName = testDataDir + "/write-only";
1351     QFile wr(wronlyFileName);
1352     QVERIFY(wr.open(QIODevice::WriteOnly | QIODevice::Truncate));
1353     wr.setPermissions(QFile::WriteOwner | QFile::WriteUser);
1354     wr.close();
1355 #endif
1356
1357     QDir::setSearchPaths("testdata", QStringList() << testDataDir);
1358 #ifndef QT_NO_SSL
1359     QSslSocket::defaultCaCertificates(); //preload certificates
1360 #endif
1361 #ifndef QT_NO_BEARERMANAGEMENT
1362     netConfMan = new QNetworkConfigurationManager(this);
1363     networkConfiguration = netConfMan->defaultConfiguration();
1364     networkSession.reset(new QNetworkSession(networkConfiguration));
1365     if (!networkSession->isOpen()) {
1366         networkSession->open();
1367         QVERIFY(networkSession->waitForOpened(30000));
1368     }
1369 #endif
1370
1371     echoProcessDir = QFINDTESTDATA("echo");
1372     QVERIFY2(!echoProcessDir.isEmpty(), qPrintable(
1373         QString::fromLatin1("Couldn't find echo dir starting from %1.").arg(QDir::currentPath())));
1374 }
1375
1376 void tst_QNetworkReply::cleanupTestCase()
1377 {
1378 #if !defined Q_OS_WIN
1379     QFile::remove(wronlyFileName);
1380 #endif
1381 #ifndef QT_NO_BEARERMANAGEMENT
1382     if (networkSession && networkSession->isOpen()) {
1383         networkSession->close();
1384     }
1385 #endif
1386 }
1387
1388 void tst_QNetworkReply::init()
1389 {
1390     cleanup();
1391 }
1392
1393 void tst_QNetworkReply::cleanup()
1394 {
1395     QFile file(testFileName);
1396     QVERIFY(!file.exists() || file.remove());
1397
1398     // clear the internal cache
1399     manager.clearAccessCache();
1400     manager.setProxy(QNetworkProxy());
1401     manager.setCache(0);
1402
1403     // clear cookies
1404     cookieJar->setAllCookies(QList<QNetworkCookie>());
1405 }
1406
1407 void tst_QNetworkReply::stateChecking()
1408 {
1409     QUrl url = QUrl("file:///");
1410     QNetworkRequest req(url);   // you can't open this file, I know
1411     QNetworkReplyPtr reply(manager.get(req));
1412
1413     QVERIFY(reply.data());
1414     QVERIFY(reply->isOpen());
1415     QVERIFY(reply->isReadable());
1416     QVERIFY(!reply->isWritable());
1417
1418     // both behaviours are OK since we might change underlying behaviour again
1419     if (!reply->isFinished())
1420         QCOMPARE(reply->errorString(), QString("Unknown error"));
1421     else
1422         QVERIFY(!reply->errorString().isEmpty());
1423
1424
1425     QCOMPARE(reply->manager(), &manager);
1426     QCOMPARE(reply->request(), req);
1427     QCOMPARE(int(reply->operation()), int(QNetworkAccessManager::GetOperation));
1428     // error and not error are OK since we might change underlying behaviour again
1429     if (!reply->isFinished())
1430         QCOMPARE(reply->error(), QNetworkReply::NoError);
1431     QCOMPARE(reply->url(), url);
1432
1433     reply->abort();
1434 }
1435
1436 void tst_QNetworkReply::invalidProtocol()
1437 {
1438     QUrl url = QUrl::fromEncoded("not-a-known-protocol://foo/bar");
1439     QNetworkRequest req(url);
1440     QNetworkReplyPtr reply;
1441
1442     QString errorMsg = "Request failed: Protocol \"not-a-known-protocol\" is unknown";
1443     QString result = runSimpleRequest(QNetworkAccessManager::GetOperation, req, reply);
1444     QCOMPARE(result, errorMsg);
1445
1446     QCOMPARE(reply->url(), url);
1447     QCOMPARE(reply->error(), QNetworkReply::ProtocolUnknownError);
1448 }
1449
1450 void tst_QNetworkReply::getFromData_data()
1451 {
1452     QTest::addColumn<QString>("request");
1453     QTest::addColumn<QByteArray>("expected");
1454     QTest::addColumn<QString>("mimeType");
1455
1456     const QString defaultMimeType("text/plain;charset=US-ASCII");
1457
1458     //QTest::newRow("empty") << "data:" << QByteArray() << defaultMimeType;
1459     QTest::newRow("empty2") << "data:," << QByteArray() << defaultMimeType;
1460     QTest::newRow("just-charset_1") << "data:charset=iso-8859-1,"
1461                                     << QByteArray() << "text/plain;charset=iso-8859-1";
1462     QTest::newRow("just-charset_2") << "data:charset = iso-8859-1 ,"
1463                                     << QByteArray() << "text/plain;charset = iso-8859-1";
1464     //QTest::newRow("just-media") << "data:text/xml" << QByteArray() << "text/xml";
1465     QTest::newRow("just-media2") << "data:text/xml," << QByteArray() << "text/xml";
1466
1467     QTest::newRow("plain_1") << "data:,foo" << QByteArray("foo") << defaultMimeType;
1468     QTest::newRow("plain_2") << "data:text/html,Hello World" << QByteArray("Hello World")
1469                              << "text/html";
1470     QTest::newRow("plain_3") << "data:text/html;charset=utf-8,Hello World"
1471                              << QByteArray("Hello World") << "text/html;charset=utf-8";
1472
1473     QTest::newRow("pct_1") << "data:,%3Cbody%20contentEditable%3Dtrue%3E%0D%0A"
1474                            << QByteArray("<body contentEditable=true>\r\n") << defaultMimeType;
1475     QTest::newRow("pct_2") << "data:text/html;charset=utf-8,%3Cbody%20contentEditable%3Dtrue%3E%0D%0A"
1476                            << QByteArray("<body contentEditable=true>\r\n")
1477                            << "text/html;charset=utf-8";
1478
1479     QTest::newRow("base64-empty_1") << "data:;base64," << QByteArray() << defaultMimeType;
1480     QTest::newRow("base64-empty_2") << "data:charset=utf-8;base64," << QByteArray()
1481                                     << "text/plain;charset=utf-8";
1482     QTest::newRow("base64-empty_3") << "data:text/html;charset=utf-8;base64,"
1483                                     << QByteArray() << "text/html;charset=utf-8";
1484
1485     QTest::newRow("base64_1") << "data:;base64,UXQgaXMgZ3JlYXQh" << QByteArray("Qt is great!")
1486                               << defaultMimeType;
1487     QTest::newRow("base64_2") << "data:charset=utf-8;base64,UXQgaXMgZ3JlYXQh"
1488                               << QByteArray("Qt is great!") << "text/plain;charset=utf-8";
1489     QTest::newRow("base64_3") << "data:text/html;charset=utf-8;base64,UXQgaXMgZ3JlYXQh"
1490                               << QByteArray("Qt is great!") << "text/html;charset=utf-8";
1491
1492     QTest::newRow("pct-nul") << "data:,a%00g" << QByteArray("a\0g", 3) << defaultMimeType;
1493     QTest::newRow("base64-nul") << "data:;base64,YQBn" << QByteArray("a\0g", 3) << defaultMimeType;
1494     QTest::newRow("pct-nonutf8") << "data:,a%E1g" << QByteArray("a\xE1g", 3) << defaultMimeType;
1495
1496     QTest::newRow("base64")
1497         << QString::fromLatin1("data:application/xml;base64,PGUvPg==")
1498         << QByteArray("<e/>")
1499         << "application/xml";
1500
1501     QTest::newRow("base64, no media type")
1502         << QString::fromLatin1("data:;base64,PGUvPg==")
1503         << QByteArray("<e/>")
1504         << defaultMimeType;
1505
1506     QTest::newRow("Percent encoding")
1507         << QString::fromLatin1("data:application/xml,%3Ce%2F%3E")
1508         << QByteArray("<e/>")
1509         << "application/xml";
1510
1511     QTest::newRow("Percent encoding, no media type")
1512         << QString::fromLatin1("data:,%3Ce%2F%3E")
1513         << QByteArray("<e/>")
1514         << defaultMimeType;
1515
1516     QTest::newRow("querychars")
1517         << QString::fromLatin1("data:,foo?x=0&y=0")
1518         << QByteArray("foo?x=0&y=0")
1519         << defaultMimeType;
1520
1521     QTest::newRow("css") << "data:text/css,div%20{%20border-right:%20solid;%20}"
1522                          << QByteArray("div { border-right: solid; }")
1523                          << "text/css";
1524 }
1525
1526 void tst_QNetworkReply::getFromData()
1527 {
1528     QFETCH(QString, request);
1529     QFETCH(QByteArray, expected);
1530     QFETCH(QString, mimeType);
1531
1532     QUrl url = QUrl::fromEncoded(request.toLatin1());
1533     QNetworkRequest req(url);
1534     QNetworkReplyPtr reply;
1535
1536     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, req, reply));
1537
1538     QCOMPARE(reply->url(), url);
1539     QCOMPARE(reply->error(), QNetworkReply::NoError);
1540
1541     QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader).toString(), mimeType);
1542     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(expected.size()));
1543     QCOMPARE(reply->readAll(), expected);
1544 }
1545
1546 void tst_QNetworkReply::getFromFile()
1547 {
1548     // create the file:
1549     QTemporaryFile file(QDir::currentPath() + "/temp-XXXXXX");
1550     file.setAutoRemove(true);
1551     QVERIFY(file.open());
1552
1553     QNetworkRequest request(QUrl::fromLocalFile(file.fileName()));
1554     QNetworkReplyPtr reply;
1555
1556     static const char fileData[] = "This is some data that is in the file.\r\n";
1557     QByteArray data = QByteArray::fromRawData(fileData, sizeof fileData - 1);
1558     QVERIFY(file.write(data) == data.size());
1559     file.flush();
1560     QCOMPARE(file.size(), qint64(data.size()));
1561
1562     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
1563
1564     QCOMPARE(reply->url(), request.url());
1565     QCOMPARE(reply->error(), QNetworkReply::NoError);
1566
1567     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), file.size());
1568     QCOMPARE(reply->readAll(), data);
1569
1570     // make the file bigger
1571     file.resize(0);
1572     const int multiply = (128 * 1024) / (sizeof fileData - 1);
1573     for (int i = 0; i < multiply; ++i)
1574         file.write(fileData, sizeof fileData - 1);
1575     file.flush();
1576
1577     // run again
1578     reply.clear();
1579
1580     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
1581     QCOMPARE(reply->url(), request.url());
1582     QCOMPARE(reply->error(), QNetworkReply::NoError);
1583
1584     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), file.size());
1585     QCOMPARE(qint64(reply->readAll().size()), file.size());
1586 }
1587
1588 void tst_QNetworkReply::getFromFileSpecial_data()
1589 {
1590     QTest::addColumn<QString>("fileName");
1591     QTest::addColumn<QString>("url");
1592
1593     QTest::newRow("resource") << ":/resource" <<  "qrc:/resource";
1594     QTest::newRow("search-path") << "testdata:/rfc3252.txt" << "testdata:/rfc3252.txt";
1595     QTest::newRow("bigfile-path") << "testdata:/bigfile" << "testdata:/bigfile";
1596 #ifdef Q_OS_WIN
1597     QTest::newRow("smb-path") << "testdata:/smb-file.txt" << "file://" + QtNetworkSettings::winServerName() + "/testshare/test.pri";
1598 #endif
1599 }
1600
1601 void tst_QNetworkReply::getFromFileSpecial()
1602 {
1603     QFETCH(QString, fileName);
1604     QFETCH(QString, url);
1605
1606     // open the resource so we can find out its size
1607     QFile resource(fileName);
1608     QVERIFY(resource.open(QIODevice::ReadOnly));
1609
1610     QNetworkRequest request;
1611     QNetworkReplyPtr reply;
1612     request.setUrl(url);
1613     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
1614
1615     QCOMPARE(reply->url(), request.url());
1616     QCOMPARE(reply->error(), QNetworkReply::NoError);
1617
1618     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), resource.size());
1619     QCOMPARE(reply->readAll(), resource.readAll());
1620 }
1621
1622 void tst_QNetworkReply::getFromFtp_data()
1623 {
1624     QTest::addColumn<QString>("referenceName");
1625     QTest::addColumn<QString>("url");
1626
1627     QTest::newRow("rfc3252.txt") << (testDataDir + "/rfc3252.txt") << "ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt";
1628     QTest::newRow("bigfile") << (testDataDir + "/bigfile") << "ftp://" + QtNetworkSettings::serverName() + "/qtest/bigfile";
1629 }
1630
1631 void tst_QNetworkReply::getFromFtp()
1632 {
1633     QFETCH(QString, referenceName);
1634     QFETCH(QString, url);
1635
1636     QFile reference(referenceName);
1637     QVERIFY(reference.open(QIODevice::ReadOnly));
1638
1639     QNetworkRequest request(url);
1640     QNetworkReplyPtr reply;
1641     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
1642
1643     QCOMPARE(reply->url(), request.url());
1644     QCOMPARE(reply->error(), QNetworkReply::NoError);
1645
1646     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
1647     QCOMPARE(reply->readAll(), reference.readAll());
1648 }
1649
1650 void tst_QNetworkReply::getFromHttp_data()
1651 {
1652     QTest::addColumn<QString>("referenceName");
1653     QTest::addColumn<QString>("url");
1654
1655     QTest::newRow("success-internal") << (testDataDir + "/rfc3252.txt") << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt";
1656     QTest::newRow("success-external") << (testDataDir + "/rfc3252.txt") << "http://www.ietf.org/rfc/rfc3252.txt";
1657     QTest::newRow("bigfile-internal") << (testDataDir + "/bigfile") << "http://" + QtNetworkSettings::serverName() + "/qtest/bigfile";
1658 }
1659
1660 void tst_QNetworkReply::getFromHttp()
1661 {
1662     QFETCH(QString, referenceName);
1663     QFETCH(QString, url);
1664
1665     QFile reference(referenceName);
1666     QVERIFY(reference.open(QIODevice::ReadOnly));
1667
1668     QNetworkRequest request(url);
1669     QNetworkReplyPtr reply;
1670     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
1671
1672     QCOMPARE(reply->url(), request.url());
1673     QCOMPARE(reply->error(), QNetworkReply::NoError);
1674     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
1675     QCOMPARE(reply->size(), reference.size());
1676     // only compare when the header is set.
1677     if (reply->header(QNetworkRequest::ContentLengthHeader).isValid())
1678         QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
1679
1680     // We know our internal server is apache..
1681     if (qstrcmp(QTest::currentDataTag(), "success-internal") == 0)
1682         QVERIFY(reply->header(QNetworkRequest::ServerHeader).toString().contains("Apache"));
1683
1684     QCOMPARE(reply->readAll(), reference.readAll());
1685 }
1686
1687 void tst_QNetworkReply::headFromHttp_data()
1688 {
1689     QTest::addColumn<qint64>("referenceSize");
1690     QTest::addColumn<QUrl>("url");
1691     QTest::addColumn<QString>("contentType");
1692     QTest::addColumn<QNetworkProxy>("proxy");
1693
1694     qint64 rfcsize = QFileInfo(testDataDir + "/rfc3252.txt").size();
1695     qint64 bigfilesize = QFileInfo(testDataDir + "/bigfile").size();
1696     qint64 indexsize = QFileInfo(testDataDir + "/index.html").size();
1697
1698     //testing proxies, mainly for the 407 response from http proxy
1699     for (int i = 0; i < proxies.count(); ++i) {
1700         QTest::newRow("rfc" + proxies.at(i).tag) << rfcsize << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt") << "text/plain" << proxies.at(i).proxy;
1701         QTest::newRow("bigfile" + proxies.at(i).tag) << bigfilesize << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/bigfile") << "text/plain" << proxies.at(i).proxy;
1702         QTest::newRow("index" + proxies.at(i).tag) << indexsize << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/") << "text/html" << proxies.at(i).proxy;
1703         QTest::newRow("with-authentication" + proxies.at(i).tag) << rfcsize << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth/rfc3252.txt") << "text/plain" << proxies.at(i).proxy;
1704         QTest::newRow("cgi" + proxies.at(i).tag) << (qint64)-1 << QUrl("http://qt-test-server/qtest/cgi-bin/httpcachetest_expires500.cgi") << "text/html" << proxies.at(i).proxy;
1705     }
1706 }
1707
1708 void tst_QNetworkReply::headFromHttp()
1709 {
1710     QFETCH(qint64, referenceSize);
1711     QFETCH(QUrl, url);
1712     QFETCH(QString, contentType);
1713     QFETCH(QNetworkProxy, proxy);
1714
1715     QNetworkRequest request(url);
1716     QNetworkReplyPtr reply;
1717
1718     QElapsedTimer time;
1719     time.start();
1720
1721     manager.setProxy(proxy);
1722     connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
1723             SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
1724     connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
1725             SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
1726
1727     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::HeadOperation, request, reply));
1728
1729     manager.disconnect(SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
1730                this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
1731     manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
1732                this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
1733
1734     QVERIFY(time.elapsed() < 8000); //check authentication didn't wait for the server to timeout the http connection (15s on qt test server)
1735
1736     QCOMPARE(reply->url(), request.url());
1737     QCOMPARE(reply->error(), QNetworkReply::NoError);
1738     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
1739     // only compare when the header is set.
1740     if (reply->header(QNetworkRequest::ContentLengthHeader).isValid() && referenceSize >= 0)
1741         QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), referenceSize);
1742     if (reply->header(QNetworkRequest::ContentTypeHeader).isValid())
1743         QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader).toString(), contentType);
1744 }
1745
1746 void tst_QNetworkReply::getErrors_data()
1747 {
1748     QTest::addColumn<QString>("url");
1749     QTest::addColumn<int>("error");
1750     QTest::addColumn<int>("httpStatusCode");
1751     QTest::addColumn<bool>("dataIsEmpty");
1752
1753     // empties
1754     QTest::newRow("empty-url") << QString() << int(QNetworkReply::ProtocolUnknownError) << 0 << true;
1755     QTest::newRow("empty-scheme-host") << (testDataDir + "/rfc3252.txt") << int(QNetworkReply::ProtocolUnknownError) << 0 << true;
1756     QTest::newRow("empty-scheme") << "//" + QtNetworkSettings::winServerName() + "/testshare/test.pri"
1757             << int(QNetworkReply::ProtocolUnknownError) << 0 << true;
1758
1759     // file: errors
1760     QTest::newRow("file-host") << "file://this-host-doesnt-exist.troll.no/foo.txt"
1761 #if !defined Q_OS_WIN
1762                                << int(QNetworkReply::ProtocolInvalidOperationError) << 0 << true;
1763 #else
1764                                << int(QNetworkReply::ContentNotFoundError) << 0 << true;
1765 #endif
1766     QTest::newRow("file-no-path") << "file://localhost"
1767                                   << int(QNetworkReply::ContentOperationNotPermittedError) << 0 << true;
1768     QTest::newRow("file-is-dir") << QUrl::fromLocalFile(QDir::currentPath()).toString()
1769                                  << int(QNetworkReply::ContentOperationNotPermittedError) << 0 << true;
1770     QTest::newRow("file-exist") << QUrl::fromLocalFile(QDir::currentPath() + "/this-file-doesnt-exist.txt").toString()
1771                                 << int(QNetworkReply::ContentNotFoundError) << 0 << true;
1772 #if !defined Q_OS_WIN
1773     QTest::newRow("file-is-wronly") << QUrl::fromLocalFile(wronlyFileName).toString()
1774                                     << int(QNetworkReply::ContentAccessDenied) << 0 << true;
1775 #endif
1776     if (QFile::exists("/etc/shadow"))
1777         QTest::newRow("file-permissions") << "file:/etc/shadow"
1778                                           << int(QNetworkReply::ContentAccessDenied) << 0 << true;
1779
1780     // ftp: errors
1781     QTest::newRow("ftp-host") << "ftp://this-host-doesnt-exist.troll.no/foo.txt"
1782                               << int(QNetworkReply::HostNotFoundError) << 0 << true;
1783     QTest::newRow("ftp-no-path") << "ftp://" + QtNetworkSettings::serverName()
1784                                  << int(QNetworkReply::ContentOperationNotPermittedError) << 0 << true;
1785     QTest::newRow("ftp-is-dir") << "ftp://" + QtNetworkSettings::serverName() + "/qtest"
1786                                 << int(QNetworkReply::ContentOperationNotPermittedError) << 0 << true;
1787     QTest::newRow("ftp-dir-not-readable") << "ftp://" + QtNetworkSettings::serverName() + "/pub/dir-not-readable/foo.txt"
1788                                           << int(QNetworkReply::ContentAccessDenied) << 0 << true;
1789     QTest::newRow("ftp-file-not-readable") << "ftp://" + QtNetworkSettings::serverName() + "/pub/file-not-readable.txt"
1790                                            << int(QNetworkReply::ContentAccessDenied) << 0 << true;
1791     QTest::newRow("ftp-exist") << "ftp://" + QtNetworkSettings::serverName() + "/pub/this-file-doesnt-exist.txt"
1792                                << int(QNetworkReply::ContentNotFoundError) << 0 << true;
1793
1794     // http: errors
1795     QTest::newRow("http-host") << "http://this-host-will-never-exist.troll.no/"
1796                                << int(QNetworkReply::HostNotFoundError) << 0 << true;
1797     QTest::newRow("http-exist") << "http://" + QtNetworkSettings::serverName() + "/this-file-doesnt-exist.txt"
1798                                 << int(QNetworkReply::ContentNotFoundError) << 404 << false;
1799     QTest::newRow("http-authentication") << "http://" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth"
1800                                          << int(QNetworkReply::AuthenticationRequiredError) << 401 << false;
1801 }
1802
1803 void tst_QNetworkReply::getErrors()
1804 {
1805     QFETCH(QString, url);
1806     QNetworkRequest request(url);
1807
1808 #ifdef Q_OS_UNIX
1809     if ((qstrcmp(QTest::currentDataTag(), "file-is-wronly") == 0) ||
1810         (qstrcmp(QTest::currentDataTag(), "file-permissions") == 0)) {
1811         if (::getuid() == 0)
1812             QSKIP("Running this test as root doesn't make sense");
1813     }
1814 #endif
1815
1816     QNetworkReplyPtr reply(manager.get(request));
1817     reply->setParent(this);     // we have expect-fails
1818
1819     if (!reply->isFinished())
1820         QCOMPARE(reply->error(), QNetworkReply::NoError);
1821
1822     // now run the request:
1823     QVERIFY(waitForFinish(reply) != Timeout);
1824
1825     QFETCH(int, error);
1826     QEXPECT_FAIL("ftp-is-dir", "QFtp cannot provide enough detail", Abort);
1827     // the line below is not necessary
1828     QEXPECT_FAIL("ftp-dir-not-readable", "QFtp cannot provide enough detail", Abort);
1829     QCOMPARE(reply->error(), QNetworkReply::NetworkError(error));
1830
1831     QTEST(reply->readAll().isEmpty(), "dataIsEmpty");
1832
1833     QVERIFY(reply->isFinished());
1834     QVERIFY(!reply->isRunning());
1835
1836     QFETCH(int, httpStatusCode);
1837     if (httpStatusCode != 0) {
1838         QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), httpStatusCode);
1839     }
1840 }
1841
1842 static inline QByteArray md5sum(const QByteArray &data)
1843 {
1844     return QCryptographicHash::hash(data, QCryptographicHash::Md5);
1845 }
1846
1847 void tst_QNetworkReply::putToFile_data()
1848 {
1849     QTest::addColumn<QByteArray>("data");
1850     QTest::addColumn<QByteArray>("md5sum");
1851
1852     QByteArray data;
1853     data = "";
1854     QTest::newRow("empty") << data << md5sum(data);
1855
1856     data = "This is a normal message.";
1857     QTest::newRow("generic") << data << md5sum(data);
1858
1859     data = "This is a message to show that Qt rocks!\r\n\n";
1860     QTest::newRow("small") << data << md5sum(data);
1861
1862     data = QByteArray("abcd\0\1\2\abcd",12);
1863     QTest::newRow("with-nul") << data << md5sum(data);
1864
1865     data = QByteArray(4097, '\4');
1866     QTest::newRow("4k+1") << data << md5sum(data);
1867
1868     data = QByteArray(128*1024+1, '\177');
1869     QTest::newRow("128k+1") << data << md5sum(data);
1870
1871     data = QByteArray(2*1024*1024+1, '\177');
1872     QTest::newRow("2MB+1") << data << md5sum(data);
1873 }
1874
1875 void tst_QNetworkReply::putToFile()
1876 {
1877     QFile file(testFileName);
1878
1879     QUrl url = QUrl::fromLocalFile(file.fileName());
1880     QNetworkRequest request(url);
1881     QNetworkReplyPtr reply;
1882
1883     QFETCH(QByteArray, data);
1884
1885     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data));
1886
1887     QCOMPARE(reply->url(), url);
1888     QCOMPARE(reply->error(), QNetworkReply::NoError);
1889     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
1890     QVERIFY(reply->readAll().isEmpty());
1891
1892     QVERIFY(file.open(QIODevice::ReadOnly));
1893     QCOMPARE(file.size(), qint64(data.size()));
1894     QByteArray contents = file.readAll();
1895     QCOMPARE(contents, data);
1896 }
1897
1898 void tst_QNetworkReply::putToFtp_data()
1899 {
1900     putToFile_data();
1901 }
1902
1903 void tst_QNetworkReply::putToFtp()
1904 {
1905     QUrl url("ftp://" + QtNetworkSettings::serverName());
1906     url.setPath(QString("/qtest/upload/qnetworkaccess-putToFtp-%1-%2")
1907                 .arg(QTest::currentDataTag())
1908                 .arg(uniqueExtension));
1909
1910     QNetworkRequest request(url);
1911     QNetworkReplyPtr reply;
1912
1913     QFETCH(QByteArray, data);
1914
1915     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data));
1916
1917     QCOMPARE(reply->url(), url);
1918     QCOMPARE(reply->error(), QNetworkReply::NoError);
1919     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
1920     QVERIFY(reply->readAll().isEmpty());
1921
1922     // download the file again from FTP to make sure it was uploaded
1923     // correctly
1924     QNetworkAccessManager qnam;
1925     QNetworkRequest req(url);
1926     QNetworkReply *r = qnam.get(req);
1927
1928     QObject::connect(r, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
1929     int count = 0;
1930     QSignalSpy spy(r, SIGNAL(downloadProgress(qint64,qint64)));
1931     while (!r->isFinished()) {
1932         QTestEventLoop::instance().enterLoop(10);
1933         if (count == spy.count() && !r->isFinished())
1934             break;
1935         count = spy.count();
1936     }
1937     QObject::disconnect(r, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
1938
1939     QByteArray uploaded = r->readAll();
1940     QCOMPARE(uploaded.size(), data.size());
1941     QCOMPARE(uploaded, data);
1942
1943     r->close();
1944     QObject::connect(r, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
1945     QTestEventLoop::instance().enterLoop(10);
1946     QObject::disconnect(r, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
1947 }
1948
1949 void tst_QNetworkReply::putToHttp_data()
1950 {
1951     putToFile_data();
1952 }
1953
1954 void tst_QNetworkReply::putToHttp()
1955 {
1956     QUrl url("http://" + QtNetworkSettings::serverName());
1957     url.setPath(QString("/dav/qnetworkaccess-putToHttp-%1-%2")
1958                 .arg(QTest::currentDataTag())
1959                 .arg(uniqueExtension));
1960
1961     QNetworkRequest request(url);
1962     QNetworkReplyPtr reply;
1963
1964     QFETCH(QByteArray, data);
1965
1966     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data));
1967
1968     QCOMPARE(reply->url(), url);
1969     QCOMPARE(reply->error(), QNetworkReply::NoError);
1970
1971     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 201); // 201 Created
1972
1973     // download the file again from HTTP to make sure it was uploaded
1974     // correctly. HTTP/0.9 is enough
1975     QTcpSocket socket;
1976     socket.connectToHost(QtNetworkSettings::serverName(), 80);
1977     socket.write("GET " + url.toEncoded(QUrl::RemoveScheme | QUrl::RemoveAuthority) + "\r\n");
1978     if (!socket.waitForDisconnected(10000))
1979         QFAIL("Network timeout");
1980
1981     QByteArray uploadedData = socket.readAll();
1982     QCOMPARE(uploadedData, data);
1983 }
1984
1985 void tst_QNetworkReply::putToHttpSynchronous_data()
1986 {
1987     uniqueExtension = createUniqueExtension();
1988     putToFile_data();
1989 }
1990
1991 void tst_QNetworkReply::putToHttpSynchronous()
1992 {
1993     QUrl url("http://" + QtNetworkSettings::serverName());
1994     url.setPath(QString("/dav/qnetworkaccess-putToHttp-%1-%2")
1995                 .arg(QTest::currentDataTag())
1996                 .arg(uniqueExtension));
1997
1998     QNetworkRequest request(url);
1999     QNetworkReplyPtr reply;
2000
2001     QFETCH(QByteArray, data);
2002
2003     request.setAttribute(
2004             QNetworkRequest::SynchronousRequestAttribute,
2005             true);
2006
2007     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data));
2008
2009     QCOMPARE(reply->url(), url);
2010     QCOMPARE(reply->error(), QNetworkReply::NoError);
2011
2012     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 201); // 201 Created
2013
2014     // download the file again from HTTP to make sure it was uploaded
2015     // correctly. HTTP/0.9 is enough
2016     QTcpSocket socket;
2017     socket.connectToHost(QtNetworkSettings::serverName(), 80);
2018     socket.write("GET " + url.toEncoded(QUrl::RemoveScheme | QUrl::RemoveAuthority) + "\r\n");
2019     if (!socket.waitForDisconnected(10000))
2020         QFAIL("Network timeout");
2021
2022     QByteArray uploadedData = socket.readAll();
2023     QCOMPARE(uploadedData, data);
2024 }
2025
2026 void tst_QNetworkReply::postToHttp_data()
2027 {
2028     putToFile_data();
2029 }
2030
2031 void tst_QNetworkReply::postToHttp()
2032 {
2033     QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi");
2034
2035     QNetworkRequest request(url);
2036     request.setRawHeader("Content-Type", "application/octet-stream");
2037     QNetworkReplyPtr reply;
2038
2039     QFETCH(QByteArray, data);
2040
2041     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data));
2042
2043     QCOMPARE(reply->url(), url);
2044     QCOMPARE(reply->error(), QNetworkReply::NoError);
2045
2046     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
2047
2048     QFETCH(QByteArray, md5sum);
2049     QByteArray uploadedData = reply->readAll().trimmed();
2050     QCOMPARE(uploadedData, md5sum.toHex());
2051 }
2052
2053 void tst_QNetworkReply::postToHttpSynchronous_data()
2054 {
2055     putToFile_data();
2056 }
2057
2058 void tst_QNetworkReply::postToHttpSynchronous()
2059 {
2060     QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi");
2061
2062     QNetworkRequest request(url);
2063     request.setRawHeader("Content-Type", "application/octet-stream");
2064
2065     request.setAttribute(
2066             QNetworkRequest::SynchronousRequestAttribute,
2067             true);
2068
2069     QNetworkReplyPtr reply;
2070
2071     QFETCH(QByteArray, data);
2072
2073     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data));
2074
2075     QCOMPARE(reply->url(), url);
2076     QCOMPARE(reply->error(), QNetworkReply::NoError);
2077
2078     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
2079
2080     QFETCH(QByteArray, md5sum);
2081     QByteArray uploadedData = reply->readAll().trimmed();
2082     QCOMPARE(uploadedData, md5sum.toHex());
2083 }
2084
2085 void tst_QNetworkReply::postToHttpMultipart_data()
2086 {
2087     QTest::addColumn<QUrl>("url");
2088     QTest::addColumn<QHttpMultiPart *>("multiPart");
2089     QTest::addColumn<QByteArray>("expectedReplyData");
2090     QTest::addColumn<QByteArray>("contentType");
2091
2092     QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/multipart.cgi");
2093     QByteArray expectedData;
2094
2095
2096     // empty parts
2097
2098     QHttpMultiPart *emptyMultiPart = new QHttpMultiPart;
2099     QTest::newRow("empty") << url << emptyMultiPart << expectedData << QByteArray("mixed");
2100
2101     QHttpMultiPart *emptyRelatedMultiPart = new QHttpMultiPart;
2102     emptyRelatedMultiPart->setContentType(QHttpMultiPart::RelatedType);
2103     QTest::newRow("empty-related") << url << emptyRelatedMultiPart << expectedData << QByteArray("related");
2104
2105     QHttpMultiPart *emptyAlternativeMultiPart = new QHttpMultiPart;
2106     emptyAlternativeMultiPart->setContentType(QHttpMultiPart::AlternativeType);
2107     QTest::newRow("empty-alternative") << url << emptyAlternativeMultiPart << expectedData << QByteArray("alternative");
2108
2109
2110     // text-only parts
2111
2112     QHttpPart textPart;
2113     textPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
2114     textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"text\""));
2115     textPart.setBody("7 bytes");
2116     QHttpMultiPart *multiPart1 = new QHttpMultiPart;
2117     multiPart1->setContentType(QHttpMultiPart::FormDataType);
2118     multiPart1->append(textPart);
2119     expectedData = "key: text, value: 7 bytes\n";
2120     QTest::newRow("text") << url << multiPart1 << expectedData << QByteArray("form-data");
2121
2122     QHttpMultiPart *customMultiPart = new QHttpMultiPart;
2123     customMultiPart->append(textPart);
2124     expectedData = "header: Content-Type, value: 'text/plain'\n"
2125                    "header: Content-Disposition, value: 'form-data; name=\"text\"'\n"
2126                    "content: 7 bytes\n"
2127                    "\n";
2128     QTest::newRow("text-custom") << url << customMultiPart << expectedData << QByteArray("custom");
2129
2130     QHttpPart textPart2;
2131     textPart2.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
2132     textPart2.setRawHeader("myRawHeader", "myValue");
2133     textPart2.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"text2\""));
2134     textPart2.setBody("some more bytes");
2135     textPart2.setBodyDevice((QIODevice *) 1); // test whether setting and unsetting of the device works
2136     textPart2.setBodyDevice(0);
2137     QHttpMultiPart *multiPart2 = new QHttpMultiPart;
2138     multiPart2->setContentType(QHttpMultiPart::FormDataType);
2139     multiPart2->append(textPart);
2140     multiPart2->append(textPart2);
2141     expectedData = "key: text2, value: some more bytes\n"
2142                    "key: text, value: 7 bytes\n";
2143     QTest::newRow("text-text") << url << multiPart2 << expectedData << QByteArray("form-data");
2144
2145
2146     QHttpPart textPart3;
2147     textPart3.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
2148     textPart3.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"text3\""));
2149     textPart3.setRawHeader("Content-Location", "http://my.test.location.tld");
2150     textPart3.setBody("even more bytes");
2151     QHttpMultiPart *multiPart3 = new QHttpMultiPart;
2152     multiPart3->setContentType(QHttpMultiPart::AlternativeType);
2153     multiPart3->append(textPart);
2154     multiPart3->append(textPart2);
2155     multiPart3->append(textPart3);
2156     expectedData = "header: Content-Type, value: 'text/plain'\n"
2157                    "header: Content-Disposition, value: 'form-data; name=\"text\"'\n"
2158                    "content: 7 bytes\n"
2159                    "\n"
2160                    "header: Content-Type, value: 'text/plain'\n"
2161                    "header: myRawHeader, value: 'myValue'\n"
2162                    "header: Content-Disposition, value: 'form-data; name=\"text2\"'\n"
2163                    "content: some more bytes\n"
2164                    "\n"
2165                    "header: Content-Type, value: 'text/plain'\n"
2166                    "header: Content-Disposition, value: 'form-data; name=\"text3\"'\n"
2167                    "header: Content-Location, value: 'http://my.test.location.tld'\n"
2168                    "content: even more bytes\n\n";
2169     QTest::newRow("text-text-text") << url << multiPart3 << expectedData << QByteArray("alternative");
2170
2171
2172
2173     // text and image parts
2174
2175     QHttpPart imagePart11;
2176     imagePart11.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
2177     imagePart11.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage\""));
2178     imagePart11.setRawHeader("Content-Location", "http://my.test.location.tld");
2179     imagePart11.setRawHeader("Content-ID", "my@id.tld");
2180     QFile *file11 = new QFile(testDataDir + "/image1.jpg");
2181     file11->open(QIODevice::ReadOnly);
2182     imagePart11.setBodyDevice(file11);
2183     QHttpMultiPart *imageMultiPart1 = new QHttpMultiPart(QHttpMultiPart::FormDataType);
2184     imageMultiPart1->append(imagePart11);
2185     file11->setParent(imageMultiPart1);
2186     expectedData = "key: testImage, value: 87ef3bb319b004ba9e5e9c9fa713776e\n"; // md5 sum of file
2187     QTest::newRow("image") << url << imageMultiPart1 << expectedData << QByteArray("form-data");
2188
2189     QHttpPart imagePart21;
2190     imagePart21.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
2191     imagePart21.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage1\""));
2192     imagePart21.setRawHeader("Content-Location", "http://my.test.location.tld");
2193     imagePart21.setRawHeader("Content-ID", "my@id.tld");
2194     QFile *file21 = new QFile(testDataDir + "/image1.jpg");
2195     file21->open(QIODevice::ReadOnly);
2196     imagePart21.setBodyDevice(file21);
2197     QHttpMultiPart *imageMultiPart2 = new QHttpMultiPart();
2198     imageMultiPart2->setContentType(QHttpMultiPart::FormDataType);
2199     imageMultiPart2->append(textPart);
2200     imageMultiPart2->append(imagePart21);
2201     file21->setParent(imageMultiPart2);
2202     QHttpPart imagePart22;
2203     imagePart22.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
2204     imagePart22.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage2\""));
2205     QFile *file22 = new QFile(testDataDir + "/image2.jpg");
2206     file22->open(QIODevice::ReadOnly);
2207     imagePart22.setBodyDevice(file22);
2208     imageMultiPart2->append(imagePart22);
2209     file22->setParent(imageMultiPart2);
2210     expectedData = "key: testImage1, value: 87ef3bb319b004ba9e5e9c9fa713776e\n"
2211                    "key: text, value: 7 bytes\n"
2212                    "key: testImage2, value: 483761b893f7fb1bd2414344cd1f3dfb\n";
2213     QTest::newRow("text-image-image") << url << imageMultiPart2 << expectedData << QByteArray("form-data");
2214
2215
2216     QHttpPart imagePart31;
2217     imagePart31.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
2218     imagePart31.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage1\""));
2219     imagePart31.setRawHeader("Content-Location", "http://my.test.location.tld");
2220     imagePart31.setRawHeader("Content-ID", "my@id.tld");
2221     QFile *file31 = new QFile(testDataDir + "/image1.jpg");
2222     file31->open(QIODevice::ReadOnly);
2223     imagePart31.setBodyDevice(file31);
2224     QHttpMultiPart *imageMultiPart3 = new QHttpMultiPart(QHttpMultiPart::FormDataType);
2225     imageMultiPart3->append(imagePart31);
2226     file31->setParent(imageMultiPart3);
2227     QHttpPart imagePart32;
2228     imagePart32.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
2229     imagePart32.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage2\""));
2230     QFile *file32 = new QFile(testDataDir + "/image2.jpg");
2231     file32->open(QIODevice::ReadOnly);
2232     imagePart32.setBodyDevice(file31); // check that resetting works
2233     imagePart32.setBodyDevice(file32);
2234     imageMultiPart3->append(imagePart32);
2235     file32->setParent(imageMultiPart3);
2236     QHttpPart imagePart33;
2237     imagePart33.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
2238     imagePart33.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage3\""));
2239     QFile *file33 = new QFile(testDataDir + "/image3.jpg");
2240     file33->open(QIODevice::ReadOnly);
2241     imagePart33.setBodyDevice(file33);
2242     imageMultiPart3->append(imagePart33);
2243     file33->setParent(imageMultiPart3);
2244     expectedData = "key: testImage1, value: 87ef3bb319b004ba9e5e9c9fa713776e\n"
2245                    "key: testImage2, value: 483761b893f7fb1bd2414344cd1f3dfb\n"
2246                    "key: testImage3, value: ab0eb6fd4fcf8b4436254870b4513033\n";
2247     QTest::newRow("3-images") << url << imageMultiPart3 << expectedData << QByteArray("form-data");
2248
2249
2250     // note: nesting multiparts is not working currently; for that, the outputDevice would need to be public
2251
2252 //    QHttpPart imagePart41;
2253 //    imagePart41.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
2254 //    QFile *file41 = new QFile(testDataDir + "/image1.jpg");
2255 //    file41->open(QIODevice::ReadOnly);
2256 //    imagePart41.setBodyDevice(file41);
2257 //
2258 //    QHttpMultiPart *innerMultiPart = new QHttpMultiPart();
2259 //    innerMultiPart->setContentType(QHttpMultiPart::FormDataType);
2260 //    textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant());
2261 //    innerMultiPart->append(textPart);
2262 //    innerMultiPart->append(imagePart41);
2263 //    textPart2.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant());
2264 //    innerMultiPart->append(textPart2);
2265 //
2266 //    QHttpPart nestedPart;
2267 //    nestedPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"nestedMessage"));
2268 //    nestedPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("multipart/alternative; boundary=\"" + innerMultiPart->boundary() + "\""));
2269 //    innerMultiPart->outputDevice()->open(QIODevice::ReadOnly);
2270 //    nestedPart.setBodyDevice(innerMultiPart->outputDevice());
2271 //
2272 //    QHttpMultiPart *outerMultiPart = new QHttpMultiPart;
2273 //    outerMultiPart->setContentType(QHttpMultiPart::FormDataType);
2274 //    outerMultiPart->append(textPart);
2275 //    outerMultiPart->append(nestedPart);
2276 //    outerMultiPart->append(textPart2);
2277 //    expectedData = "nothing"; // the CGI.pm module running on the test server does not understand nested multiparts
2278 //    openFiles.clear();
2279 //    openFiles << file41;
2280 //    QTest::newRow("nested") << url << outerMultiPart << expectedData << openFiles;
2281
2282
2283     // test setting large chunks of content with a byte array instead of a device (DISCOURAGED because of high memory consumption,
2284     // but we need to test that the behavior is correct)
2285     QHttpPart imagePart51;
2286     imagePart51.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
2287     imagePart51.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage\""));
2288     QFile *file51 = new QFile(testDataDir + "/image1.jpg");
2289     file51->open(QIODevice::ReadOnly);
2290     QByteArray imageData = file51->readAll();
2291     file51->close();
2292     delete file51;
2293     imagePart51.setBody("7 bytes"); // check that resetting works
2294     imagePart51.setBody(imageData);
2295     QHttpMultiPart *imageMultiPart5 = new QHttpMultiPart;
2296     imageMultiPart5->setContentType(QHttpMultiPart::FormDataType);
2297     imageMultiPart5->append(imagePart51);
2298     expectedData = "key: testImage, value: 87ef3bb319b004ba9e5e9c9fa713776e\n"; // md5 sum of file
2299     QTest::newRow("image-as-content") << url << imageMultiPart5 << expectedData << QByteArray("form-data");
2300 }
2301
2302 void tst_QNetworkReply::postToHttpMultipart()
2303 {
2304     QFETCH(QUrl, url);
2305
2306     static QSet<QByteArray> boundaries;
2307
2308     QNetworkRequest request(url);
2309     QNetworkReplyPtr reply;
2310
2311     QFETCH(QHttpMultiPart *, multiPart);
2312     QFETCH(QByteArray, expectedReplyData);
2313     QFETCH(QByteArray, contentType);
2314
2315     // hack for testing the setting of the content-type header by hand:
2316     if (contentType == "custom") {
2317         QByteArray contentType("multipart/custom; boundary=\"" + multiPart->boundary() + "\"");
2318         request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
2319     }
2320
2321     QVERIFY2(! boundaries.contains(multiPart->boundary()), "boundary '" + multiPart->boundary() + "' has been created twice");
2322     boundaries.insert(multiPart->boundary());
2323
2324     RUN_REQUEST(runMultipartRequest(request, reply, multiPart, "POST"));
2325     multiPart->deleteLater();
2326
2327     QCOMPARE(reply->url(), url);
2328     QCOMPARE(reply->error(), QNetworkReply::NoError);
2329
2330     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
2331
2332     QVERIFY(multiPart->boundary().count() > 20); // check that there is randomness after the "boundary_.oOo._" string
2333     QVERIFY(multiPart->boundary().count() < 70);
2334     QByteArray replyData = reply->readAll();
2335
2336     expectedReplyData.prepend("content type: multipart/" + contentType + "; boundary=\"" + multiPart->boundary() + "\"\n");
2337 //    QEXPECT_FAIL("nested", "the server does not understand nested multipart messages", Continue); // see above
2338     QCOMPARE(replyData, expectedReplyData);
2339 }
2340
2341 void tst_QNetworkReply::putToHttpMultipart_data()
2342 {
2343     postToHttpMultipart_data();
2344 }
2345
2346 void tst_QNetworkReply::putToHttpMultipart()
2347 {
2348     QSKIP("test server script cannot handle PUT data yet");
2349     QFETCH(QUrl, url);
2350
2351     static QSet<QByteArray> boundaries;
2352
2353     QNetworkRequest request(url);
2354     QNetworkReplyPtr reply;
2355
2356     QFETCH(QHttpMultiPart *, multiPart);
2357     QFETCH(QByteArray, expectedReplyData);
2358     QFETCH(QByteArray, contentType);
2359
2360     // hack for testing the setting of the content-type header by hand:
2361     if (contentType == "custom") {
2362         QByteArray contentType("multipart/custom; boundary=\"" + multiPart->boundary() + "\"");
2363         request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
2364     }
2365
2366     QVERIFY2(! boundaries.contains(multiPart->boundary()), "boundary '" + multiPart->boundary() + "' has been created twice");
2367     boundaries.insert(multiPart->boundary());
2368
2369     RUN_REQUEST(runMultipartRequest(request, reply, multiPart, "PUT"));
2370     multiPart->deleteLater();
2371
2372     QCOMPARE(reply->url(), url);
2373     QCOMPARE(reply->error(), QNetworkReply::NoError);
2374
2375     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
2376
2377     QVERIFY(multiPart->boundary().count() > 20); // check that there is randomness after the "boundary_.oOo._" string
2378     QVERIFY(multiPart->boundary().count() < 70);
2379     QByteArray replyData = reply->readAll();
2380
2381     expectedReplyData.prepend("content type: multipart/" + contentType + "; boundary=\"" + multiPart->boundary() + "\"\n");
2382 //    QEXPECT_FAIL("nested", "the server does not understand nested multipart messages", Continue); // see above
2383     QCOMPARE(replyData, expectedReplyData);
2384 }
2385
2386 void tst_QNetworkReply::deleteFromHttp_data()
2387 {
2388     QTest::addColumn<QUrl>("url");
2389     QTest::addColumn<int>("resultCode");
2390     QTest::addColumn<QNetworkReply::NetworkError>("error");
2391
2392     // for status codes to expect, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
2393
2394     QTest::newRow("405-method-not-allowed") << QUrl("http://" + QtNetworkSettings::serverName() + "/index.html") << 405 << QNetworkReply::ContentOperationNotPermittedError;
2395     QTest::newRow("200-ok") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/http-delete.cgi?200-ok") << 200 << QNetworkReply::NoError;
2396     QTest::newRow("202-accepted") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/http-delete.cgi?202-accepted") << 202 << QNetworkReply::NoError;
2397     QTest::newRow("204-no-content") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/http-delete.cgi?204-no-content") << 204 << QNetworkReply::NoError;
2398     QTest::newRow("404-not-found") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/http-delete.cgi?404-not-found") << 404 << QNetworkReply::ContentNotFoundError;
2399 }
2400
2401 void tst_QNetworkReply::deleteFromHttp()
2402 {
2403     QFETCH(QUrl, url);
2404     QFETCH(int, resultCode);
2405     QFETCH(QNetworkReply::NetworkError, error);
2406     QNetworkRequest request(url);
2407     QNetworkReplyPtr reply;
2408     runSimpleRequest(QNetworkAccessManager::DeleteOperation, request, reply, 0);
2409     QCOMPARE(reply->url(), url);
2410     QCOMPARE(reply->error(), error);
2411     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), resultCode);
2412 }
2413
2414 void tst_QNetworkReply::putGetDeleteGetFromHttp_data()
2415 {
2416     QTest::addColumn<QUrl>("putUrl");
2417     QTest::addColumn<int>("putResultCode");
2418     QTest::addColumn<QNetworkReply::NetworkError>("putError");
2419     QTest::addColumn<QUrl>("deleteUrl");
2420     QTest::addColumn<int>("deleteResultCode");
2421     QTest::addColumn<QNetworkReply::NetworkError>("deleteError");
2422     QTest::addColumn<QUrl>("get2Url");
2423     QTest::addColumn<int>("get2ResultCode");
2424     QTest::addColumn<QNetworkReply::NetworkError>("get2Error");
2425
2426     QUrl url("http://" + QtNetworkSettings::serverName());
2427     url.setPath(QString("/dav/qnetworkaccess-putToHttp-%1-%2")
2428                 .arg(QTest::currentDataTag())
2429                 .arg(uniqueExtension));
2430
2431     // first use case: put, get (to check it is there), delete, get (to check it is not there anymore)
2432     QTest::newRow("success") << url << 201 << QNetworkReply::NoError << url << 204 << QNetworkReply::NoError << url << 404 << QNetworkReply::ContentNotFoundError;
2433
2434     QUrl wrongUrl("http://" + QtNetworkSettings::serverName());
2435     wrongUrl.setPath(QString("/dav/qnetworkaccess-thisURLisNotAvailable"));
2436
2437     // second use case: put, get (to check it is there), delete wrong URL, get (to check it is still there)
2438     QTest::newRow("delete-error") << url << 201 << QNetworkReply::NoError << wrongUrl << 404 << QNetworkReply::ContentNotFoundError << url << 200 << QNetworkReply::NoError;
2439
2440 }
2441
2442 void tst_QNetworkReply::putGetDeleteGetFromHttp()
2443 {
2444     QFETCH(QUrl, putUrl);
2445     QFETCH(int, putResultCode);
2446     QFETCH(QNetworkReply::NetworkError, putError);
2447     QFETCH(QUrl, deleteUrl);
2448     QFETCH(int, deleteResultCode);
2449     QFETCH(QNetworkReply::NetworkError, deleteError);
2450     QFETCH(QUrl, get2Url);
2451     QFETCH(int, get2ResultCode);
2452     QFETCH(QNetworkReply::NetworkError, get2Error);
2453
2454     QNetworkRequest putRequest(putUrl);
2455     QNetworkRequest deleteRequest(deleteUrl);
2456     QNetworkRequest get2Request(get2Url);
2457     QNetworkReplyPtr reply;
2458
2459     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, putRequest, reply, 0));
2460     QCOMPARE(reply->error(), putError);
2461     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), putResultCode);
2462
2463     runSimpleRequest(QNetworkAccessManager::GetOperation, putRequest, reply, 0);
2464     QCOMPARE(reply->error(), QNetworkReply::NoError);
2465     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
2466
2467     runSimpleRequest(QNetworkAccessManager::DeleteOperation, deleteRequest, reply, 0);
2468     QCOMPARE(reply->error(), deleteError);
2469     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), deleteResultCode);
2470
2471     runSimpleRequest(QNetworkAccessManager::GetOperation, get2Request, reply, 0);
2472     QCOMPARE(reply->error(), get2Error);
2473     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), get2ResultCode);
2474
2475 }
2476
2477 void tst_QNetworkReply::connectToIPv6Address_data()
2478 {
2479     QTest::addColumn<QUrl>("url");
2480     QTest::addColumn<QNetworkReply::NetworkError>("error");
2481     QTest::addColumn<QByteArray>("dataToSend");
2482     QTest::addColumn<QByteArray>("hostfield");
2483     QTest::newRow("localhost") << QUrl(QByteArray("http://[::1]")) << QNetworkReply::NoError<< QByteArray("localhost") << QByteArray("[::1]");
2484     //QTest::newRow("ipv4localhost") << QUrl(QByteArray("http://127.0.0.1")) << QNetworkReply::NoError<< QByteArray("ipv4localhost") << QByteArray("127.0.0.1");
2485     //to add more test data here
2486 }
2487
2488 void tst_QNetworkReply::connectToIPv6Address()
2489 {
2490     QFETCH(QUrl, url);
2491     QFETCH(QNetworkReply::NetworkError, error);
2492     QFETCH(QByteArray, dataToSend);
2493     QFETCH(QByteArray, hostfield);
2494
2495 #if !defined(HAVE_IPV6) && defined(Q_OS_UNIX)
2496     QSKIP("system doesn't support ipv6!");
2497 #endif
2498
2499     QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: ");
2500     httpResponse += QByteArray::number(dataToSend.size());
2501     httpResponse += "\r\n\r\n";
2502     httpResponse += dataToSend;
2503
2504     MiniHttpServer server(httpResponse, false, NULL/*thread*/, true/*useipv6*/);
2505     server.doClose = true;
2506
2507     url.setPort(server.serverPort());
2508     QNetworkRequest request(url);
2509
2510     QNetworkReplyPtr reply(manager.get(request));
2511     QVERIFY(waitForFinish(reply) == Success);
2512     QByteArray content = reply->readAll();
2513     //qDebug() << server.receivedData;
2514     QByteArray hostinfo = "\r\nHost: " + hostfield + ":" + QByteArray::number(server.serverPort()) + "\r\n";
2515     QVERIFY(server.receivedData.contains(hostinfo));
2516     QVERIFY(content == dataToSend);
2517     QCOMPARE(reply->url(), request.url());
2518     QVERIFY(reply->error() == error);
2519 }
2520
2521 void tst_QNetworkReply::sendCustomRequestToHttp_data()
2522 {
2523     QTest::addColumn<QUrl>("url");
2524     QTest::addColumn<QByteArray>("verb");
2525     QTest::addColumn<QBuffer *>("device");
2526     QTest::addColumn<int>("resultCode");
2527     QTest::addColumn<QNetworkReply::NetworkError>("error");
2528     QTest::addColumn<QByteArray>("expectedContent");
2529
2530     QTest::newRow("options") << QUrl("http://" + QtNetworkSettings::serverName()) <<
2531             QByteArray("OPTIONS") << (QBuffer *) 0 << 200 << QNetworkReply::NoError << QByteArray();
2532     QTest::newRow("trace") << QUrl("http://" + QtNetworkSettings::serverName()) <<
2533             QByteArray("TRACE") << (QBuffer *) 0 << 200 << QNetworkReply::NoError << QByteArray();
2534     QTest::newRow("connect") << QUrl("http://" + QtNetworkSettings::serverName()) <<
2535             QByteArray("CONNECT") << (QBuffer *) 0 << 400 << QNetworkReply::UnknownContentError << QByteArray(); // 400 = Bad Request
2536     QTest::newRow("nonsense") << QUrl("http://" + QtNetworkSettings::serverName()) <<
2537             QByteArray("NONSENSE") << (QBuffer *) 0 << 501 << QNetworkReply::ProtocolUnknownError << QByteArray(); // 501 = Method Not Implemented
2538
2539     QByteArray ba("test");
2540     QBuffer *buffer = new QBuffer;
2541     buffer->setData(ba);
2542     buffer->open(QIODevice::ReadOnly);
2543     QTest::newRow("post") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi") << QByteArray("POST")
2544             << buffer << 200 << QNetworkReply::NoError << QByteArray("098f6bcd4621d373cade4e832627b4f6\n");
2545
2546     QByteArray ba2("test");
2547     QBuffer *buffer2 = new QBuffer;
2548     buffer2->setData(ba2);
2549     buffer2->open(QIODevice::ReadOnly);
2550     QTest::newRow("put") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi") << QByteArray("PUT")
2551             << buffer2 << 200 << QNetworkReply::NoError << QByteArray("098f6bcd4621d373cade4e832627b4f6\n");
2552 }
2553
2554 void tst_QNetworkReply::sendCustomRequestToHttp()
2555 {
2556     QFETCH(QUrl, url);
2557     QNetworkRequest request(url);
2558     QNetworkReplyPtr reply;
2559     QFETCH(QByteArray, verb);
2560     QFETCH(QBuffer *, device);
2561     runCustomRequest(request, reply, verb, device);
2562     QCOMPARE(reply->url(), url);
2563     QFETCH(QNetworkReply::NetworkError, error);
2564     QCOMPARE(reply->error(), error);
2565     QFETCH(int, resultCode);
2566     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), resultCode);
2567     QFETCH(QByteArray, expectedContent);
2568     if (! expectedContent.isEmpty())
2569         QCOMPARE(reply->readAll(), expectedContent);
2570 }
2571
2572 void tst_QNetworkReply::ioGetFromData_data()
2573 {
2574     QTest::addColumn<QString>("urlStr");
2575     QTest::addColumn<QByteArray>("data");
2576
2577     QTest::newRow("data-empty") << "data:," << QByteArray();
2578     QTest::newRow("data-literal") << "data:,foo" << QByteArray("foo");
2579     QTest::newRow("data-pct") << "data:,%3Cbody%20contentEditable%3Dtrue%3E%0D%0A"
2580                            << QByteArray("<body contentEditable=true>\r\n");
2581     QTest::newRow("data-base64") << "data:;base64,UXQgaXMgZ3JlYXQh" << QByteArray("Qt is great!");
2582 }
2583
2584 void tst_QNetworkReply::ioGetFromData()
2585 {
2586     QFETCH(QString, urlStr);
2587
2588     QUrl url = QUrl::fromEncoded(urlStr.toLatin1());
2589     QNetworkRequest request(url);
2590
2591     QNetworkReplyPtr reply(manager.get(request));
2592     DataReader reader(reply);
2593
2594     connect(reply, SIGNAL(finished()),
2595             &QTestEventLoop::instance(), SLOT(exitLoop()));
2596     QTestEventLoop::instance().enterLoop(10);
2597     QVERIFY(!QTestEventLoop::instance().timeout());
2598
2599     QCOMPARE(reply->url(), request.url());
2600     QCOMPARE(reply->error(), QNetworkReply::NoError);
2601
2602     QFETCH(QByteArray, data);
2603     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toInt(), data.size());
2604     QCOMPARE(reader.data.size(), data.size());
2605     QCOMPARE(reader.data, data);
2606 }
2607
2608 void tst_QNetworkReply::ioGetFromFileSpecial_data()
2609 {
2610     getFromFileSpecial_data();
2611 }
2612
2613 void tst_QNetworkReply::ioGetFromFileSpecial()
2614 {
2615     QFETCH(QString, fileName);
2616     QFETCH(QString, url);
2617
2618     QFile resource(fileName);
2619     QVERIFY(resource.open(QIODevice::ReadOnly));
2620
2621     QNetworkRequest request;
2622     request.setUrl(url);
2623     QNetworkReplyPtr reply(manager.get(request));
2624     DataReader reader(reply);
2625
2626     connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
2627     QTestEventLoop::instance().enterLoop(10);
2628     QVERIFY(!QTestEventLoop::instance().timeout());
2629
2630     QCOMPARE(reply->url(), request.url());
2631     QCOMPARE(reply->error(), QNetworkReply::NoError);
2632
2633     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), resource.size());
2634     QCOMPARE(qint64(reader.data.size()), resource.size());
2635     QCOMPARE(reader.data, resource.readAll());
2636 }
2637
2638 void tst_QNetworkReply::ioGetFromFile_data()
2639 {
2640     putToFile_data();
2641 }
2642
2643 void tst_QNetworkReply::ioGetFromFile()
2644 {
2645     QTemporaryFile file(QDir::currentPath() + "/temp-XXXXXX");
2646     file.setAutoRemove(true);
2647     QVERIFY(file.open());
2648
2649     QFETCH(QByteArray, data);
2650     QVERIFY(file.write(data) == data.size());
2651     file.flush();
2652     QCOMPARE(file.size(), qint64(data.size()));
2653
2654     QNetworkRequest request(QUrl::fromLocalFile(file.fileName()));
2655     QNetworkReplyPtr reply(manager.get(request));
2656     QVERIFY(reply->isFinished()); // a file should immediately be done
2657     DataReader reader(reply);
2658
2659     connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
2660     QTestEventLoop::instance().enterLoop(10);
2661     QVERIFY(!QTestEventLoop::instance().timeout());
2662
2663     QCOMPARE(reply->url(), request.url());
2664     QCOMPARE(reply->error(), QNetworkReply::NoError);
2665
2666     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), file.size());
2667     QCOMPARE(qint64(reader.data.size()), file.size());
2668     QCOMPARE(reader.data, data);
2669 }
2670
2671 void tst_QNetworkReply::ioGetFromFtp_data()
2672 {
2673     QTest::addColumn<QString>("fileName");
2674     QTest::addColumn<qint64>("expectedSize");
2675
2676     QTest::newRow("bigfile") << "bigfile" << Q_INT64_C(519240);
2677
2678     QFile file(testDataDir + "/rfc3252.txt");
2679     QTest::newRow("rfc3252.txt") << "rfc3252.txt" << file.size();
2680 }
2681
2682 void tst_QNetworkReply::ioGetFromFtp()
2683 {
2684     QFETCH(QString, fileName);
2685     QFile reference(fileName);
2686     reference.open(QIODevice::ReadOnly); // will fail for bigfile
2687
2688     QNetworkRequest request("ftp://" + QtNetworkSettings::serverName() + "/qtest/" + fileName);
2689     QNetworkReplyPtr reply(manager.get(request));
2690     DataReader reader(reply);
2691
2692     QVERIFY(waitForFinish(reply) == Success);
2693
2694     QCOMPARE(reply->url(), request.url());
2695     QCOMPARE(reply->error(), QNetworkReply::NoError);
2696
2697     QFETCH(qint64, expectedSize);
2698     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), expectedSize);
2699     QCOMPARE(qint64(reader.data.size()), expectedSize);
2700
2701     if (reference.isOpen())
2702         QCOMPARE(reader.data, reference.readAll());
2703 }
2704
2705 void tst_QNetworkReply::ioGetFromFtpWithReuse()
2706 {
2707     QString fileName = testDataDir + "/rfc3252.txt";
2708     QFile reference(fileName);
2709     reference.open(QIODevice::ReadOnly);
2710
2711     QNetworkRequest request(QUrl("ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
2712
2713     // two concurrent (actually, consecutive) gets:
2714     QNetworkReplyPtr reply1(manager.get(request));
2715     DataReader reader1(reply1);
2716     QNetworkReplyPtr reply2(manager.get(request));
2717     DataReader reader2(reply2);
2718     QSignalSpy spy(reply1.data(), SIGNAL(finished()));
2719
2720     QVERIFY(waitForFinish(reply1) == Success);
2721     QVERIFY(waitForFinish(reply2) == Success);
2722
2723     QCOMPARE(reply1->url(), request.url());
2724     QCOMPARE(reply2->url(), request.url());
2725     QCOMPARE(reply1->error(), QNetworkReply::NoError);
2726     QCOMPARE(reply2->error(), QNetworkReply::NoError);
2727
2728     QCOMPARE(qint64(reader1.data.size()), reference.size());
2729     QCOMPARE(qint64(reader2.data.size()), reference.size());
2730     QCOMPARE(reply1->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
2731     QCOMPARE(reply2->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
2732
2733     QByteArray referenceData = reference.readAll();
2734     QCOMPARE(reader1.data, referenceData);
2735     QCOMPARE(reader2.data, referenceData);
2736 }
2737
2738 void tst_QNetworkReply::ioGetFromHttp()
2739 {
2740     QFile reference(testDataDir + "/rfc3252.txt");
2741     QVERIFY(reference.open(QIODevice::ReadOnly));
2742
2743     QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
2744     QNetworkReplyPtr reply(manager.get(request));
2745     DataReader reader(reply);
2746
2747     QVERIFY(waitForFinish(reply) == Success);
2748
2749     QCOMPARE(reply->url(), request.url());
2750     QCOMPARE(reply->error(), QNetworkReply::NoError);
2751     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
2752
2753     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
2754     QCOMPARE(qint64(reader.data.size()), reference.size());
2755
2756     QCOMPARE(reader.data, reference.readAll());
2757 }
2758
2759 void tst_QNetworkReply::ioGetFromHttpWithReuseParallel()
2760 {
2761     QFile reference(testDataDir + "/rfc3252.txt");
2762     QVERIFY(reference.open(QIODevice::ReadOnly));
2763
2764     QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
2765     QNetworkReplyPtr reply1(manager.get(request));
2766     QNetworkReplyPtr reply2(manager.get(request));
2767     DataReader reader1(reply1);
2768     DataReader reader2(reply2);
2769     QSignalSpy spy(reply1.data(), SIGNAL(finished()));
2770
2771     QVERIFY(waitForFinish(reply2) == Success);
2772     QVERIFY(waitForFinish(reply1) == Success);
2773
2774     QCOMPARE(reply1->url(), request.url());
2775     QCOMPARE(reply2->url(), request.url());
2776     QCOMPARE(reply1->error(), QNetworkReply::NoError);
2777     QCOMPARE(reply2->error(), QNetworkReply::NoError);
2778     QCOMPARE(reply1->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
2779     QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
2780
2781     QCOMPARE(reply1->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
2782     QCOMPARE(reply2->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
2783     QCOMPARE(qint64(reader1.data.size()), reference.size());
2784     QCOMPARE(qint64(reader2.data.size()), reference.size());
2785
2786     QByteArray referenceData = reference.readAll();
2787     QCOMPARE(reader1.data, referenceData);
2788     QCOMPARE(reader2.data, referenceData);
2789 }
2790
2791 void tst_QNetworkReply::ioGetFromHttpWithReuseSequential()
2792 {
2793     QFile reference(testDataDir + "/rfc3252.txt");
2794     QVERIFY(reference.open(QIODevice::ReadOnly));
2795
2796     QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
2797     {
2798         QNetworkReplyPtr reply(manager.get(request));
2799         DataReader reader(reply);
2800
2801         QVERIFY(waitForFinish(reply) == Success);
2802
2803         QCOMPARE(reply->url(), request.url());
2804         QCOMPARE(reply->error(), QNetworkReply::NoError);
2805         QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
2806
2807         QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
2808         QCOMPARE(qint64(reader.data.size()), reference.size());
2809
2810         QCOMPARE(reader.data, reference.readAll());
2811     }
2812
2813     reference.seek(0);
2814     // rinse and repeat:
2815     {
2816         QNetworkReplyPtr reply(manager.get(request));
2817         DataReader reader(reply);
2818
2819         QVERIFY(waitForFinish(reply) == Success);
2820
2821         QCOMPARE(reply->url(), request.url());
2822         QCOMPARE(reply->error(), QNetworkReply::NoError);
2823         QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
2824
2825         QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
2826         QCOMPARE(qint64(reader.data.size()), reference.size());
2827
2828         QCOMPARE(reader.data, reference.readAll());
2829     }
2830 }
2831
2832 void tst_QNetworkReply::ioGetFromHttpWithAuth_data()
2833 {
2834     QTest::addColumn<QUrl>("url");
2835     QTest::addColumn<QByteArray>("expectedData");
2836     QTest::addColumn<int>("expectedAuth");
2837
2838     QFile reference(testDataDir + "/rfc3252.txt");
2839     reference.open(QIODevice::ReadOnly);
2840     QByteArray referenceData = reference.readAll();
2841     QTest::newRow("basic") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth/rfc3252.txt") << referenceData << 1;
2842     QTest::newRow("digest") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/auth-digest/") << QByteArray("digest authentication successful\n") << 1;
2843     //if url contains username & password, then it should be used
2844     QTest::newRow("basic-in-url") << QUrl("http://httptest:httptest@" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth/rfc3252.txt") << referenceData << 0;
2845     QTest::newRow("digest-in-url") << QUrl("http://httptest:httptest@" + QtNetworkSettings::serverName() + "/qtest/auth-digest/") << QByteArray("digest authentication successful\n") << 0;
2846     // if url contains incorrect credentials, expect QNAM to ask for good ones (even if cached - matches behaviour of browsers)
2847     QTest::newRow("basic-bad-user-in-url") << QUrl("http://baduser:httptest@" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth/rfc3252.txt") << referenceData << 3;
2848     QTest::newRow("basic-bad-password-in-url") << QUrl("http://httptest:wrong@" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth/rfc3252.txt") << referenceData << 3;
2849     QTest::newRow("digest-bad-user-in-url") << QUrl("http://baduser:httptest@" + QtNetworkSettings::serverName() + "/qtest/auth-digest/") << QByteArray("digest authentication successful\n") << 3;
2850     QTest::newRow("digest-bad-password-in-url") << QUrl("http://httptest:wrong@" + QtNetworkSettings::serverName() + "/qtest/auth-digest/") << QByteArray("digest authentication successful\n") << 3;
2851 }
2852
2853 void tst_QNetworkReply::ioGetFromHttpWithAuth()
2854 {
2855     // This test sends three requests
2856     // The first two in parallel
2857     // The third after the first two finished
2858
2859     QFETCH(QUrl, url);
2860     QFETCH(QByteArray, expectedData);
2861     QFETCH(int, expectedAuth);
2862     QNetworkRequest request(url);
2863     {
2864         QNetworkReplyPtr reply1(manager.get(request));
2865         QNetworkReplyPtr reply2(manager.get(request));
2866         DataReader reader1(reply1);
2867         DataReader reader2(reply2);
2868         QSignalSpy finishedspy(reply1.data(), SIGNAL(finished()));
2869
2870         QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
2871         connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
2872                 SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
2873
2874         QVERIFY(waitForFinish(reply2) == Success);
2875         QVERIFY(waitForFinish(reply1) == Success);
2876
2877         manager.disconnect(SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
2878                            this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
2879
2880         QCOMPARE(reply1->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
2881         QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
2882         QCOMPARE(reader1.data, expectedData);
2883         QCOMPARE(reader2.data, expectedData);
2884
2885         QCOMPARE(authspy.count(), (expectedAuth ? 1 : 0));
2886         expectedAuth = qMax(0, expectedAuth - 1);
2887     }
2888
2889     // rinse and repeat:
2890     {
2891         QNetworkReplyPtr reply(manager.get(request));
2892         DataReader reader(reply);
2893
2894         QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
2895         connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
2896                 SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
2897
2898         QVERIFY(waitForFinish(reply) == Success);
2899
2900         manager.disconnect(SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
2901                            this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
2902
2903         QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
2904         QCOMPARE(reader.data, expectedData);
2905
2906         QCOMPARE(authspy.count(), (expectedAuth ? 1 : 0));
2907         expectedAuth = qMax(0, expectedAuth - 1);
2908     }
2909
2910     // now check with synchronous calls:
2911     {
2912         request.setAttribute(
2913                 QNetworkRequest::SynchronousRequestAttribute,
2914                 true);
2915
2916         QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
2917         QNetworkReplyPtr replySync(manager.get(request));
2918         QVERIFY(replySync->isFinished()); // synchronous
2919         if (expectedAuth) {
2920             // bad credentials in a synchronous request should just fail
2921             QCOMPARE(replySync->error(), QNetworkReply::AuthenticationRequiredError);
2922         } else {
2923             QCOMPARE(authspy.count(), 0);
2924
2925             // we cannot use a data reader here, since that connects to the readyRead signal,
2926             // just use readAll()
2927
2928             // the only thing we check here is that the auth cache was used when using synchronous requests
2929             QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
2930             QCOMPARE(replySync->readAll(), expectedData);
2931         }
2932     }
2933
2934     // check that credentials are used from cache if the same url is requested without credentials
2935     {
2936         url.setUserInfo(QString());
2937         request.setUrl(url);
2938         request.setAttribute(
2939                 QNetworkRequest::SynchronousRequestAttribute,
2940                 true);
2941
2942         QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
2943         QNetworkReplyPtr replySync(manager.get(request));
2944         QVERIFY(replySync->isFinished()); // synchronous
2945         if (expectedAuth) {
2946             // bad credentials in a synchronous request should just fail
2947             QCOMPARE(replySync->error(), QNetworkReply::AuthenticationRequiredError);
2948         } else {
2949             QCOMPARE(authspy.count(), 0);
2950
2951             // we cannot use a data reader here, since that connects to the readyRead signal,
2952             // just use readAll()
2953
2954             // the only thing we check here is that the auth cache was used when using synchronous requests
2955             QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
2956             QCOMPARE(replySync->readAll(), expectedData);
2957         }
2958     }
2959 }
2960
2961 void tst_QNetworkReply::ioGetFromHttpWithAuthSynchronous()
2962 {
2963     // verify that we do not enter an endless loop with synchronous calls and wrong credentials
2964     // the case when we succeed with the login is tested in ioGetFromHttpWithAuth()
2965
2966     QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth/rfc3252.txt"));
2967     request.setAttribute(
2968             QNetworkRequest::SynchronousRequestAttribute,
2969             true);
2970
2971     QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
2972     QNetworkReplyPtr replySync(manager.get(request));
2973     QVERIFY(replySync->isFinished()); // synchronous
2974     QCOMPARE(replySync->error(), QNetworkReply::AuthenticationRequiredError);
2975     QCOMPARE(authspy.count(), 0);
2976     QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 401);
2977 }
2978
2979 void tst_QNetworkReply::ioGetFromHttpWithProxyAuth()
2980 {
2981     // This test sends three requests
2982     // The first two in parallel
2983     // The third after the first two finished
2984     QFile reference(testDataDir + "/rfc3252.txt");
2985     QVERIFY(reference.open(QIODevice::ReadOnly));
2986
2987     QNetworkProxy proxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129);
2988     QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
2989     {
2990         manager.setProxy(proxy);
2991         QNetworkReplyPtr reply1(manager.get(request));
2992         QNetworkReplyPtr reply2(manager.get(request));
2993         manager.setProxy(QNetworkProxy());
2994
2995         DataReader reader1(reply1);
2996         DataReader reader2(reply2);
2997         QSignalSpy finishedspy(reply1.data(), SIGNAL(finished()));
2998
2999         QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3000         connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
3001                 SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3002
3003         QVERIFY(waitForFinish(reply2) == Success);
3004         QVERIFY(waitForFinish(reply1) == Success);
3005
3006         manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
3007                            this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3008
3009         QCOMPARE(reply1->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3010         QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3011         QByteArray referenceData = reference.readAll();
3012         QCOMPARE(reader1.data, referenceData);
3013         QCOMPARE(reader2.data, referenceData);
3014
3015         QCOMPARE(authspy.count(), 1);
3016     }
3017
3018     reference.seek(0);
3019     // rinse and repeat:
3020     {
3021         manager.setProxy(proxy);
3022         QNetworkReplyPtr reply(manager.get(request));
3023         DataReader reader(reply);
3024         manager.setProxy(QNetworkProxy());
3025
3026         QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3027         connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
3028                 SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3029
3030         QVERIFY(waitForFinish(reply) == Success);
3031
3032         manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
3033                            this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3034
3035         QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3036         QCOMPARE(reader.data, reference.readAll());
3037
3038         QCOMPARE(authspy.count(), 0);
3039     }
3040
3041     // now check with synchronous calls:
3042     reference.seek(0);
3043     {
3044         request.setAttribute(
3045                 QNetworkRequest::SynchronousRequestAttribute,
3046                 true);
3047
3048         QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3049         QNetworkReplyPtr replySync(manager.get(request));
3050         QVERIFY(replySync->isFinished()); // synchronous
3051         QCOMPARE(authspy.count(), 0);
3052
3053         // we cannot use a data reader here, since that connects to the readyRead signal,
3054         // just use readAll()
3055
3056         // the only thing we check here is that the proxy auth cache was used when using synchronous requests
3057         QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3058         QCOMPARE(replySync->readAll(), reference.readAll());
3059     }
3060 }
3061
3062 void tst_QNetworkReply::ioGetFromHttpWithProxyAuthSynchronous()
3063 {
3064     // verify that we do not enter an endless loop with synchronous calls and wrong credentials
3065     // the case when we succeed with the login is tested in ioGetFromHttpWithAuth()
3066
3067     QNetworkProxy proxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129);
3068     QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
3069     manager.setProxy(proxy);
3070     request.setAttribute(
3071             QNetworkRequest::SynchronousRequestAttribute,
3072             true);
3073
3074     QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3075     QNetworkReplyPtr replySync(manager.get(request));
3076     manager.setProxy(QNetworkProxy()); // reset
3077     QVERIFY(replySync->isFinished()); // synchronous
3078     QCOMPARE(replySync->error(), QNetworkReply::ProxyAuthenticationRequiredError);
3079     QCOMPARE(authspy.count(), 0);
3080     QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 407);
3081 }
3082
3083 void tst_QNetworkReply::ioGetFromHttpWithSocksProxy()
3084 {
3085     // HTTP caching proxies are tested by the above function
3086     // test SOCKSv5 proxies too
3087
3088     QFile reference(testDataDir + "/rfc3252.txt");
3089     QVERIFY(reference.open(QIODevice::ReadOnly));
3090
3091     QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080);
3092     QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
3093     {
3094         manager.setProxy(proxy);
3095         QNetworkReplyPtr reply(manager.get(request));
3096         DataReader reader(reply);
3097         manager.setProxy(QNetworkProxy());
3098
3099         QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3100         connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
3101                 SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3102
3103         QVERIFY(waitForFinish(reply) == Success);
3104
3105         manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
3106                            this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3107
3108         QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3109         QCOMPARE(reader.data, reference.readAll());
3110
3111         QCOMPARE(authspy.count(), 0);
3112     }
3113
3114     // set an invalid proxy just to make sure that we can't load
3115     proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1079);
3116     {
3117         manager.setProxy(proxy);
3118         QNetworkReplyPtr reply(manager.get(request));
3119         DataReader reader(reply);
3120         manager.setProxy(QNetworkProxy());
3121
3122         QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3123         connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
3124                 SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3125
3126         QVERIFY(waitForFinish(reply) == Failure);
3127
3128         manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
3129                            this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3130
3131         QVERIFY(!reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).isValid());
3132         QVERIFY(reader.data.isEmpty());
3133
3134         QVERIFY(int(reply->error()) > 0);
3135         QEXPECT_FAIL("", "QTcpSocket doesn't return enough information yet", Continue);
3136         QCOMPARE(int(reply->error()), int(QNetworkReply::ProxyConnectionRefusedError));
3137
3138         QCOMPARE(authspy.count(), 0);
3139     }
3140 }
3141
3142 #ifndef QT_NO_SSL
3143 void tst_QNetworkReply::ioGetFromHttpsWithSslErrors()
3144 {
3145     QFile reference(testDataDir + "/rfc3252.txt");
3146     QVERIFY(reference.open(QIODevice::ReadOnly));
3147
3148     QNetworkRequest request(QUrl("https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
3149     QNetworkReplyPtr reply(manager.get(request));
3150     DataReader reader(reply);
3151
3152     QSignalSpy sslspy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
3153     connect(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
3154             SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
3155     connect(reply, SIGNAL(metaDataChanged()), SLOT(storeSslConfiguration()));
3156
3157     QVERIFY(waitForFinish(reply) == Success);
3158
3159     manager.disconnect(SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
3160                        this, SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
3161
3162     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3163     QCOMPARE(reader.data, reference.readAll());
3164
3165     QCOMPARE(sslspy.count(), 1);
3166
3167     QVERIFY(!storedSslConfiguration.isNull());
3168     QVERIFY(!reply->sslConfiguration().isNull());
3169 }
3170
3171 void tst_QNetworkReply::ioGetFromHttpsWithIgnoreSslErrors()
3172 {
3173     // same as above, except that we call ignoreSslErrors and don't connect
3174     // to the sslErrors() signal (which is *still* emitted)
3175
3176     QFile reference(testDataDir + "/rfc3252.txt");
3177     QVERIFY(reference.open(QIODevice::ReadOnly));
3178
3179     QNetworkRequest request(QUrl("https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
3180
3181     QNetworkReplyPtr reply(manager.get(request));
3182     reply->ignoreSslErrors();
3183     DataReader reader(reply);
3184
3185     QSignalSpy sslspy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
3186     connect(reply, SIGNAL(metaDataChanged()), SLOT(storeSslConfiguration()));
3187
3188     QVERIFY(waitForFinish(reply) == Success);
3189
3190     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3191     QCOMPARE(reader.data, reference.readAll());
3192
3193     QCOMPARE(sslspy.count(), 1);
3194
3195     QVERIFY(!storedSslConfiguration.isNull());
3196     QVERIFY(!reply->sslConfiguration().isNull());
3197 }
3198
3199 void tst_QNetworkReply::ioGetFromHttpsWithSslHandshakeError()
3200 {
3201     QFile reference(testDataDir + "/rfc3252.txt");
3202     QVERIFY(reference.open(QIODevice::ReadOnly));
3203
3204     QNetworkRequest request(QUrl("https://" + QtNetworkSettings::serverName() + ":80"));
3205
3206     QNetworkReplyPtr reply(manager.get(request));
3207     reply->ignoreSslErrors();
3208     DataReader reader(reply);
3209
3210     QSignalSpy sslspy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
3211     connect(reply, SIGNAL(metaDataChanged()), SLOT(storeSslConfiguration()));
3212
3213     QVERIFY(waitForFinish(reply) == Failure);
3214
3215     QCOMPARE(reply->error(), QNetworkReply::SslHandshakeFailedError);
3216     QCOMPARE(sslspy.count(), 0);
3217 }
3218 #endif
3219
3220 void tst_QNetworkReply::ioGetFromHttpBrokenServer_data()
3221 {
3222     QTest::addColumn<QByteArray>("dataToSend");
3223     QTest::addColumn<bool>("doDisconnect");
3224
3225     QTest::newRow("no-newline") << QByteArray("Hello World") << false;
3226
3227     // these are OK now, we just eat the lonely newlines
3228     //QTest::newRow("just-newline") << QByteArray("\r\n") << false;
3229     //QTest::newRow("just-2newline") << QByteArray("\r\n\r\n") << false;
3230
3231     QTest::newRow("with-newlines") << QByteArray("Long first line\r\nLong second line") << false;
3232     QTest::newRow("with-newlines2") << QByteArray("\r\nSecond line") << false;
3233     QTest::newRow("with-newlines3") << QByteArray("ICY\r\nSecond line") << false;
3234     QTest::newRow("invalid-version") << QByteArray("HTTP/123 200 \r\n") << false;
3235     QTest::newRow("invalid-version2") << QByteArray("HTTP/a.\033 200 \r\n") << false;
3236     QTest::newRow("invalid-reply-code") << QByteArray("HTTP/1.0 fuu \r\n") << false;
3237
3238     QTest::newRow("empty+disconnect") << QByteArray() << true;
3239
3240     QTest::newRow("no-newline+disconnect") << QByteArray("Hello World") << true;
3241     QTest::newRow("just-newline+disconnect") << QByteArray("\r\n") << true;
3242     QTest::newRow("just-2newline+disconnect") << QByteArray("\r\n\r\n") << true;
3243     QTest::newRow("with-newlines+disconnect") << QByteArray("Long first line\r\nLong second line") << true;
3244     QTest::newRow("with-newlines2+disconnect") << QByteArray("\r\nSecond line") << true;
3245     QTest::newRow("with-newlines3+disconnect") << QByteArray("ICY\r\nSecond line") << true;
3246
3247     QTest::newRow("invalid-version+disconnect") << QByteArray("HTTP/123 200 ") << true;
3248     QTest::newRow("invalid-version2+disconnect") << QByteArray("HTTP/a.\033 200 ") << true;
3249     QTest::newRow("invalid-reply-code+disconnect") << QByteArray("HTTP/1.0 fuu ") << true;
3250
3251     QTest::newRow("immediate disconnect") << QByteArray("") << true;
3252     QTest::newRow("justHalfStatus+disconnect") << QByteArray("HTTP/1.1") << true;
3253     QTest::newRow("justStatus+disconnect") << QByteArray("HTTP/1.1 200 OK\r\n") << true;
3254     QTest::newRow("justStatusAndHalfHeaders+disconnect") << QByteArray("HTTP/1.1 200 OK\r\nContent-L") << true;
3255
3256     QTest::newRow("halfContent+disconnect") << QByteArray("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nAB") << true;
3257
3258 }
3259
3260 void tst_QNetworkReply::ioGetFromHttpBrokenServer()
3261 {
3262     QFETCH(QByteArray, dataToSend);
3263     QFETCH(bool, doDisconnect);
3264     MiniHttpServer server(dataToSend);
3265     server.doClose = doDisconnect;
3266
3267     QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
3268     QNetworkReplyPtr reply(manager.get(request));
3269     QSignalSpy spy(reply.data(), SIGNAL(error(QNetworkReply::NetworkError)));
3270
3271     QVERIFY(waitForFinish(reply) == Failure);
3272
3273     QCOMPARE(reply->url(), request.url());
3274     QCOMPARE(spy.count(), 1);
3275     QVERIFY(reply->error() != QNetworkReply::NoError);
3276 }
3277
3278 void tst_QNetworkReply::ioGetFromHttpStatus100_data()
3279 {
3280     QTest::addColumn<QByteArray>("dataToSend");
3281     QTest::addColumn<int>("statusCode");
3282     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;
3283     QTest::newRow("minimal") << QByteArray("HTTP/1.1 100 Continue\n\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200;
3284     QTest::newRow("minimal2") << QByteArray("HTTP/1.1 100 Continue\n\nHTTP/1.0 200 OK\r\n\r\n") << 200;
3285     QTest::newRow("minimal3") << QByteArray("HTTP/1.1 100 Continue\n\nHTTP/1.0 200 OK\n\n") << 200;
3286     QTest::newRow("minimal+404") << QByteArray("HTTP/1.1 100 Continue\n\nHTTP/1.0 204 No Content\r\n\r\n") << 204;
3287     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;
3288     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;
3289 }
3290
3291 void tst_QNetworkReply::ioGetFromHttpStatus100()
3292 {
3293     QFETCH(QByteArray, dataToSend);
3294     QFETCH(int, statusCode);
3295     MiniHttpServer server(dataToSend);
3296     server.doClose = true;
3297
3298     QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
3299     QNetworkReplyPtr reply(manager.get(request));
3300
3301     QVERIFY(waitForFinish(reply) == Success);
3302
3303     QCOMPARE(reply->url(), request.url());
3304     QCOMPARE(reply->error(), QNetworkReply::NoError);
3305     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), statusCode);
3306     QVERIFY(reply->rawHeader("bla").isNull());
3307 }
3308
3309 void tst_QNetworkReply::ioGetFromHttpNoHeaders_data()
3310 {
3311     QTest::addColumn<QByteArray>("dataToSend");
3312     QTest::newRow("justStatus+noheaders+disconnect") << QByteArray("HTTP/1.0 200 OK\r\n\r\n");
3313 }
3314
3315 void tst_QNetworkReply::ioGetFromHttpNoHeaders()
3316 {
3317     QFETCH(QByteArray, dataToSend);
3318     MiniHttpServer server(dataToSend);
3319     server.doClose = true;
3320
3321     QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
3322     QNetworkReplyPtr reply(manager.get(request));
3323
3324     QVERIFY(waitForFinish(reply) == Success);
3325
3326     QCOMPARE(reply->url(), request.url());
3327     QCOMPARE(reply->error(), QNetworkReply::NoError);
3328     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3329 }
3330
3331 void tst_QNetworkReply::ioGetFromHttpWithCache_data()
3332 {
3333     qRegisterMetaType<MyMemoryCache::CachedContent>();
3334     QTest::addColumn<QByteArray>("dataToSend");
3335     QTest::addColumn<QString>("body");
3336     QTest::addColumn<MyMemoryCache::CachedContent>("cachedReply");
3337     QTest::addColumn<int>("cacheMode");
3338     QTest::addColumn<QStringList>("extraHttpHeaders");
3339     QTest::addColumn<bool>("loadedFromCache");
3340     QTest::addColumn<bool>("networkUsed");
3341
3342     QByteArray reply200 =
3343             "HTTP/1.0 200\r\n"
3344             "Connection: keep-alive\r\n"
3345             "Content-Type: text/plain\r\n"
3346             "Cache-control: no-cache\r\n"
3347             "Content-length: 8\r\n"
3348             "\r\n"
3349             "Reloaded";
3350     QByteArray reply304 =
3351             "HTTP/1.0 304 Use Cache\r\n"
3352             "Connection: keep-alive\r\n"
3353             "\r\n";
3354
3355     QTest::newRow("not-cached,always-network")
3356             << reply200 << "Reloaded" << MyMemoryCache::CachedContent() << int(QNetworkRequest::AlwaysNetwork) << QStringList() << false << true;
3357     QTest::newRow("not-cached,prefer-network")
3358             << reply200 << "Reloaded" << MyMemoryCache::CachedContent() << int(QNetworkRequest::PreferNetwork) << QStringList() << false << true;
3359     QTest::newRow("not-cached,prefer-cache")
3360             << reply200 << "Reloaded" << MyMemoryCache::CachedContent() << int(QNetworkRequest::PreferCache) << QStringList() << false << true;
3361
3362     QDateTime present = QDateTime::currentDateTime().toUTC();
3363     QDateTime past = present.addSecs(-3600);
3364     QDateTime future = present.addSecs(3600);
3365     static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'";
3366
3367     QNetworkCacheMetaData::RawHeaderList rawHeaders;
3368     MyMemoryCache::CachedContent content;
3369     content.second = "Not-reloaded";
3370     content.first.setLastModified(past);
3371
3372     //
3373     // Set to expired
3374     //
3375     rawHeaders.clear();
3376     rawHeaders << QNetworkCacheMetaData::RawHeader("Date", QLocale::c().toString(past, dateFormat).toLatin1())
3377             << QNetworkCacheMetaData::RawHeader("Cache-control", "max-age=0"); // isn't used in cache loading
3378     content.first.setRawHeaders(rawHeaders);
3379     content.first.setLastModified(past);
3380     content.first.setExpirationDate(past);
3381
3382     QTest::newRow("expired,200,prefer-network")
3383             << reply200 << "Reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << false << true;
3384     QTest::newRow("expired,200,prefer-cache")
3385             << reply200 << "Reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << false << true;
3386
3387     QTest::newRow("expired,304,prefer-network")
3388             << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << true << true;
3389     QTest::newRow("expired,304,prefer-cache")
3390             << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << true;
3391
3392     //
3393     // Set to not-expired
3394     //
3395     rawHeaders.clear();
3396     rawHeaders << QNetworkCacheMetaData::RawHeader("Date", QLocale::c().toString(past, dateFormat).toLatin1())
3397             << QNetworkCacheMetaData::RawHeader("Cache-control", "max-age=7200"); // isn't used in cache loading
3398     content.first.setRawHeaders(rawHeaders);
3399     content.first.setExpirationDate(future);
3400
3401     QTest::newRow("not-expired,200,always-network")
3402             << reply200 << "Reloaded" << content << int(QNetworkRequest::AlwaysNetwork) << QStringList() << false << true;
3403     QTest::newRow("not-expired,200,prefer-network")
3404             << reply200 << "Not-reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << true << false;
3405     QTest::newRow("not-expired,200,prefer-cache")
3406             << reply200 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << false;
3407     QTest::newRow("not-expired,200,always-cache")
3408             << reply200 << "Not-reloaded" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << true << false;
3409
3410     QTest::newRow("not-expired,304,prefer-network")
3411             << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << true << false;
3412     QTest::newRow("not-expired,304,prefer-cache")
3413             << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << false;
3414     QTest::newRow("not-expired,304,always-cache")
3415             << reply304 << "Not-reloaded" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << true << false;
3416
3417     //
3418     // Set must-revalidate now
3419     //
3420     rawHeaders.clear();
3421     rawHeaders << QNetworkCacheMetaData::RawHeader("Date", QLocale::c().toString(past, dateFormat).toLatin1())
3422             << QNetworkCacheMetaData::RawHeader("Cache-control", "max-age=7200, must-revalidate"); // must-revalidate is used
3423     content.first.setRawHeaders(rawHeaders);
3424
3425     QTest::newRow("must-revalidate,200,always-network")
3426             << reply200 << "Reloaded" << content << int(QNetworkRequest::AlwaysNetwork) << QStringList() << false << true;
3427     QTest::newRow("must-revalidate,200,prefer-network")
3428             << reply200 << "Reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << false << true;
3429     QTest::newRow("must-revalidate,200,prefer-cache")
3430             << reply200 << "Reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << false << true;
3431     QTest::newRow("must-revalidate,200,always-cache")
3432             << reply200 << "" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << false << false;
3433
3434     QTest::newRow("must-revalidate,304,prefer-network")
3435             << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << true << true;
3436     QTest::newRow("must-revalidate,304,prefer-cache")
3437             << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << true;
3438     QTest::newRow("must-revalidate,304,always-cache")
3439             << reply304 << "" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << false << false;
3440
3441     //
3442     // Partial content
3443     //
3444     rawHeaders.clear();
3445     rawHeaders << QNetworkCacheMetaData::RawHeader("Date", QLocale::c().toString(past, dateFormat).toLatin1())
3446             << QNetworkCacheMetaData::RawHeader("Cache-control", "max-age=7200"); // isn't used in cache loading
3447     content.first.setRawHeaders(rawHeaders);
3448     content.first.setExpirationDate(future);
3449
3450     QByteArray reply206 =
3451             "HTTP/1.0 206\r\n"
3452             "Connection: keep-alive\r\n"
3453             "Content-Type: text/plain\r\n"
3454             "Cache-control: no-cache\r\n"
3455             "Content-Range: bytes 2-6/8\r\n"
3456             "Content-length: 4\r\n"
3457             "\r\n"
3458             "load";
3459
3460     QTest::newRow("partial,dontuse-cache")
3461             << reply206 << "load" << content << int(QNetworkRequest::PreferCache) << (QStringList() << "Range" << "bytes=2-6") << false << true;
3462 }
3463
3464 void tst_QNetworkReply::ioGetFromHttpWithCache()
3465 {
3466     QFETCH(QByteArray, dataToSend);
3467     MiniHttpServer server(dataToSend);
3468     server.doClose = false;
3469
3470     MyMemoryCache *memoryCache = new MyMemoryCache(&manager);
3471     manager.setCache(memoryCache);
3472
3473     QFETCH(MyMemoryCache::CachedContent, cachedReply);
3474     QUrl url = "http://localhost:" + QString::number(server.serverPort());
3475     cachedReply.first.setUrl(url);
3476     if (!cachedReply.second.isNull())
3477         memoryCache->cache.insert(url.toEncoded(), cachedReply);
3478
3479     QFETCH(int, cacheMode);
3480     QNetworkRequest request(url);
3481     request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, cacheMode);
3482     request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false);
3483
3484     QFETCH(QStringList, extraHttpHeaders);
3485     QStringListIterator it(extraHttpHeaders);
3486     while (it.hasNext()) {
3487         QString header = it.next();
3488         QString value = it.next();
3489         request.setRawHeader(header.toLatin1(), value.toLatin1()); // To latin1? Deal with it!
3490     }
3491
3492     QNetworkReplyPtr reply(manager.get(request));
3493
3494     QVERIFY(waitForFinish(reply) != Timeout);
3495
3496     QTEST(reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(), "loadedFromCache");
3497     QTEST(server.totalConnections > 0, "networkUsed");
3498     QFETCH(QString, body);
3499     QCOMPARE(reply->readAll().constData(), qPrintable(body));
3500 }
3501
3502 void tst_QNetworkReply::ioGetWithManyProxies_data()
3503 {
3504     QTest::addColumn<QList<QNetworkProxy> >("proxyList");
3505     QTest::addColumn<QNetworkProxy>("proxyUsed");
3506     QTest::addColumn<QString>("url");
3507     QTest::addColumn<QNetworkReply::NetworkError>("expectedError");
3508
3509     QList<QNetworkProxy> proxyList;
3510
3511     // All of the other functions test DefaultProxy
3512     // So let's test something else
3513
3514     // Simple tests that work:
3515
3516     // HTTP request with HTTP caching proxy
3517     proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129);
3518     QTest::newRow("http-on-http")
3519         << proxyList << proxyList.at(0)
3520         << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3521         << QNetworkReply::NoError;
3522
3523     // HTTP request with HTTP transparent proxy
3524     proxyList.clear();
3525     proxyList << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3129);
3526     QTest::newRow("http-on-http2")
3527         << proxyList << proxyList.at(0)
3528         << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3529         << QNetworkReply::NoError;
3530
3531     // HTTP request with SOCKS transparent proxy
3532     proxyList.clear();
3533     proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081);
3534     QTest::newRow("http-on-socks")
3535         << proxyList << proxyList.at(0)
3536         << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3537         << QNetworkReply::NoError;
3538
3539     // FTP request with FTP caching proxy
3540     proxyList.clear();
3541     proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121);
3542     QTest::newRow("ftp-on-ftp")
3543         << proxyList << proxyList.at(0)
3544         << "ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3545         << QNetworkReply::NoError;
3546
3547     // The following test doesn't work because QFtp is too limited
3548     // It can only talk to its own kind of proxies
3549
3550     // FTP request with SOCKSv5 transparent proxy
3551     proxyList.clear();
3552     proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081);
3553     QTest::newRow("ftp-on-socks")
3554         << proxyList << proxyList.at(0)
3555         << "ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3556         << QNetworkReply::NoError;
3557
3558 #ifndef QT_NO_SSL
3559     // HTTPS with HTTP transparent proxy
3560     proxyList.clear();
3561     proxyList << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3129);
3562     QTest::newRow("https-on-http")
3563         << proxyList << proxyList.at(0)
3564         << "https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3565         << QNetworkReply::NoError;
3566
3567     // HTTPS request with SOCKS transparent proxy
3568     proxyList.clear();
3569     proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081);
3570     QTest::newRow("https-on-socks")
3571         << proxyList << proxyList.at(0)
3572         << "https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3573         << QNetworkReply::NoError;
3574 #endif
3575
3576     // Tests that fail:
3577
3578     // HTTP request with FTP caching proxy
3579     proxyList.clear();
3580     proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121);
3581     QTest::newRow("http-on-ftp")
3582         << proxyList << QNetworkProxy()
3583         << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3584         << QNetworkReply::ProxyNotFoundError;
3585
3586     // FTP request with HTTP caching proxy
3587     proxyList.clear();
3588     proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129);
3589     QTest::newRow("ftp-on-http")
3590         << proxyList << QNetworkProxy()
3591         << "ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3592         << QNetworkReply::ProxyNotFoundError;
3593
3594     // FTP request with HTTP caching proxies
3595     proxyList.clear();
3596     proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129)
3597               << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3130);
3598     QTest::newRow("ftp-on-multiple-http")
3599         << proxyList << QNetworkProxy()
3600         << "ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3601         << QNetworkReply::ProxyNotFoundError;
3602
3603 #ifndef QT_NO_SSL
3604     // HTTPS with HTTP caching proxy
3605     proxyList.clear();
3606     proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129);
3607     QTest::newRow("https-on-httptransparent")
3608         << proxyList << QNetworkProxy()
3609         << "https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3610         << QNetworkReply::ProxyNotFoundError;
3611
3612     // HTTPS with FTP caching proxy
3613     proxyList.clear();
3614     proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121);
3615     QTest::newRow("https-on-ftp")
3616         << proxyList << QNetworkProxy()
3617         << "https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3618         << QNetworkReply::ProxyNotFoundError;
3619 #endif
3620
3621     // Complex requests:
3622
3623     // HTTP request with more than one HTTP proxy
3624     proxyList.clear();
3625     proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129)
3626               << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3130);
3627     QTest::newRow("http-on-multiple-http")
3628         << proxyList << proxyList.at(0)
3629         << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3630         << QNetworkReply::NoError;
3631
3632     // HTTP request with HTTP + SOCKS
3633     proxyList.clear();
3634     proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129)
3635               << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081);
3636     QTest::newRow("http-on-http+socks")
3637         << proxyList << proxyList.at(0)
3638         << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3639         << QNetworkReply::NoError;
3640
3641     // HTTP request with FTP + HTTP + SOCKS
3642     proxyList.clear();
3643     proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121)
3644               << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129)
3645               << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081);
3646     QTest::newRow("http-on-ftp+http+socks")
3647         << proxyList << proxyList.at(1) // second proxy should be used
3648         << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3649         << QNetworkReply::NoError;
3650
3651     // HTTP request with NoProxy + HTTP
3652     proxyList.clear();
3653     proxyList << QNetworkProxy(QNetworkProxy::NoProxy)
3654               << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129);
3655     QTest::newRow("http-on-noproxy+http")
3656         << proxyList << proxyList.at(0)
3657         << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3658         << QNetworkReply::NoError;
3659
3660     // HTTP request with FTP + NoProxy
3661     proxyList.clear();
3662     proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121)
3663               << QNetworkProxy(QNetworkProxy::NoProxy);
3664     QTest::newRow("http-on-ftp+noproxy")
3665         << proxyList << proxyList.at(1) // second proxy should be used
3666         << "http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3667         << QNetworkReply::NoError;
3668
3669     // FTP request with HTTP Caching + FTP
3670     proxyList.clear();
3671     proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129)
3672               << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121);
3673     QTest::newRow("ftp-on-http+ftp")
3674         << proxyList << proxyList.at(1) // second proxy should be used
3675         << "ftp://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3676         << QNetworkReply::NoError;
3677
3678 #ifndef QT_NO_SSL
3679     // HTTPS request with HTTP Caching + HTTP transparent
3680     proxyList.clear();
3681     proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129)
3682               << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3129);
3683     QTest::newRow("https-on-httpcaching+http")
3684         << proxyList << proxyList.at(1) // second proxy should be used
3685         << "https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3686         << QNetworkReply::NoError;
3687
3688     // HTTPS request with FTP + HTTP C + HTTP T
3689     proxyList.clear();
3690     proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121)
3691               << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129)
3692               << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3129);
3693     QTest::newRow("https-on-ftp+httpcaching+http")
3694         << proxyList << proxyList.at(2) // skip the first two
3695         << "https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"
3696         << QNetworkReply::NoError;
3697 #endif
3698 }
3699
3700 void tst_QNetworkReply::ioGetWithManyProxies()
3701 {
3702     // Test proxy factories
3703
3704     QFile reference(testDataDir + "/rfc3252.txt");
3705     QVERIFY(reference.open(QIODevice::ReadOnly));
3706
3707     // set the proxy factory:
3708     QFETCH(QList<QNetworkProxy>, proxyList);
3709     MyProxyFactory *proxyFactory = new MyProxyFactory;
3710     proxyFactory->toReturn = proxyList;
3711     manager.setProxyFactory(proxyFactory);
3712
3713     QFETCH(QString, url);
3714     QUrl theUrl(url);
3715     QNetworkRequest request(theUrl);
3716     QNetworkReplyPtr reply(manager.get(request));
3717     DataReader reader(reply);
3718
3719     QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3720     connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
3721             SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3722 #ifndef QT_NO_SSL
3723     connect(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
3724             SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
3725 #endif
3726
3727     QVERIFY(waitForFinish(reply) != Timeout);
3728
3729     manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
3730                        this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3731 #ifndef QT_NO_SSL
3732     manager.disconnect(SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
3733                        this, SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
3734 #endif
3735
3736     QFETCH(QNetworkReply::NetworkError, expectedError);
3737     QEXPECT_FAIL("ftp-on-socks", "QFtp is too limited and won't accept non-FTP proxies", Abort);
3738     QCOMPARE(reply->error(), expectedError);
3739
3740     // Verify that the factory was called properly
3741     QCOMPARE(proxyFactory->callCount, 1);
3742     QCOMPARE(proxyFactory->lastQuery, QNetworkProxyQuery(theUrl));
3743
3744     if (expectedError == QNetworkReply::NoError) {
3745         // request succeeded
3746         QCOMPARE(reader.data, reference.readAll());
3747
3748         // now verify that the proxies worked:
3749         QFETCH(QNetworkProxy, proxyUsed);
3750         if (proxyUsed.type() == QNetworkProxy::NoProxy) {
3751             QCOMPARE(authspy.count(), 0);
3752         } else {
3753             if (QByteArray(QTest::currentDataTag()).startsWith("ftp-"))
3754                 return;         // No authentication with current FTP or with FTP proxies
3755             QCOMPARE(authspy.count(), 1);
3756             QCOMPARE(qvariant_cast<QNetworkProxy>(authspy.at(0).at(0)), proxyUsed);
3757         }
3758     } else {
3759         // request failed
3760         QCOMPARE(authspy.count(), 0);
3761     }
3762 }
3763
3764 void tst_QNetworkReply::ioPutToFileFromFile_data()
3765 {
3766     QTest::addColumn<QString>("fileName");
3767
3768     QTest::newRow("empty") << (testDataDir + "/empty");
3769     QTest::newRow("real-file") << (testDataDir + "/rfc3252.txt");
3770     QTest::newRow("resource") << ":/resource";
3771     QTest::newRow("search-path") << "testdata:/rfc3252.txt";
3772 }
3773
3774 void tst_QNetworkReply::ioPutToFileFromFile()
3775 {
3776     QFETCH(QString, fileName);
3777     QFile sourceFile(fileName);
3778     QFile targetFile(testFileName);
3779
3780     QVERIFY(sourceFile.open(QIODevice::ReadOnly));
3781
3782     QUrl url = QUrl::fromLocalFile(targetFile.fileName());
3783     QNetworkRequest request(url);
3784     QNetworkReplyPtr reply(manager.put(request, &sourceFile));
3785
3786     QVERIFY(waitForFinish(reply) == Success);
3787
3788     QCOMPARE(reply->url(), url);
3789     QCOMPARE(reply->error(), QNetworkReply::NoError);
3790     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
3791     QVERIFY(reply->readAll().isEmpty());
3792
3793     QVERIFY(sourceFile.atEnd());
3794     sourceFile.seek(0);         // reset it to the beginning
3795
3796     QVERIFY(targetFile.open(QIODevice::ReadOnly));
3797     QCOMPARE(targetFile.size(), sourceFile.size());
3798     QCOMPARE(targetFile.readAll(), sourceFile.readAll());
3799 }
3800
3801 void tst_QNetworkReply::ioPutToFileFromSocket_data()
3802 {
3803     putToFile_data();
3804 }
3805
3806 void tst_QNetworkReply::ioPutToFileFromSocket()
3807 {
3808     QFile file(testFileName);
3809
3810     QUrl url = QUrl::fromLocalFile(file.fileName());
3811     QNetworkRequest request(url);
3812
3813     QFETCH(QByteArray, data);
3814     SocketPair socketpair;
3815     socketpair.create();
3816     QVERIFY(socketpair.endPoints[0] && socketpair.endPoints[1]);
3817
3818     socketpair.endPoints[0]->write(data);
3819     QNetworkReplyPtr reply(manager.put(QNetworkRequest(url), socketpair.endPoints[1]));
3820     socketpair.endPoints[0]->close();
3821
3822     QVERIFY(waitForFinish(reply) == Success);
3823     QCOMPARE(reply->error(), QNetworkReply::NoError);
3824
3825     QCOMPARE(reply->url(), url);
3826     QCOMPARE(reply->error(), QNetworkReply::NoError);
3827     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
3828     QVERIFY(reply->readAll().isEmpty());
3829
3830     QVERIFY(file.open(QIODevice::ReadOnly));
3831     QCOMPARE(file.size(), qint64(data.size()));
3832     QByteArray contents = file.readAll();
3833     QCOMPARE(contents, data);
3834 }
3835
3836 void tst_QNetworkReply::ioPutToFileFromLocalSocket_data()
3837 {
3838     putToFile_data();
3839 }
3840
3841 void tst_QNetworkReply::ioPutToFileFromLocalSocket()
3842 {
3843     QString socketname = "networkreplytest";
3844     QLocalServer server;
3845     if (!server.listen(socketname)) {
3846         QLocalServer::removeServer(socketname);
3847         QVERIFY(server.listen(socketname));
3848     }
3849     QLocalSocket active;
3850     active.connectToServer(socketname);
3851     QVERIFY2(server.waitForNewConnection(10), server.errorString().toLatin1().constData());
3852     QVERIFY2(active.waitForConnected(10), active.errorString().toLatin1().constData());
3853     QVERIFY2(server.hasPendingConnections(), server.errorString().toLatin1().constData());
3854     QLocalSocket *passive = server.nextPendingConnection();
3855
3856     QFile file(testFileName);
3857     QUrl url = QUrl::fromLocalFile(file.fileName());
3858     QNetworkRequest request(url);
3859
3860     QFETCH(QByteArray, data);
3861     active.write(data);
3862     active.close();
3863     QNetworkReplyPtr reply(manager.put(QNetworkRequest(url), passive));
3864     passive->setParent(reply.data());
3865
3866 #ifdef Q_OS_WIN
3867     if (!data.isEmpty())
3868         QEXPECT_FAIL("", "QTBUG-18385", Abort);
3869 #endif
3870     QVERIFY(waitForFinish(reply) == Success);
3871     QCOMPARE(reply->error(), QNetworkReply::NoError);
3872
3873     QCOMPARE(reply->url(), url);
3874     QCOMPARE(reply->error(), QNetworkReply::NoError);
3875     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
3876     QVERIFY(reply->readAll().isEmpty());
3877
3878     QVERIFY(file.open(QIODevice::ReadOnly));
3879     QCOMPARE(file.size(), qint64(data.size()));
3880     QByteArray contents = file.readAll();
3881     QCOMPARE(contents, data);
3882 }
3883
3884 // Currently no stdin/out supported for Windows CE.
3885 #ifndef QT_NO_PROCESS
3886 void tst_QNetworkReply::ioPutToFileFromProcess_data()
3887 {
3888     putToFile_data();
3889 }
3890
3891 void tst_QNetworkReply::ioPutToFileFromProcess()
3892 {
3893 #if defined(Q_OS_WINCE)
3894     QSKIP("Currently no stdin/out supported for Windows CE");
3895 #else
3896 #ifdef Q_OS_WIN
3897     if (qstrcmp(QTest::currentDataTag(), "small") == 0)
3898         QSKIP("When passing a CR-LF-LF sequence through Windows stdio, it gets converted, "
3899               "so this test fails. Disabled on Windows");
3900 #endif
3901
3902     QFile file(testFileName);
3903
3904     QUrl url = QUrl::fromLocalFile(file.fileName());
3905     QNetworkRequest request(url);
3906
3907     QFETCH(QByteArray, data);
3908     QProcess process;
3909     QString echoExe = echoProcessDir + "/echo";
3910     process.start(echoExe, QStringList("all"));
3911     QVERIFY2(process.waitForStarted(), qPrintable(
3912         QString::fromLatin1("Could not start %1: %2").arg(echoExe, process.errorString())));
3913     process.write(data);
3914     process.closeWriteChannel();
3915
3916     QNetworkReplyPtr reply(manager.put(QNetworkRequest(url), &process));
3917
3918     QVERIFY(waitForFinish(reply) == Success);
3919
3920     QCOMPARE(reply->url(), url);
3921     QCOMPARE(reply->error(), QNetworkReply::NoError);
3922     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
3923     QVERIFY(reply->readAll().isEmpty());
3924
3925     QVERIFY(file.open(QIODevice::ReadOnly));
3926     QCOMPARE(file.size(), qint64(data.size()));
3927     QByteArray contents = file.readAll();
3928     QCOMPARE(contents, data);
3929 #endif
3930 }
3931 #endif
3932
3933 void tst_QNetworkReply::ioPutToFtpFromFile_data()
3934 {
3935     ioPutToFileFromFile_data();
3936 }
3937
3938 void tst_QNetworkReply::ioPutToFtpFromFile()
3939 {
3940     QFETCH(QString, fileName);
3941     QFile sourceFile(fileName);
3942     QVERIFY(sourceFile.open(QIODevice::ReadOnly));
3943
3944     QUrl url("ftp://" + QtNetworkSettings::serverName());
3945     url.setPath(QString("/qtest/upload/qnetworkaccess-ioPutToFtpFromFile-%1-%2")
3946                 .arg(QTest::currentDataTag())
3947                 .arg(uniqueExtension));
3948
3949     QNetworkRequest request(url);
3950     QNetworkReplyPtr reply(manager.put(request, &sourceFile));
3951
3952     QVERIFY(waitForFinish(reply) == Success);
3953
3954     QCOMPARE(reply->url(), url);
3955     QCOMPARE(reply->error(), QNetworkReply::NoError);
3956     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
3957     QVERIFY(reply->readAll().isEmpty());
3958
3959     QVERIFY(sourceFile.atEnd());
3960     sourceFile.seek(0);         // reset it to the beginning
3961
3962     // download the file again from FTP to make sure it was uploaded
3963     // correctly
3964     QNetworkAccessManager qnam;
3965     QNetworkRequest req(url);
3966     QNetworkReply *r = qnam.get(req);
3967
3968     QObject::connect(r, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
3969     QTestEventLoop::instance().enterLoop(3);
3970     QObject::disconnect(r, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
3971
3972     QByteArray uploaded = r->readAll();
3973     QCOMPARE(qint64(uploaded.size()), sourceFile.size());
3974     QCOMPARE(uploaded, sourceFile.readAll());
3975
3976     r->close();
3977     QObject::connect(r, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
3978     QTestEventLoop::instance().enterLoop(10);
3979     QObject::disconnect(r, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
3980 }
3981
3982 void tst_QNetworkReply::ioPutToHttpFromFile_data()
3983 {
3984     ioPutToFileFromFile_data();
3985 }
3986
3987 void tst_QNetworkReply::ioPutToHttpFromFile()
3988 {
3989     QFETCH(QString, fileName);
3990     QFile sourceFile(fileName);
3991     QVERIFY(sourceFile.open(QIODevice::ReadOnly));
3992
3993     QUrl url("http://" + QtNetworkSettings::serverName());
3994     url.setPath(QString("/dav/qnetworkaccess-ioPutToHttpFromFile-%1-%2")
3995                 .arg(QTest::currentDataTag())
3996                 .arg(uniqueExtension));
3997
3998     QNetworkRequest request(url);
3999     QNetworkReplyPtr reply(manager.put(request, &sourceFile));
4000
4001     QVERIFY(waitForFinish(reply) == Success);
4002
4003     QCOMPARE(reply->url(), url);
4004     QCOMPARE(reply->error(), QNetworkReply::NoError);
4005
4006     // verify that the HTTP status code is 201 Created
4007     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 201);
4008
4009     QVERIFY(sourceFile.atEnd());
4010     sourceFile.seek(0);         // reset it to the beginning
4011
4012     // download the file again from HTTP to make sure it was uploaded
4013     // correctly
4014     reply.reset(manager.get(request));
4015
4016     QVERIFY(waitForFinish(reply) == Success);
4017
4018     QCOMPARE(reply->url(), url);
4019     QCOMPARE(reply->error(), QNetworkReply::NoError);
4020     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
4021
4022     QCOMPARE(reply->readAll(), sourceFile.readAll());
4023 }
4024
4025 void tst_QNetworkReply::ioPostToHttpFromFile_data()
4026 {
4027     ioPutToFileFromFile_data();
4028 }
4029
4030 void tst_QNetworkReply::ioPostToHttpFromFile()
4031 {
4032     QFETCH(QString, fileName);
4033     QFile sourceFile(fileName);
4034     QVERIFY(sourceFile.open(QIODevice::ReadOnly));
4035
4036     QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi");
4037     QNetworkRequest request(url);
4038     request.setRawHeader("Content-Type", "application/octet-stream");
4039
4040     QNetworkReplyPtr reply(manager.post(request, &sourceFile));
4041
4042     QVERIFY(waitForFinish(reply) == Success);
4043
4044     QCOMPARE(reply->url(), url);
4045     QCOMPARE(reply->error(), QNetworkReply::NoError);
4046
4047     // verify that the HTTP status code is 200 Ok
4048     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
4049
4050     QVERIFY(sourceFile.atEnd());
4051     sourceFile.seek(0);         // reset it to the beginning
4052
4053     QCOMPARE(reply->readAll().trimmed(), md5sum(sourceFile.readAll()).toHex());
4054 }
4055
4056 void tst_QNetworkReply::ioPostToHttpFromSocket_data()
4057 {
4058     QTest::addColumn<QByteArray>("data");
4059     QTest::addColumn<QByteArray>("md5sum");
4060     QTest::addColumn<QUrl>("url");
4061     QTest::addColumn<QNetworkProxy>("proxy");
4062     QTest::addColumn<int>("authenticationRequiredCount");
4063     QTest::addColumn<int>("proxyAuthenticationRequiredCount");
4064
4065     for (int i = 0; i < proxies.count(); ++i)
4066         for (int auth = 0; auth < 2; ++auth) {
4067             QUrl url;
4068             if (auth)
4069                 url = "http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi";
4070             else
4071                 url = "http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi";
4072
4073             QNetworkProxy proxy = proxies.at(i).proxy;
4074             QByteArray testsuffix = QByteArray(auth ? "+auth" : "") + proxies.at(i).tag;
4075             int proxyauthcount = proxies.at(i).requiresAuthentication;
4076
4077             QByteArray data;
4078             data = "";
4079             QTest::newRow("empty" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount;
4080
4081             data = "This is a normal message.";
4082             QTest::newRow("generic" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount;
4083
4084             data = "This is a message to show that Qt rocks!\r\n\n";
4085             QTest::newRow("small" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount;
4086
4087             data = QByteArray("abcd\0\1\2\abcd",12);
4088             QTest::newRow("with-nul" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount;
4089
4090             data = QByteArray(4097, '\4');
4091             QTest::newRow("4k+1" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount;
4092
4093             data = QByteArray(128*1024+1, '\177');
4094             QTest::newRow("128k+1" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount;
4095         }
4096 }
4097
4098 void tst_QNetworkReply::ioPostToHttpFromSocket()
4099 {
4100     QFETCH(QByteArray, data);
4101     QFETCH(QUrl, url);
4102     QFETCH(QNetworkProxy, proxy);
4103     SocketPair socketpair;
4104     socketpair.create();
4105     QVERIFY(socketpair.endPoints[0] && socketpair.endPoints[1]);
4106
4107     socketpair.endPoints[0]->write(data);
4108
4109     QNetworkRequest request(url);
4110     request.setRawHeader("Content-Type", "application/octet-stream");
4111
4112     manager.setProxy(proxy);
4113     QNetworkReplyPtr reply(manager.post(request, socketpair.endPoints[1]));
4114     socketpair.endPoints[0]->close();
4115
4116     connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
4117             SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
4118     connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
4119             SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4120
4121     QSignalSpy authenticationRequiredSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4122     QSignalSpy proxyAuthenticationRequiredSpy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
4123
4124     QVERIFY(waitForFinish(reply) == Success);
4125
4126     disconnect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
4127                this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
4128     disconnect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
4129                this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4130     QCOMPARE(reply->error(), QNetworkReply::NoError);
4131
4132     QCOMPARE(reply->url(), url);
4133     QCOMPARE(reply->error(), QNetworkReply::NoError);
4134     // verify that the HTTP status code is 200 Ok
4135     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
4136
4137     QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
4138
4139     QTEST(authenticationRequiredSpy.count(), "authenticationRequiredCount");
4140     QTEST(proxyAuthenticationRequiredSpy.count(), "proxyAuthenticationRequiredCount");
4141 }
4142
4143 void tst_QNetworkReply::ioPostToHttpFromSocketSynchronous_data()
4144 {
4145     QTest::addColumn<QByteArray>("data");
4146     QTest::addColumn<QByteArray>("md5sum");
4147
4148     QByteArray data;
4149     data = "";
4150     QTest::newRow("empty") << data << md5sum(data);
4151
4152     data = "This is a normal message.";
4153     QTest::newRow("generic") << data << md5sum(data);
4154
4155     data = "This is a message to show that Qt rocks!\r\n\n";
4156     QTest::newRow("small") << data << md5sum(data);
4157
4158     data = QByteArray("abcd\0\1\2\abcd",12);
4159     QTest::newRow("with-nul") << data << md5sum(data);
4160
4161     data = QByteArray(4097, '\4');
4162     QTest::newRow("4k+1") << data << md5sum(data);
4163
4164     data = QByteArray(128*1024+1, '\177');
4165     QTest::newRow("128k+1") << data << md5sum(data);
4166
4167     data = QByteArray(2*1024*1024+1, '\177');
4168     QTest::newRow("2MB+1") << data << md5sum(data);
4169 }
4170
4171 void tst_QNetworkReply::ioPostToHttpFromSocketSynchronous()
4172 {
4173     QFETCH(QByteArray, data);
4174
4175     SocketPair socketpair;
4176     QVERIFY(socketpair.create());
4177     QVERIFY(socketpair.endPoints[0] && socketpair.endPoints[1]);
4178     socketpair.endPoints[0]->write(data);
4179     socketpair.endPoints[0]->waitForBytesWritten(5000);
4180     // ### for 4.8: make the socket pair unbuffered, to not read everything in one go in QNetworkReplyImplPrivate::setup()
4181     QTestEventLoop::instance().enterLoop(3);
4182
4183     QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi");
4184     QNetworkRequest request(url);
4185     request.setRawHeader("Content-Type", "application/octet-stream");
4186     request.setAttribute(
4187             QNetworkRequest::SynchronousRequestAttribute,
4188             true);
4189
4190     QNetworkReplyPtr reply(manager.post(request, socketpair.endPoints[1]));
4191     QVERIFY(reply->isFinished());
4192     socketpair.endPoints[0]->close();
4193
4194     QCOMPARE(reply->error(), QNetworkReply::NoError);
4195
4196     QCOMPARE(reply->url(), url);
4197     QCOMPARE(reply->error(), QNetworkReply::NoError);
4198     // verify that the HTTP status code is 200 Ok
4199     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
4200
4201     QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
4202 }
4203
4204 // this tests checks if rewinding the POST-data to some place in the middle
4205 // worked.
4206 void tst_QNetworkReply::ioPostToHttpFromMiddleOfFileToEnd()
4207 {
4208     QFile sourceFile(testDataDir + "/rfc3252.txt");
4209     QVERIFY(sourceFile.open(QIODevice::ReadOnly));
4210     // seeking to the middle
4211     sourceFile.seek(sourceFile.size() / 2);
4212
4213     QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi";
4214     QNetworkRequest request(url);
4215     request.setRawHeader("Content-Type", "application/octet-stream");
4216     QNetworkReplyPtr reply(manager.post(request, &sourceFile));
4217
4218     connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
4219             SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4220
4221     QVERIFY(waitForFinish(reply) == Success);
4222
4223     disconnect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
4224                this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4225
4226     // compare half data
4227     sourceFile.seek(sourceFile.size() / 2);
4228     QByteArray data = sourceFile.readAll();
4229     QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
4230 }
4231
4232 void tst_QNetworkReply::ioPostToHttpFromMiddleOfFileFiveBytes()
4233 {
4234     QFile sourceFile(testDataDir + "/rfc3252.txt");
4235     QVERIFY(sourceFile.open(QIODevice::ReadOnly));
4236     // seeking to the middle
4237     sourceFile.seek(sourceFile.size() / 2);
4238
4239     QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi";
4240     QNetworkRequest request(url);
4241     request.setRawHeader("Content-Type", "application/octet-stream");
4242     // only send 5 bytes
4243     request.setHeader(QNetworkRequest::ContentLengthHeader, 5);
4244     QVERIFY(request.header(QNetworkRequest::ContentLengthHeader).isValid());
4245     QNetworkReplyPtr reply(manager.post(request, &sourceFile));
4246
4247     connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
4248             SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4249
4250     QVERIFY(waitForFinish(reply) == Success);
4251
4252     disconnect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
4253                this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4254
4255     // compare half data
4256     sourceFile.seek(sourceFile.size() / 2);
4257     QByteArray data = sourceFile.read(5);
4258     QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
4259 }
4260
4261 void tst_QNetworkReply::ioPostToHttpFromMiddleOfQBufferFiveBytes()
4262 {
4263     // test needed since a QBuffer goes with a different codepath than the QFile
4264     // tested in ioPostToHttpFromMiddleOfFileFiveBytes
4265     QBuffer uploadBuffer;
4266     uploadBuffer.open(QIODevice::ReadWrite);
4267     uploadBuffer.write("1234567890");
4268     uploadBuffer.seek(5);
4269
4270     QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi";
4271     QNetworkRequest request(url);
4272     request.setRawHeader("Content-Type", "application/octet-stream");
4273     QNetworkReplyPtr reply(manager.post(request, &uploadBuffer));
4274
4275     connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
4276             SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4277
4278     QVERIFY(waitForFinish(reply) == Success);
4279
4280     disconnect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
4281                this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4282
4283     // compare half data
4284     uploadBuffer.seek(5);
4285     QByteArray data = uploadBuffer.read(5);
4286     QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
4287 }
4288
4289
4290 void tst_QNetworkReply::ioPostToHttpNoBufferFlag()
4291 {
4292     QByteArray data = QByteArray("daaaaaaataaaaaaa");
4293     // create a sequential QIODevice by feeding the data into a local TCP server
4294     SocketPair socketpair;
4295     socketpair.create();
4296     QVERIFY(socketpair.endPoints[0] && socketpair.endPoints[1]);
4297     socketpair.endPoints[0]->write(data);
4298
4299     QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi";
4300     QNetworkRequest request(url);
4301     request.setRawHeader("Content-Type", "application/octet-stream");
4302     // disallow buffering
4303     request.setAttribute(QNetworkRequest::DoNotBufferUploadDataAttribute, true);
4304     request.setHeader(QNetworkRequest::ContentLengthHeader, data.size());
4305     QNetworkReplyPtr reply(manager.post(request, socketpair.endPoints[1]));
4306     socketpair.endPoints[0]->close();
4307
4308     connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
4309             SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4310
4311     QVERIFY(waitForFinish(reply) == Failure);
4312
4313     disconnect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
4314                this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4315
4316     // verify: error code is QNetworkReply::ContentReSendError
4317     QCOMPARE(reply->error(), QNetworkReply::ContentReSendError);
4318 }
4319
4320 #ifndef QT_NO_SSL
4321 class SslServer : public QTcpServer {
4322     Q_OBJECT
4323 public:
4324     SslServer() : socket(0) {};
4325     void incomingConnection(qintptr socketDescriptor) {
4326         QSslSocket *serverSocket = new QSslSocket;
4327         serverSocket->setParent(this);
4328
4329         if (serverSocket->setSocketDescriptor(socketDescriptor)) {
4330             QString testDataDir = QFileInfo(QFINDTESTDATA("rfc3252.txt")).absolutePath();
4331             if (testDataDir.isEmpty())
4332                 testDataDir = QCoreApplication::applicationDirPath();
4333
4334             connect(serverSocket, SIGNAL(encrypted()), this, SLOT(encryptedSlot()));
4335             connect(serverSocket, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
4336             serverSocket->setProtocol(QSsl::AnyProtocol);
4337             connect(serverSocket, SIGNAL(sslErrors(const QList<QSslError>&)), serverSocket, SLOT(ignoreSslErrors()));
4338             serverSocket->setLocalCertificate(testDataDir + "/certs/server.pem");
4339             serverSocket->setPrivateKey(testDataDir + "/certs/server.key");
4340             serverSocket->startServerEncryption();
4341         } else {
4342             delete serverSocket;
4343         }
4344     }
4345 signals:
4346     void newEncryptedConnection();
4347 public slots:
4348     void encryptedSlot() {
4349         socket = (QSslSocket*) sender();
4350         emit newEncryptedConnection();
4351     }
4352     void readyReadSlot() {
4353         // for the incoming sockets, not the server socket
4354         //qDebug() << static_cast<QSslSocket*>(sender())->bytesAvailable() << static_cast<QSslSocket*>(sender())->encryptedBytesAvailable();
4355     }
4356
4357 public:
4358     QSslSocket *socket;
4359 };
4360
4361 // very similar to ioPostToHttpUploadProgress but for SSL
4362 void tst_QNetworkReply::ioPostToHttpsUploadProgress()
4363 {
4364     //QFile sourceFile(testDataDir + "/bigfile");
4365     //QVERIFY(sourceFile.open(QIODevice::ReadOnly));
4366     qint64 wantedSize = 2*1024*1024; // 2 MB
4367     QByteArray sourceFile;
4368     // And in the case of SSL, the compression can fool us and let the
4369     // server send the data much faster than expected.
4370     // So better provide random data that cannot be compressed.
4371     for (int i = 0; i < wantedSize; ++i)
4372         sourceFile += (char)qrand();
4373
4374     // emulate a minimal https server
4375     SslServer server;
4376     server.listen(QHostAddress(QHostAddress::LocalHost), 0);
4377
4378     // create the request
4379     QUrl url = QUrl(QString("https://127.0.0.1:%1/").arg(server.serverPort()));
4380     QNetworkRequest request(url);
4381
4382     request.setRawHeader("Content-Type", "application/octet-stream");
4383     QNetworkReplyPtr reply(manager.post(request, sourceFile));
4384
4385     QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64)));
4386     connect(&server, SIGNAL(newEncryptedConnection()), &QTestEventLoop::instance(), SLOT(exitLoop()));
4387     connect(reply, SIGNAL(sslErrors(const QList<QSslError>&)), reply.data(), SLOT(ignoreSslErrors()));
4388
4389     // get the request started and the incoming socket connected
4390     QTestEventLoop::instance().enterLoop(10);
4391     QVERIFY(!QTestEventLoop::instance().timeout());
4392     QTcpSocket *incomingSocket = server.socket;
4393     QVERIFY(incomingSocket);
4394     disconnect(&server, SIGNAL(newEncryptedConnection()), &QTestEventLoop::instance(), SLOT(exitLoop()));
4395
4396
4397     incomingSocket->setReadBufferSize(1*1024);
4398     QTestEventLoop::instance().enterLoop(2);
4399     // some progress should have been made
4400     QVERIFY(!spy.isEmpty());
4401     QList<QVariant> args = spy.last();
4402     QVERIFY(args.at(0).toLongLong() > 0);
4403     // but not everything!
4404     QVERIFY(args.at(0).toLongLong() != sourceFile.size());
4405
4406     // set the read buffer to unlimited
4407     incomingSocket->setReadBufferSize(0);
4408     QTestEventLoop::instance().enterLoop(10);
4409     // progress should be finished
4410     QVERIFY(!spy.isEmpty());
4411     QList<QVariant> args3 = spy.last();
4412     QCOMPARE(args3.at(0).toLongLong(), args3.at(1).toLongLong());
4413     QCOMPARE(args3.at(0).toLongLong(), qint64(sourceFile.size()));
4414
4415     // after sending this, the QNAM should emit finished()
4416     incomingSocket->write("HTTP/1.0 200 OK\r\n");
4417     incomingSocket->write("Content-Length: 0\r\n");
4418     incomingSocket->write("\r\n");
4419
4420     QVERIFY(waitForFinish(reply) == Success);
4421
4422     incomingSocket->close();
4423     server.close();
4424 }
4425 #endif
4426
4427 void tst_QNetworkReply::ioGetFromBuiltinHttp_data()
4428 {
4429     QTest::addColumn<bool>("https");
4430     QTest::addColumn<int>("bufferSize");
4431     QTest::newRow("http+unlimited") << false << 0;
4432     QTest::newRow("http+limited") << false << 4096;
4433 #ifndef QT_NO_SSL
4434     QTest::newRow("https+unlimited") << true << 0;
4435     QTest::newRow("https+limited") << true << 4096;
4436 #endif
4437 }
4438
4439 void tst_QNetworkReply::ioGetFromBuiltinHttp()
4440 {
4441     QSKIP("Limiting is broken right now, check QTBUG-15065");
4442     QFETCH(bool, https);
4443     QFETCH(int, bufferSize);
4444
4445     QByteArray testData;
4446     // Make the data big enough so that it can fill the kernel buffer
4447     // (which seems to hold 202 KB here)
4448     const int wantedSize = 1200 * 1000;
4449     testData.reserve(wantedSize);
4450     // And in the case of SSL, the compression can fool us and let the
4451     // server send the data much faster than expected.
4452     // So better provide random data that cannot be compressed.
4453     for (int i = 0; i < wantedSize; ++i)
4454         testData += (char)qrand();
4455
4456     QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: ");
4457     httpResponse += QByteArray::number(testData.size());
4458     httpResponse += "\r\n\r\n";
4459     httpResponse += testData;
4460
4461     qDebug() << "Server will send" << (httpResponse.size()-testData.size()) << "bytes of header and"
4462              << testData.size() << "bytes of data";
4463
4464     const bool fillKernelBuffer = bufferSize > 0;
4465     FastSender server(httpResponse, https, fillKernelBuffer);
4466
4467     QUrl url(QString("%1://127.0.0.1:%2/qtest/rfc3252.txt")
4468              .arg(https?"https":"http")
4469              .arg(server.serverPort()));
4470     QNetworkRequest request(url);
4471     QNetworkReplyPtr reply(manager.get(request));
4472     reply->setReadBufferSize(bufferSize);
4473     reply->ignoreSslErrors();
4474     const int rate = 200; // in kB per sec
4475     RateControlledReader reader(server, reply.data(), rate, bufferSize);
4476
4477     QTime loopTime;
4478     loopTime.start();
4479
4480     QVERIFY(waitForFinish(reply) == Success);
4481
4482     const int elapsedTime = loopTime.elapsed();
4483     server.wait();
4484     reader.wrapUp();
4485
4486     qDebug() << "send rate:" << server.transferRate << "B/s";
4487     qDebug() << "receive rate:" << reader.totalBytesRead * 1000 / elapsedTime
4488              << "(it received" << reader.totalBytesRead << "bytes in" << elapsedTime << "ms)";
4489
4490     QCOMPARE(reply->url(), request.url());
4491     QCOMPARE(reply->error(), QNetworkReply::NoError);
4492     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
4493
4494     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), (qint64)testData.size());
4495     if (reader.data.size() < testData.size()) { // oops?
4496         QCOMPARE(reader.data, testData.mid(0, reader.data.size()));
4497         qDebug() << "The data is incomplete, the last" << testData.size() - reader.data.size() << "bytes are missing";
4498         QEXPECT_FAIL("http+limited", "Limiting is broken right now, check QTBUG-15065", Abort);
4499         QEXPECT_FAIL("https+limited", "Limiting is broken right now, check QTBUG-15065", Abort);
4500     }
4501     QCOMPARE(reader.data.size(), testData.size());
4502     QCOMPARE(reader.data, testData);
4503
4504     // OK we got the file alright, but did setReadBufferSize work?
4505     QVERIFY(server.transferRate != -1);
4506     if (bufferSize > 0) {
4507         const int allowedDeviation = 16; // TODO find out why the send rate is 13% faster currently
4508         const int minRate = rate * 1024 * (100-allowedDeviation) / 100;
4509         const int maxRate = rate * 1024 * (100+allowedDeviation) / 100;
4510         qDebug() << minRate << "<="<< server.transferRate << "<=" << maxRate << "?";
4511         QEXPECT_FAIL("http+limited", "Limiting is broken right now, check QTBUG-15065", Continue);
4512         QEXPECT_FAIL("https+limited", "Limiting is broken right now, check QTBUG-15065", Continue);
4513         QVERIFY(server.transferRate >= minRate && server.transferRate <= maxRate);
4514     }
4515 }
4516
4517 void tst_QNetworkReply::ioPostToHttpUploadProgress()
4518 {
4519     QFile sourceFile(testDataDir + "/bigfile");
4520     QVERIFY(sourceFile.open(QIODevice::ReadOnly));
4521
4522     // emulate a minimal http server
4523     QTcpServer server;
4524     server.listen(QHostAddress(QHostAddress::LocalHost), 0);
4525
4526     // create the request
4527     QUrl url = QUrl(QString("http://127.0.0.1:%1/").arg(server.serverPort()));
4528     QNetworkRequest request(url);
4529     request.setRawHeader("Content-Type", "application/octet-stream");
4530     QNetworkReplyPtr reply(manager.post(request, &sourceFile));
4531     QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64)));
4532     connect(&server, SIGNAL(newConnection()), &QTestEventLoop::instance(), SLOT(exitLoop()));
4533
4534     // get the request started and the incoming socket connected
4535     QTestEventLoop::instance().enterLoop(10);
4536     QVERIFY(!QTestEventLoop::instance().timeout());
4537     QTcpSocket *incomingSocket = server.nextPendingConnection();
4538     QVERIFY(incomingSocket);
4539     disconnect(&server, SIGNAL(newConnection()), &QTestEventLoop::instance(), SLOT(exitLoop()));
4540
4541     incomingSocket->setReadBufferSize(1*1024);
4542     QTestEventLoop::instance().enterLoop(5);
4543     // some progress should have been made
4544     QList<QVariant> args = spy.last();
4545     QVERIFY(!args.isEmpty());
4546     QVERIFY(args.at(0).toLongLong() > 0);
4547     // but not everything!
4548     QVERIFY(args.at(0).toLongLong() != sourceFile.size());
4549
4550     // set the read buffer to unlimited
4551     incomingSocket->setReadBufferSize(0);
4552     QTestEventLoop::instance().enterLoop(10);
4553     // progress should be finished
4554     QList<QVariant> args3 = spy.last();
4555     QVERIFY(!args3.isEmpty());
4556     // More progress than before
4557     QVERIFY(args3.at(0).toLongLong() > args.at(0).toLongLong());
4558     QCOMPARE(args3.at(0).toLongLong(), args3.at(1).toLongLong());
4559     // And actually finished..
4560     QCOMPARE(args3.at(0).toLongLong(), sourceFile.size());
4561
4562     // after sending this, the QNAM should emit finished()
4563     connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
4564     incomingSocket->write("HTTP/1.0 200 OK\r\n");
4565     incomingSocket->write("Content-Length: 0\r\n");
4566     incomingSocket->write("\r\n");
4567     QTestEventLoop::instance().enterLoop(10);
4568     // not timeouted -> finished() was emitted
4569     QVERIFY(!QTestEventLoop::instance().timeout());
4570
4571     incomingSocket->close();
4572     server.close();
4573 }
4574
4575 void tst_QNetworkReply::ioPostToHttpEmptyUploadProgress()
4576 {
4577     QByteArray ba;
4578     ba.resize(0);
4579     QBuffer buffer(&ba,0);
4580     QVERIFY(buffer.open(QIODevice::ReadOnly));
4581
4582     // emulate a minimal http server
4583     QTcpServer server;
4584     server.listen(QHostAddress(QHostAddress::LocalHost), 0);
4585
4586     // create the request
4587     QUrl url = QUrl(QString("http://127.0.0.1:%1/").arg(server.serverPort()));
4588     QNetworkRequest request(url);
4589     request.setRawHeader("Content-Type", "application/octet-stream");
4590     QNetworkReplyPtr reply(manager.post(request, &buffer));
4591     QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64)));
4592     connect(&server, SIGNAL(newConnection()), &QTestEventLoop::instance(), SLOT(exitLoop()));
4593
4594
4595     // get the request started and the incoming socket connected
4596     QTestEventLoop::instance().enterLoop(10);
4597     QVERIFY(!QTestEventLoop::instance().timeout());
4598     QTcpSocket *incomingSocket = server.nextPendingConnection();
4599     QVERIFY(incomingSocket);
4600
4601     // after sending this, the QNAM should emit finished()
4602     connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
4603     incomingSocket->write("HTTP/1.0 200 OK\r\n");
4604     incomingSocket->write("Content-Length: 0\r\n");
4605     incomingSocket->write("\r\n");
4606     incomingSocket->flush();
4607     QTestEventLoop::instance().enterLoop(10);
4608     // not timeouted -> finished() was emitted
4609     QVERIFY(!QTestEventLoop::instance().timeout());
4610
4611     // final check: only 1 uploadProgress has been emitted
4612     QVERIFY(spy.length() == 1);
4613     QList<QVariant> args = spy.last();
4614     QVERIFY(!args.isEmpty());
4615     QCOMPARE(args.at(0).toLongLong(), buffer.size());
4616     QCOMPARE(args.at(0).toLongLong(), buffer.size());
4617
4618     incomingSocket->close();
4619     server.close();
4620 }
4621
4622 void tst_QNetworkReply::lastModifiedHeaderForFile()
4623 {
4624     QFileInfo fileInfo(testDataDir + "/bigfile");
4625     QVERIFY(fileInfo.exists());
4626
4627     QUrl url = QUrl::fromLocalFile(fileInfo.filePath());
4628
4629     QNetworkRequest request(url);
4630     QNetworkReplyPtr reply(manager.head(request));
4631
4632     QVERIFY(waitForFinish(reply) == Success);
4633
4634     QDateTime header = reply->header(QNetworkRequest::LastModifiedHeader).toDateTime();
4635     QCOMPARE(header, fileInfo.lastModified());
4636 }
4637
4638 void tst_QNetworkReply::lastModifiedHeaderForHttp()
4639 {
4640     // Tue, 22 May 2007 12:04:57 GMT according to webserver
4641     QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/fluke.gif";
4642
4643     QNetworkRequest request(url);
4644     QNetworkReplyPtr reply(manager.head(request));
4645
4646     QVERIFY(waitForFinish(reply) == Success);
4647
4648     QDateTime header = reply->header(QNetworkRequest::LastModifiedHeader).toDateTime();
4649     QDateTime realDate = QDateTime::fromString("2007-05-22T12:04:57", Qt::ISODate);
4650     realDate.setTimeSpec(Qt::UTC);
4651
4652     QCOMPARE(header, realDate);
4653 }
4654
4655 void tst_QNetworkReply::httpCanReadLine()
4656 {
4657     QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"));
4658     QNetworkReplyPtr reply(manager.get(request));
4659
4660     QVERIFY(waitForFinish(reply) == Success);
4661
4662     QCOMPARE(reply->error(), QNetworkReply::NoError);
4663
4664     QVERIFY(reply->canReadLine());
4665     QVERIFY(!reply->readAll().isEmpty());
4666     QVERIFY(!reply->canReadLine());
4667 }
4668
4669 void tst_QNetworkReply::rateControl_data()
4670 {
4671     QTest::addColumn<int>("rate");
4672
4673     QTest::newRow("15") << 15;
4674     QTest::newRow("40") << 40;
4675     QTest::newRow("73") << 73;
4676     QTest::newRow("80") << 80;
4677     QTest::newRow("125") << 125;
4678     QTest::newRow("250") << 250;
4679     QTest::newRow("1024") << 1024;
4680 }
4681
4682 void tst_QNetworkReply::rateControl()
4683 {
4684     QSKIP("Test disabled -- only for manual purposes");
4685     // this function tests that we aren't reading from the network
4686     // faster than the data is being consumed.
4687     QFETCH(int, rate);
4688
4689 #if !defined(QT_BUILD_INTERNAL)
4690     QSKIP("backend for testing not available!");
4691 #endif
4692
4693     // ask for 20 seconds worth of data
4694     FastSender sender(20 * rate * 1024);
4695
4696     QNetworkRequest request("debugpipe://localhost:" + QString::number(sender.serverPort()));
4697     QNetworkReplyPtr reply(manager.get(request));
4698     reply->setReadBufferSize(32768);
4699     QSignalSpy errorSpy(reply.data(), SIGNAL(error(QNetworkReply::NetworkError)));
4700
4701     RateControlledReader reader(sender, reply.data(), rate, 20);
4702
4703     // this test is designed to run for 25 seconds at most
4704     QTime loopTime;
4705     loopTime.start();
4706
4707     QVERIFY(waitForFinish(reply) == Success);
4708
4709     int elapsedTime = loopTime.elapsed();
4710
4711     if (!errorSpy.isEmpty()) {
4712         qDebug() << "ERROR!" << errorSpy[0][0] << reply->errorString();
4713     }
4714
4715     qDebug() << "tst_QNetworkReply::rateControl" << "send rate:" << sender.transferRate;
4716     qDebug() << "tst_QNetworkReply::rateControl" << "receive rate:" << reader.totalBytesRead * 1000 / elapsedTime
4717              << "(it received" << reader.totalBytesRead << "bytes in" << elapsedTime << "ms)";
4718
4719     sender.wait();
4720
4721     QCOMPARE(reply->url(), request.url());
4722     QCOMPARE(reply->error(), QNetworkReply::NoError);
4723
4724     QVERIFY(sender.transferRate != -1);
4725     int minRate = rate * 1024 * 9 / 10;
4726     int maxRate = rate * 1024 * 11 / 10;
4727     QVERIFY(sender.transferRate >= minRate);
4728     QVERIFY(sender.transferRate <= maxRate);
4729 }
4730
4731 void tst_QNetworkReply::downloadProgress_data()
4732 {
4733     QTest::addColumn<int>("loopCount");
4734
4735     QTest::newRow("empty") << 0;
4736     QTest::newRow("small") << 4;
4737     QTest::newRow("big") << 4096;
4738 }
4739
4740 void tst_QNetworkReply::downloadProgress()
4741 {
4742 #if !defined(QT_BUILD_INTERNAL)
4743     QSKIP("backend for testing not available!");
4744 #endif
4745     QTcpServer server;
4746     QVERIFY(server.listen());
4747
4748     QNetworkRequest request("debugpipe://127.0.0.1:" + QString::number(server.serverPort()) + "/?bare=1");
4749     QNetworkReplyPtr reply(manager.get(request));
4750     QSignalSpy spy(reply.data(), SIGNAL(downloadProgress(qint64,qint64)));
4751     connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
4752             &QTestEventLoop::instance(), SLOT(exitLoop()));
4753     QVERIFY(spy.isValid());
4754     QVERIFY(!reply->isFinished());
4755     QVERIFY(reply->isRunning());
4756
4757     QCoreApplication::instance()->processEvents();
4758     if (!server.hasPendingConnections())
4759         server.waitForNewConnection(1000);
4760     QVERIFY(server.hasPendingConnections());
4761     QCOMPARE(spy.count(), 0);
4762
4763     QByteArray data(128, 'a');
4764     QTcpSocket *sender = server.nextPendingConnection();
4765     QVERIFY(sender);
4766
4767     QFETCH(int, loopCount);
4768     for (int i = 1; i <= loopCount; ++i) {
4769         sender->write(data);
4770         QVERIFY2(sender->waitForBytesWritten(2000), "Network timeout");
4771
4772         spy.clear();
4773         QTestEventLoop::instance().enterLoop(2);
4774         QVERIFY(!QTestEventLoop::instance().timeout());
4775         QVERIFY(spy.count() > 0);
4776         QVERIFY(!reply->isFinished());
4777         QVERIFY(reply->isRunning());
4778
4779         QList<QVariant> args = spy.last();
4780         QCOMPARE(args.at(0).toInt(), i*data.size());
4781         QCOMPARE(args.at(1).toInt(), -1);
4782     }
4783
4784     // close the connection:
4785     delete sender;
4786
4787     spy.clear();
4788     QTestEventLoop::instance().enterLoop(2);
4789     QCOMPARE(reply->error(), QNetworkReply::NoError);
4790     QVERIFY(!QTestEventLoop::instance().timeout());
4791     QVERIFY(spy.count() > 0);
4792     QVERIFY(!reply->isRunning());
4793     QVERIFY(reply->isFinished());
4794
4795     QList<QVariant> args = spy.last();
4796     QCOMPARE(args.at(0).toInt(), loopCount * data.size());
4797     QCOMPARE(args.at(1).toInt(), loopCount * data.size());
4798 }
4799
4800 void tst_QNetworkReply::uploadProgress_data()
4801 {
4802     putToFile_data();
4803 }
4804
4805 void tst_QNetworkReply::uploadProgress()
4806 {
4807     QFETCH(QByteArray, data);
4808 #if !defined(QT_BUILD_INTERNAL)
4809     QSKIP("backend for testing not available!");
4810 #endif
4811     QTcpServer server;
4812     QVERIFY(server.listen());
4813
4814     QNetworkRequest request("debugpipe://127.0.0.1:" + QString::number(server.serverPort()) + "/?bare=1");
4815     QNetworkReplyPtr reply(manager.put(request, data));
4816     QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64)));
4817     QSignalSpy finished(reply.data(), SIGNAL(finished()));
4818     QVERIFY(spy.isValid());
4819     QVERIFY(finished.isValid());
4820
4821     QCoreApplication::instance()->processEvents();
4822     if (!server.hasPendingConnections())
4823         server.waitForNewConnection(1000);
4824     QVERIFY(server.hasPendingConnections());
4825
4826     QTcpSocket *receiver = server.nextPendingConnection();
4827     if (finished.count() == 0) {
4828         // it's not finished yet, so wait for it to be
4829         QVERIFY(waitForFinish(reply) == Success);
4830     }
4831     delete receiver;
4832
4833     QVERIFY(finished.count() > 0);
4834     QVERIFY(spy.count() > 0);
4835
4836     QList<QVariant> args = spy.last();
4837     QCOMPARE(args.at(0).toInt(), data.size());
4838     QCOMPARE(args.at(1).toInt(), data.size());
4839 }
4840
4841 void tst_QNetworkReply::chaining_data()
4842 {
4843     putToFile_data();
4844 }
4845
4846 void tst_QNetworkReply::chaining()
4847 {
4848     QTemporaryFile sourceFile(QDir::currentPath() + "/temp-XXXXXX");
4849     sourceFile.setAutoRemove(true);
4850     QVERIFY(sourceFile.open());
4851
4852     QFETCH(QByteArray, data);
4853     QVERIFY(sourceFile.write(data) == data.size());
4854     sourceFile.flush();
4855     QCOMPARE(sourceFile.size(), qint64(data.size()));
4856
4857     QNetworkRequest request(QUrl::fromLocalFile(sourceFile.fileName()));
4858     QNetworkReplyPtr getReply(manager.get(request));
4859
4860     QFile targetFile(testFileName);
4861     QUrl url = QUrl::fromLocalFile(targetFile.fileName());
4862     request.setUrl(url);
4863     QNetworkReplyPtr putReply(manager.put(request, getReply.data()));
4864
4865     QVERIFY(waitForFinish(putReply) == Success);
4866
4867     QCOMPARE(getReply->url(), QUrl::fromLocalFile(sourceFile.fileName()));
4868     QCOMPARE(getReply->error(), QNetworkReply::NoError);
4869     QCOMPARE(getReply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), sourceFile.size());
4870
4871     QCOMPARE(putReply->url(), url);
4872     QCOMPARE(putReply->error(), QNetworkReply::NoError);
4873     QCOMPARE(putReply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
4874     QVERIFY(putReply->readAll().isEmpty());
4875
4876     QVERIFY(sourceFile.atEnd());
4877     sourceFile.seek(0);         // reset it to the beginning
4878
4879     QVERIFY(targetFile.open(QIODevice::ReadOnly));
4880     QCOMPARE(targetFile.size(), sourceFile.size());
4881     QCOMPARE(targetFile.readAll(), sourceFile.readAll());
4882 }
4883
4884 void tst_QNetworkReply::receiveCookiesFromHttp_data()
4885 {
4886     QTest::addColumn<QString>("cookieString");
4887     QTest::addColumn<QList<QNetworkCookie> >("expectedCookiesFromHttp");
4888     QTest::addColumn<QList<QNetworkCookie> >("expectedCookiesInJar");
4889
4890     QTest::newRow("empty") << "" << QList<QNetworkCookie>() << QList<QNetworkCookie>();
4891
4892     QList<QNetworkCookie> header, jar;
4893     QNetworkCookie cookie("a", "b");
4894     header << cookie;
4895     cookie.setDomain(QtNetworkSettings::serverName());
4896     cookie.setPath("/qtest/cgi-bin/");
4897     jar << cookie;
4898     QTest::newRow("simple-cookie") << "a=b" << header << jar;
4899
4900     header << QNetworkCookie("c", "d");
4901     cookie.setName("c");
4902     cookie.setValue("d");
4903     jar << cookie;
4904     QTest::newRow("two-cookies") << "a=b, c=d" << header << jar;
4905     QTest::newRow("two-cookies-2") << "a=b\nc=d" << header << jar;
4906
4907     header.clear();
4908     jar.clear();
4909     cookie = QNetworkCookie("a", "b");
4910     cookie.setPath("/not/part-of-path");
4911     header << cookie;
4912     cookie.setDomain(QtNetworkSettings::serverName());
4913     jar << cookie;
4914     QTest::newRow("invalid-cookie-path") << "a=b; path=/not/part-of-path" << header << jar;
4915
4916     jar.clear();
4917     cookie = QNetworkCookie("a", "b");
4918     cookie.setDomain(".example.com");
4919     header.clear();
4920     header << cookie;
4921     QTest::newRow("invalid-cookie-domain") << "a=b; domain=.example.com" << header << jar;
4922 }
4923
4924 void tst_QNetworkReply::receiveCookiesFromHttp()
4925 {
4926     QFETCH(QString, cookieString);
4927
4928     QByteArray data = cookieString.toLatin1() + '\n';
4929     QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/set-cookie.cgi");
4930     QNetworkRequest request(url);
4931     request.setRawHeader("Content-Type", "application/octet-stream");
4932     QNetworkReplyPtr reply;
4933     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data));
4934
4935     QCOMPARE(reply->url(), url);
4936     QCOMPARE(reply->error(), QNetworkReply::NoError);
4937
4938     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
4939
4940     QList<QNetworkCookie> setCookies =
4941         qvariant_cast<QList<QNetworkCookie> >(reply->header(QNetworkRequest::SetCookieHeader));
4942     QTEST(setCookies, "expectedCookiesFromHttp");
4943     QTEST(cookieJar->allCookies(), "expectedCookiesInJar");
4944 }
4945
4946 void tst_QNetworkReply::receiveCookiesFromHttpSynchronous_data()
4947 {
4948     tst_QNetworkReply::receiveCookiesFromHttp_data();
4949 }
4950
4951 void tst_QNetworkReply::receiveCookiesFromHttpSynchronous()
4952 {
4953     QFETCH(QString, cookieString);
4954
4955     QByteArray data = cookieString.toLatin1() + '\n';
4956     QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/set-cookie.cgi");
4957
4958     QNetworkRequest request(url);
4959     request.setRawHeader("Content-Type", "application/octet-stream");
4960     request.setAttribute(
4961             QNetworkRequest::SynchronousRequestAttribute,
4962             true);
4963
4964     QNetworkReplyPtr reply;
4965     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data));
4966
4967     QCOMPARE(reply->url(), url);
4968     QCOMPARE(reply->error(), QNetworkReply::NoError);
4969
4970     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
4971
4972     QList<QNetworkCookie> setCookies =
4973             qvariant_cast<QList<QNetworkCookie> >(reply->header(QNetworkRequest::SetCookieHeader));
4974     QTEST(setCookies, "expectedCookiesFromHttp");
4975     QTEST(cookieJar->allCookies(), "expectedCookiesInJar");
4976 }
4977
4978 void tst_QNetworkReply::sendCookies_data()
4979 {
4980     QTest::addColumn<QList<QNetworkCookie> >("cookiesToSet");
4981     QTest::addColumn<QString>("expectedCookieString");
4982
4983     QList<QNetworkCookie> list;
4984     QTest::newRow("empty") << list << "";
4985
4986     QNetworkCookie cookie("a", "b");
4987     cookie.setPath("/");
4988     cookie.setDomain("example.com");
4989     list << cookie;
4990     QTest::newRow("no-match-domain") << list << "";
4991
4992     cookie.setDomain(QtNetworkSettings::serverName());
4993     cookie.setPath("/something/else");
4994     list << cookie;
4995     QTest::newRow("no-match-path") << list << "";
4996
4997     cookie.setPath("/");
4998     list << cookie;
4999     QTest::newRow("simple-cookie") << list << "a=b";
5000
5001     cookie.setPath("/qtest");
5002     cookie.setValue("longer");
5003     list << cookie;
5004     QTest::newRow("two-cookies") << list << "a=longer; a=b";
5005
5006     list.clear();
5007     cookie = QNetworkCookie("a", "b");
5008     cookie.setPath("/");
5009     cookie.setDomain("." + QtNetworkSettings::serverDomainName());
5010     list << cookie;
5011     QTest::newRow("domain-match") << list << "a=b";
5012
5013     // but it shouldn't match this:
5014     cookie.setDomain(QtNetworkSettings::serverDomainName());
5015     list << cookie;
5016     QTest::newRow("domain-match-2") << list << "a=b";
5017 }
5018
5019 void tst_QNetworkReply::sendCookies()
5020 {
5021     QFETCH(QString, expectedCookieString);
5022     QFETCH(QList<QNetworkCookie>, cookiesToSet);
5023     cookieJar->setAllCookies(cookiesToSet);
5024
5025     QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/get-cookie.cgi");
5026     QNetworkRequest request(url);
5027     QNetworkReplyPtr reply;
5028     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
5029
5030     QCOMPARE(reply->url(), url);
5031     QCOMPARE(reply->error(), QNetworkReply::NoError);
5032
5033     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
5034
5035     QCOMPARE(QString::fromLatin1(reply->readAll()).trimmed(), expectedCookieString);
5036 }
5037
5038 void tst_QNetworkReply::sendCookiesSynchronous_data()
5039 {
5040     tst_QNetworkReply::sendCookies_data();
5041 }
5042
5043 void tst_QNetworkReply::sendCookiesSynchronous()
5044 {
5045     QFETCH(QString, expectedCookieString);
5046     QFETCH(QList<QNetworkCookie>, cookiesToSet);
5047     cookieJar->setAllCookies(cookiesToSet);
5048
5049     QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/get-cookie.cgi");
5050     QNetworkRequest request(url);
5051
5052     request.setAttribute(
5053             QNetworkRequest::SynchronousRequestAttribute,
5054             true);
5055
5056     QNetworkReplyPtr reply;
5057     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
5058
5059     QCOMPARE(reply->url(), url);
5060     QCOMPARE(reply->error(), QNetworkReply::NoError);
5061
5062     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
5063
5064     QCOMPARE(QString::fromLatin1(reply->readAll()).trimmed(), expectedCookieString);
5065 }
5066
5067 void tst_QNetworkReply::nestedEventLoops_slot()
5068 {
5069     QEventLoop subloop;
5070
5071     // 16 seconds: fluke times out in 15 seconds, which triggers a QTcpSocket error
5072     QTimer::singleShot(16000, &subloop, SLOT(quit()));
5073     subloop.exec();
5074
5075     QTestEventLoop::instance().exitLoop();
5076 }
5077
5078 void tst_QNetworkReply::nestedEventLoops()
5079 {
5080     // Slightly fragile test, it may not be testing anything
5081     // This is certifying that we're not running into the same issue
5082     // that QHttp had (task 200432): the QTcpSocket connection is
5083     // closed by the remote end because of the kept-alive HTTP
5084     // connection timed out.
5085     //
5086     // The exact time required for this to happen is not exactly
5087     // defined. Our server (Apache httpd) times out after 15
5088     // seconds. (see above)
5089
5090     qDebug("Takes 16 seconds to run, please wait");
5091
5092     QUrl url("http://" + QtNetworkSettings::serverName());
5093     QNetworkRequest request(url);
5094     QNetworkReplyPtr reply(manager.get(request));
5095
5096     QSignalSpy finishedspy(reply.data(), SIGNAL(finished()));
5097     QSignalSpy errorspy(reply.data(), SIGNAL(error(QNetworkReply::NetworkError)));
5098
5099     connect(reply, SIGNAL(finished()), SLOT(nestedEventLoops_slot()));
5100     QTestEventLoop::instance().enterLoop(20);
5101     QVERIFY2(!QTestEventLoop::instance().timeout(), "Network timeout");
5102
5103     QCOMPARE(finishedspy.count(), 1);
5104     QCOMPARE(errorspy.count(), 0);
5105 }
5106
5107 void tst_QNetworkReply::httpProxyCommands_data()
5108 {
5109     QTest::addColumn<QUrl>("url");
5110     QTest::addColumn<QByteArray>("responseToSend");
5111     QTest::addColumn<QString>("expectedCommand");
5112
5113     QTest::newRow("http")
5114         << QUrl("http://0.0.0.0:4443/http-request")
5115         << QByteArray("HTTP/1.0 200 OK\r\nProxy-Connection: close\r\nContent-Length: 1\r\n\r\n1")
5116         << "GET http://0.0.0.0:4443/http-request HTTP/1.";
5117 #ifndef QT_NO_SSL
5118     QTest::newRow("https")
5119         << QUrl("https://0.0.0.0:4443/https-request")
5120         << QByteArray("HTTP/1.0 200 Connection Established\r\n\r\n")
5121         << "CONNECT 0.0.0.0:4443 HTTP/1.";
5122 #endif
5123 }
5124
5125 void tst_QNetworkReply::httpProxyCommands()
5126 {
5127     QFETCH(QUrl, url);
5128     QFETCH(QByteArray, responseToSend);
5129     QFETCH(QString, expectedCommand);
5130
5131     MiniHttpServer proxyServer(responseToSend);
5132     QNetworkProxy proxy(QNetworkProxy::HttpProxy, "127.0.0.1", proxyServer.serverPort());
5133
5134     manager.setProxy(proxy);
5135     QNetworkRequest request(url);
5136     request.setRawHeader("User-Agent", "QNetworkReplyAutoTest/1.0");
5137     QNetworkReplyPtr reply(manager.get(request));
5138     //clearing the proxy here causes the test to fail.
5139     //the proxy isn't used until after the bearer has been started
5140     //which is correct in general, because system proxy isn't known until that time.
5141     //removing this line is safe, as the proxy is also reset by the cleanup() function
5142     //manager.setProxy(QNetworkProxy());
5143
5144     // wait for the finished signal
5145     QVERIFY(waitForFinish(reply) != Timeout);
5146
5147     //qDebug() << reply->error() << reply->errorString();
5148     //qDebug() << proxyServer.receivedData;
5149
5150     // we don't really care if the request succeeded
5151     // especially since it won't succeed in the HTTPS case
5152     // so just check that the command was correct
5153
5154     QString receivedHeader = proxyServer.receivedData.left(expectedCommand.length());
5155     QCOMPARE(receivedHeader, expectedCommand);
5156
5157     //QTBUG-17223 - make sure the user agent from the request is sent to proxy server even for CONNECT
5158     int uapos = proxyServer.receivedData.indexOf("User-Agent");
5159     int uaend = proxyServer.receivedData.indexOf("\r\n", uapos);
5160     QByteArray uaheader = proxyServer.receivedData.mid(uapos, uaend - uapos);
5161     QCOMPARE(uaheader, QByteArray("User-Agent: QNetworkReplyAutoTest/1.0"));
5162 }
5163
5164 class ProxyChangeHelper : public QObject {
5165     Q_OBJECT
5166 public:
5167     ProxyChangeHelper() : QObject(), signalCount(0) {};
5168 public slots:
5169     void finishedSlot() {
5170         signalCount++;
5171         if (signalCount == 2)
5172             QMetaObject::invokeMethod(&QTestEventLoop::instance(), "exitLoop", Qt::QueuedConnection);
5173     }
5174 private:
5175    int signalCount;
5176 };
5177
5178 void tst_QNetworkReply::httpProxyCommandsSynchronous_data()
5179 {
5180     httpProxyCommands_data();
5181 }
5182
5183 struct QThreadCleanup
5184 {
5185     static inline void cleanup(QThread *thread)
5186     {
5187         thread->quit();
5188         if (thread->wait(3000))
5189             delete thread;
5190         else
5191             qWarning("thread hung, leaking memory so test can finish");
5192     }
5193 };
5194
5195 struct QDeleteLaterCleanup
5196 {
5197     static inline void cleanup(QObject *o)
5198     {
5199         o->deleteLater();
5200     }
5201 };
5202
5203 void tst_QNetworkReply::httpProxyCommandsSynchronous()
5204 {
5205     QFETCH(QUrl, url);
5206     QFETCH(QByteArray, responseToSend);
5207     QFETCH(QString, expectedCommand);
5208
5209     // when using synchronous commands, we need a different event loop for
5210     // the server thread, because the client is never returning to the
5211     // event loop
5212     QScopedPointer<QThread, QThreadCleanup> serverThread(new QThread);
5213     QScopedPointer<MiniHttpServer, QDeleteLaterCleanup> proxyServer(new MiniHttpServer(responseToSend, false, serverThread.data()));
5214     QNetworkProxy proxy(QNetworkProxy::HttpProxy, "127.0.0.1", proxyServer->serverPort());
5215
5216     manager.setProxy(proxy);
5217     QNetworkRequest request(url);
5218
5219     // send synchronous request
5220     request.setAttribute(
5221             QNetworkRequest::SynchronousRequestAttribute,
5222             true);
5223
5224     QNetworkReplyPtr reply(manager.get(request));
5225     QVERIFY(reply->isFinished()); // synchronous
5226     manager.setProxy(QNetworkProxy());
5227
5228     //qDebug() << reply->error() << reply->errorString();
5229
5230     // we don't really care if the request succeeded
5231     // especially since it won't succeed in the HTTPS case
5232     // so just check that the command was correct
5233
5234     QString receivedHeader = proxyServer->receivedData.left(expectedCommand.length());
5235     QCOMPARE(receivedHeader, expectedCommand);
5236 }
5237
5238 void tst_QNetworkReply::proxyChange()
5239 {
5240     ProxyChangeHelper helper;
5241     MiniHttpServer proxyServer(
5242         "HTTP/1.0 200 OK\r\nProxy-Connection: keep-alive\r\n"
5243         "Content-Length: 1\r\n\r\n1");
5244     QNetworkProxy dummyProxy(QNetworkProxy::HttpProxy, "127.0.0.1", proxyServer.serverPort());
5245     QNetworkRequest req(QUrl("http://" + QtNetworkSettings::serverName()));
5246     proxyServer.doClose = false;
5247
5248     manager.setProxy(dummyProxy);
5249     QNetworkReplyPtr reply1(manager.get(req));
5250     connect(reply1, SIGNAL(finished()), &helper, SLOT(finishedSlot()));
5251
5252     manager.setProxy(QNetworkProxy());
5253     QNetworkReplyPtr reply2(manager.get(req));
5254     connect(reply2, SIGNAL(finished()), &helper, SLOT(finishedSlot()));
5255
5256     QTestEventLoop::instance().enterLoop(20);
5257     QVERIFY(!QTestEventLoop::instance().timeout());
5258
5259     // verify that the replies succeeded
5260     QCOMPARE(reply1->error(), QNetworkReply::NoError);
5261     QCOMPARE(reply1->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
5262     QVERIFY(reply1->size() == 1);
5263
5264     QCOMPARE(reply2->error(), QNetworkReply::NoError);
5265     QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
5266     QVERIFY(reply2->size() > 1);
5267
5268     // now try again and get an error
5269     // this verifies that we reuse the already-open connection
5270
5271     proxyServer.doClose = true;
5272     proxyServer.dataToTransmit =
5273         "HTTP/1.0 403 Forbidden\r\nProxy-Connection: close\r\n"
5274         "Content-Length: 1\r\n\r\n1";
5275
5276     manager.setProxy(dummyProxy);
5277     QNetworkReplyPtr reply3(manager.get(req));
5278
5279     QVERIFY(waitForFinish(reply3) == Failure);
5280
5281     QVERIFY(int(reply3->error()) > 0);
5282 }
5283
5284 void tst_QNetworkReply::authorizationError_data()
5285 {
5286
5287     QTest::addColumn<QString>("url");
5288     QTest::addColumn<int>("errorSignalCount");
5289     QTest::addColumn<int>("finishedSignalCount");
5290     QTest::addColumn<int>("error");
5291     QTest::addColumn<int>("httpStatusCode");
5292     QTest::addColumn<QString>("httpBody");
5293
5294     QTest::newRow("unknown-authorization-method") << "http://" + QtNetworkSettings::serverName() +
5295                                                      "/qtest/cgi-bin/http-unknown-authentication-method.cgi?401-authorization-required" << 1 << 1
5296                                                   << int(QNetworkReply::AuthenticationRequiredError) << 401 << "authorization required";
5297     QTest::newRow("unknown-proxy-authorization-method") << "http://" + QtNetworkSettings::serverName() +
5298                                                            "/qtest/cgi-bin/http-unknown-authentication-method.cgi?407-proxy-authorization-required" << 1 << 1
5299                                                         << int(QNetworkReply::ProxyAuthenticationRequiredError) << 407
5300                                                         << "authorization required";
5301 }
5302
5303 void tst_QNetworkReply::authorizationError()
5304 {
5305     QFETCH(QString, url);
5306     QNetworkRequest request(url);
5307     QNetworkReplyPtr reply(manager.get(request));
5308
5309     QCOMPARE(reply->error(), QNetworkReply::NoError);
5310
5311     QSignalSpy errorSpy(reply.data(), SIGNAL(error(QNetworkReply::NetworkError)));
5312     QSignalSpy finishedSpy(reply.data(), SIGNAL(finished()));
5313     // now run the request:
5314     QVERIFY(waitForFinish(reply) == Failure);
5315
5316     QFETCH(int, errorSignalCount);
5317     QCOMPARE(errorSpy.count(), errorSignalCount);
5318     QFETCH(int, finishedSignalCount);
5319     QCOMPARE(finishedSpy.count(), finishedSignalCount);
5320     QFETCH(int, error);
5321     QCOMPARE(reply->error(), QNetworkReply::NetworkError(error));
5322
5323     QFETCH(int, httpStatusCode);
5324     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), httpStatusCode);
5325
5326     QFETCH(QString, httpBody);
5327     QCOMPARE(qint64(reply->size()), qint64(httpBody.size()));
5328     QCOMPARE(QString(reply->readAll()), httpBody);
5329 }
5330
5331 void tst_QNetworkReply::httpConnectionCount()
5332 {
5333     QTcpServer server;
5334     QVERIFY(server.listen());
5335     QCoreApplication::instance()->processEvents();
5336
5337     for (int i = 0; i < 10; i++) {
5338         QNetworkRequest request (QUrl("http://127.0.0.1:" + QString::number(server.serverPort()) + "/" +  QString::number(i)));
5339         QNetworkReply* reply = manager.get(request);
5340         reply->setParent(&server);
5341     }
5342
5343     int pendingConnectionCount = 0;
5344     QTime time;
5345     time.start();
5346
5347     while(pendingConnectionCount <= 20) {
5348         QTestEventLoop::instance().enterLoop(1);
5349         QTcpSocket *socket = server.nextPendingConnection();
5350         while (socket != 0) {
5351             pendingConnectionCount++;
5352             socket->setParent(&server);
5353             socket = server.nextPendingConnection();
5354         }
5355
5356         // at max. wait 10 sec
5357         if (time.elapsed() > 10000)
5358             break;
5359     }
5360
5361     QCOMPARE(pendingConnectionCount, 6);
5362 }
5363
5364 void tst_QNetworkReply::httpReUsingConnectionSequential_data()
5365 {
5366     QTest::addColumn<bool>("doDeleteLater");
5367     QTest::newRow("deleteLater") << true;
5368     QTest::newRow("noDeleteLater") << false;
5369 }
5370
5371 void tst_QNetworkReply::httpReUsingConnectionSequential()
5372 {
5373     QFETCH(bool, doDeleteLater);
5374
5375     QByteArray response("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
5376     MiniHttpServer server(response);
5377     server.multiple = true;
5378     server.doClose = false;
5379
5380     QUrl url;
5381     url.setScheme("http");
5382     url.setPort(server.serverPort());
5383     url.setHost("127.0.0.1");
5384     // first request
5385     QNetworkReply* reply1 = manager.get(QNetworkRequest(url));
5386     connect(reply1, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
5387     QTestEventLoop::instance().enterLoop(2);
5388     QVERIFY(!QTestEventLoop::instance().timeout());
5389     QVERIFY(!reply1->error());
5390     int reply1port = server.client->peerPort();
5391
5392     if (doDeleteLater)
5393         reply1->deleteLater();
5394
5395     // finished received, send the next one
5396     QNetworkReply*reply2 = manager.get(QNetworkRequest(url));
5397     connect(reply2, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
5398     QTestEventLoop::instance().enterLoop(2);
5399     QVERIFY(!QTestEventLoop::instance().timeout());
5400     QVERIFY(!reply2->error());
5401     int reply2port = server.client->peerPort(); // should still be the same object
5402
5403     QVERIFY(reply1port > 0);
5404     QCOMPARE(server.totalConnections, 1);
5405     QCOMPARE(reply2port, reply1port);
5406
5407     if (!doDeleteLater)
5408         reply1->deleteLater(); // only do it if it was not done earlier
5409     reply2->deleteLater();
5410 }
5411
5412 class HttpReUsingConnectionFromFinishedSlot : public QObject {
5413     Q_OBJECT
5414 public:
5415     QNetworkReply* reply1;
5416     QNetworkReply* reply2;
5417     QUrl url;
5418     QNetworkAccessManager manager;
5419 public slots:
5420     void finishedSlot() {
5421         QVERIFY(!reply1->error());
5422
5423         QFETCH(bool, doDeleteLater);
5424         if (doDeleteLater) {
5425             reply1->deleteLater();
5426             reply1 = 0;
5427         }
5428
5429         // kick off 2nd request and exit the loop when it is done
5430         reply2 = manager.get(QNetworkRequest(url));
5431         reply2->setParent(this);
5432         connect(reply2, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
5433     }
5434 };
5435
5436 void tst_QNetworkReply::httpReUsingConnectionFromFinishedSlot_data()
5437 {
5438     httpReUsingConnectionSequential_data();
5439 }
5440
5441 void tst_QNetworkReply::httpReUsingConnectionFromFinishedSlot()
5442 {
5443     QByteArray response("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
5444     MiniHttpServer server(response);
5445     server.multiple = true;
5446     server.doClose = false;
5447
5448     HttpReUsingConnectionFromFinishedSlot helper;
5449     helper.reply1 = 0;
5450     helper.reply2 = 0;
5451     helper.url.setScheme("http");
5452     helper.url.setPort(server.serverPort());
5453     helper.url.setHost("127.0.0.1");
5454
5455     // first request
5456     helper.reply1 = helper.manager.get(QNetworkRequest(helper.url));
5457     helper.reply1->setParent(&helper);
5458     connect(helper.reply1, SIGNAL(finished()), &helper, SLOT(finishedSlot()));
5459     QTestEventLoop::instance().enterLoop(4);
5460     QVERIFY(!QTestEventLoop::instance().timeout());
5461
5462     QVERIFY(helper.reply2);
5463     QVERIFY(!helper.reply2->error());
5464
5465     QCOMPARE(server.totalConnections, 1);
5466 }
5467
5468 class HttpRecursiveCreationHelper : public QObject {
5469     Q_OBJECT
5470 public:
5471
5472     HttpRecursiveCreationHelper():
5473             QObject(0),
5474             requestsStartedCount_finished(0),
5475             requestsStartedCount_readyRead(0),
5476             requestsFinishedCount(0)
5477     {
5478     }
5479     QNetworkAccessManager manager;
5480     int requestsStartedCount_finished;
5481     int requestsStartedCount_readyRead;
5482     int requestsFinishedCount;
5483 public slots:
5484     void finishedSlot() {
5485         requestsFinishedCount++;
5486
5487         QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
5488         QVERIFY(!reply->error());
5489         QVERIFY(reply->bytesAvailable() == 27906);
5490
5491         if (requestsFinishedCount == 60) {
5492             QTestEventLoop::instance().exitLoop();
5493             return;
5494         }
5495
5496         if (requestsStartedCount_finished < 30) {
5497             startOne();
5498             requestsStartedCount_finished++;
5499         }
5500
5501         reply->deleteLater();
5502     }
5503     void readyReadSlot() {
5504         QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
5505         QVERIFY(!reply->error());
5506
5507         if (requestsStartedCount_readyRead < 30 && reply->bytesAvailable() > 27906/2) {
5508             startOne();
5509             requestsStartedCount_readyRead++;
5510         }
5511     }
5512     void startOne() {
5513         QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/fluke.gif";
5514         QNetworkRequest request(url);
5515         QNetworkReply *reply = manager.get(request);
5516         reply->setParent(this);
5517         connect(reply, SIGNAL(finished()), this, SLOT(finishedSlot()));
5518         connect(reply, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
5519     }
5520 };
5521
5522 void tst_QNetworkReply::httpRecursiveCreation()
5523 {
5524     // this test checks if creation of new requests to the same host properly works
5525     // from readyRead() and finished() signals
5526     HttpRecursiveCreationHelper helper;
5527     helper.startOne();
5528     QTestEventLoop::instance().enterLoop(30);
5529     QVERIFY(!QTestEventLoop::instance().timeout());
5530 }
5531
5532 #ifndef QT_NO_SSL
5533 void tst_QNetworkReply::ignoreSslErrorsList_data()
5534 {
5535     QTest::addColumn<QString>("url");
5536     QTest::addColumn<QList<QSslError> >("expectedSslErrors");
5537     QTest::addColumn<QNetworkReply::NetworkError>("expectedNetworkError");
5538
5539     QList<QSslError> expectedSslErrors;
5540     QList<QSslCertificate> certs = QSslCertificate::fromPath(testDataDir + "/certs/qt-test-server-cacert.pem");
5541     QSslError rightError(QSslError::SelfSignedCertificate, certs.at(0));
5542     QSslError wrongError(QSslError::SelfSignedCertificate);
5543
5544     QTest::newRow("SSL-failure-empty-list") << "https://" + QtNetworkSettings::serverName() + "/index.html" << expectedSslErrors << QNetworkReply::SslHandshakeFailedError;
5545     expectedSslErrors.append(wrongError);
5546     QTest::newRow("SSL-failure-wrong-error") << "https://" + QtNetworkSettings::serverName() + "/index.html" << expectedSslErrors << QNetworkReply::SslHandshakeFailedError;
5547     expectedSslErrors.append(rightError);
5548     QTest::newRow("allErrorsInExpectedList1") << "https://" + QtNetworkSettings::serverName() + "/index.html" << expectedSslErrors << QNetworkReply::NoError;
5549     expectedSslErrors.removeAll(wrongError);
5550     QTest::newRow("allErrorsInExpectedList2") << "https://" + QtNetworkSettings::serverName() + "/index.html" << expectedSslErrors << QNetworkReply::NoError;
5551     expectedSslErrors.removeAll(rightError);
5552     QTest::newRow("SSL-failure-empty-list-again") << "https://" + QtNetworkSettings::serverName() + "/index.html" << expectedSslErrors << QNetworkReply::SslHandshakeFailedError;
5553 }
5554
5555 void tst_QNetworkReply::ignoreSslErrorsList()
5556 {
5557     QFETCH(QString, url);
5558     QNetworkRequest request(url);
5559     QNetworkReplyPtr reply(manager.get(request));
5560
5561     QFETCH(QList<QSslError>, expectedSslErrors);
5562     reply->ignoreSslErrors(expectedSslErrors);
5563
5564     QVERIFY(waitForFinish(reply) != Timeout);
5565
5566     QFETCH(QNetworkReply::NetworkError, expectedNetworkError);
5567     QCOMPARE(reply->error(), expectedNetworkError);
5568 }
5569
5570 void tst_QNetworkReply::ignoreSslErrorsListWithSlot_data()
5571 {
5572     ignoreSslErrorsList_data();
5573 }
5574
5575 // this is not a test, just a slot called in the test below
5576 void tst_QNetworkReply::ignoreSslErrorListSlot(QNetworkReply *reply, const QList<QSslError> &)
5577 {
5578     reply->ignoreSslErrors(storedExpectedSslErrors);
5579 }
5580
5581 // do the same as in ignoreSslErrorsList, but ignore the errors in the slot
5582 void tst_QNetworkReply::ignoreSslErrorsListWithSlot()
5583 {
5584     QFETCH(QString, url);
5585     QNetworkRequest request(url);
5586     QNetworkReplyPtr reply(manager.get(request));
5587
5588     QFETCH(QList<QSslError>, expectedSslErrors);
5589     // store the errors to ignore them later in the slot connected below
5590     storedExpectedSslErrors = expectedSslErrors;
5591     connect(&manager, SIGNAL(sslErrors(QNetworkReply *, const QList<QSslError> &)),
5592             this, SLOT(ignoreSslErrorListSlot(QNetworkReply *, const QList<QSslError> &)));
5593
5594
5595     QVERIFY(waitForFinish(reply) != Timeout);
5596
5597     QFETCH(QNetworkReply::NetworkError, expectedNetworkError);
5598     QCOMPARE(reply->error(), expectedNetworkError);
5599 }
5600
5601 void tst_QNetworkReply::sslConfiguration_data()
5602 {
5603     QTest::addColumn<QSslConfiguration>("configuration");
5604     QTest::addColumn<bool>("works");
5605
5606     QTest::newRow("empty") << QSslConfiguration() << false;
5607     QSslConfiguration conf = QSslConfiguration::defaultConfiguration();
5608     QTest::newRow("default") << conf << false; // does not contain test server cert
5609     QList<QSslCertificate> testServerCert = QSslCertificate::fromPath(testDataDir + "/certs/qt-test-server-cacert.pem");
5610     conf.setCaCertificates(testServerCert);
5611     QTest::newRow("set-root-cert") << conf << true;
5612     conf.setProtocol(QSsl::SecureProtocols);
5613     QTest::newRow("secure") << conf << true;
5614 }
5615
5616 void tst_QNetworkReply::sslConfiguration()
5617 {
5618     QNetworkRequest request(QUrl("https://" + QtNetworkSettings::serverName() + "/index.html"));
5619     QFETCH(QSslConfiguration, configuration);
5620     request.setSslConfiguration(configuration);
5621     QNetworkReplyPtr reply(manager.get(request));
5622
5623     QVERIFY(waitForFinish(reply) != Timeout);
5624
5625     QFETCH(bool, works);
5626     QNetworkReply::NetworkError expectedError = works ? QNetworkReply::NoError : QNetworkReply::SslHandshakeFailedError;
5627     QCOMPARE(reply->error(), expectedError);
5628 }
5629
5630 #endif // QT_NO_SSL
5631
5632 void tst_QNetworkReply::getAndThenDeleteObject_data()
5633 {
5634     QTest::addColumn<bool>("replyFirst");
5635
5636     QTest::newRow("delete-reply-first") << true;
5637     QTest::newRow("delete-qnam-first") << false;
5638 }
5639
5640 void tst_QNetworkReply::getAndThenDeleteObject()
5641 {
5642     QSKIP("unstable test - reply may be finished too early");
5643     // yes, this will leak if the testcase fails. I don't care. It must not fail then :P
5644     QNetworkAccessManager *manager = new QNetworkAccessManager();
5645     QNetworkRequest request("http://" + QtNetworkSettings::serverName() + "/qtest/bigfile");
5646     QNetworkReply *reply = manager->get(request);
5647     reply->setReadBufferSize(1);
5648     reply->setParent((QObject*)0); // must be 0 because else it is the manager
5649
5650     QTime stopWatch;
5651     stopWatch.start();
5652     forever {
5653         QCoreApplication::instance()->processEvents();
5654         if (reply->bytesAvailable())
5655             break;
5656         if (stopWatch.elapsed() >= 30000)
5657             break;
5658     }
5659
5660     QVERIFY(reply->bytesAvailable());
5661     QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
5662     QVERIFY(!reply->isFinished()); // must not be finished
5663
5664     QFETCH(bool, replyFirst);
5665
5666     if (replyFirst) {
5667         delete reply;
5668         delete manager;
5669     } else {
5670         delete manager;
5671         delete reply;
5672     }
5673 }
5674
5675 // see https://bugs.webkit.org/show_bug.cgi?id=38935
5676 void tst_QNetworkReply::symbianOpenCDataUrlCrash()
5677 {
5678     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==");
5679     QUrl url = QUrl::fromEncoded(requestUrl.toLatin1());
5680     QNetworkRequest req(url);
5681     QNetworkReplyPtr reply;
5682
5683     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, req, reply));
5684
5685     QCOMPARE(reply->url(), url);
5686     QCOMPARE(reply->error(), QNetworkReply::NoError);
5687
5688     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(598));
5689 }
5690
5691 void tst_QNetworkReply::getFromHttpIntoBuffer_data()
5692 {
5693     QTest::addColumn<QUrl>("url");
5694
5695     QTest::newRow("rfc-internal") << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt");
5696 }
5697
5698 // Please note that the whole "zero copy" download buffer API is private right now. Do not use it.
5699 void tst_QNetworkReply::getFromHttpIntoBuffer()
5700 {
5701     QFETCH(QUrl, url);
5702     QNetworkRequest request(url);
5703     request.setAttribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute, 1024*128); // 128 kB
5704
5705     QNetworkAccessManager manager;
5706     QNetworkReply *reply = manager.get(request);
5707     connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
5708     QTestEventLoop::instance().enterLoop(10);
5709     QVERIFY(!QTestEventLoop::instance().timeout());
5710     QVERIFY(reply->isFinished());
5711
5712     QFile reference(testDataDir + "/rfc3252.txt");
5713     QVERIFY(reference.open(QIODevice::ReadOnly));
5714
5715     QCOMPARE(reference.bytesAvailable(), reply->bytesAvailable());
5716     QCOMPARE(reference.size(), reply->size());
5717
5718     // Compare the memory buffer
5719     QVariant downloadBufferAttribute = reply->attribute(QNetworkRequest::DownloadBufferAttribute);
5720     QVERIFY(downloadBufferAttribute.isValid());
5721     QSharedPointer<char> sharedPointer = downloadBufferAttribute.value<QSharedPointer<char> >();
5722     bool memoryComparison =
5723             (0 == memcmp(static_cast<void*>(reference.readAll().data()),
5724                          sharedPointer.data(), reference.size()));
5725     QVERIFY(memoryComparison);
5726
5727     // Make sure the normal reading works
5728     reference.seek(0);
5729     QCOMPARE(reply->read(42), reference.read(42));
5730     QCOMPARE(reply->getChar(0), reference.getChar(0));
5731     QCOMPARE(reply->peek(23), reference.peek(23));
5732     QCOMPARE(reply->readLine(), reference.readLine());
5733     QCOMPARE(reference.bytesAvailable(), reply->bytesAvailable());
5734     QCOMPARE(reply->readAll(), reference.readAll());
5735     QVERIFY(reply->atEnd());
5736 }
5737
5738 // FIXME we really need to consolidate all those server implementations
5739 class GetFromHttpIntoBuffer2Server : QObject {
5740     Q_OBJECT
5741     qint64 dataSize;
5742     qint64 dataSent;
5743     QTcpServer server;
5744     QTcpSocket *client;
5745     bool serverSendsContentLength;
5746     bool chunkedEncoding;
5747
5748 public:
5749     GetFromHttpIntoBuffer2Server (qint64 ds, bool sscl, bool ce) : dataSize(ds), dataSent(0),
5750     client(0), serverSendsContentLength(sscl), chunkedEncoding(ce) {
5751         server.listen();
5752         connect(&server, SIGNAL(newConnection()), this, SLOT(newConnectionSlot()));
5753     }
5754
5755     int serverPort() {
5756         return server.serverPort();
5757     }
5758
5759 public slots:
5760
5761     void newConnectionSlot() {
5762         client = server.nextPendingConnection();
5763         client->setParent(this);
5764         connect(client, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
5765         connect(client, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWrittenSlot(qint64)));
5766     }
5767
5768     void readyReadSlot() {
5769         client->readAll();
5770         client->write("HTTP/1.0 200 OK\n");
5771         if (serverSendsContentLength)
5772             client->write(QString("Content-Length: " + QString::number(dataSize) + "\n").toAscii());
5773         if (chunkedEncoding)
5774             client->write(QString("Transfer-Encoding: chunked\n").toAscii());
5775         client->write("Connection: close\n\n");
5776     }
5777
5778     void bytesWrittenSlot(qint64 amount) {
5779         Q_UNUSED(amount);
5780         if (dataSent == dataSize && client) {
5781             // close eventually
5782
5783             // chunked encoding: we have to send a last "empty" chunk
5784             if (chunkedEncoding)
5785                 client->write(QString("0\r\n\r\n").toAscii());
5786
5787             client->disconnectFromHost();
5788             server.close();
5789             client = 0;
5790             return;
5791         }
5792
5793         // send data
5794         if (client && client->bytesToWrite() < 100*1024 && dataSent < dataSize) {
5795             qint64 amount = qMin(qint64(16*1024), dataSize - dataSent);
5796             QByteArray data(amount, '@');
5797
5798             if (chunkedEncoding) {
5799                 client->write(QString(QString("%1").arg(amount,0,16).toUpper() + "\r\n").toAscii());
5800                 client->write(data.constData(), amount);
5801                 client->write(QString("\r\n").toAscii());
5802             } else {
5803                 client->write(data.constData(), amount);
5804             }
5805
5806             dataSent += amount;
5807         }
5808     }
5809 };
5810
5811 class GetFromHttpIntoBuffer2Client : QObject {
5812     Q_OBJECT
5813 private:
5814     bool useDownloadBuffer;
5815     QNetworkReply *reply;
5816     qint64 uploadSize;
5817     QList<qint64> bytesAvailableList;
5818 public:
5819     GetFromHttpIntoBuffer2Client (QNetworkReply *reply, bool useDownloadBuffer, qint64 uploadSize)
5820         : useDownloadBuffer(useDownloadBuffer), reply(reply), uploadSize(uploadSize)
5821     {
5822         connect(reply, SIGNAL(metaDataChanged()), this, SLOT(metaDataChangedSlot()));
5823         connect(reply, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
5824         connect(reply, SIGNAL(finished()), this, SLOT(finishedSlot()));
5825     }
5826
5827     public slots:
5828     void metaDataChangedSlot() {
5829         if (useDownloadBuffer) {
5830             QSharedPointer<char> sharedPointer = qvariant_cast<QSharedPointer<char> >(reply->attribute(QNetworkRequest::DownloadBufferAttribute));
5831             QVERIFY(!sharedPointer.isNull()); // It will be 0 if it failed
5832         }
5833
5834         // metaDataChanged needs to come before everything else
5835         QVERIFY(bytesAvailableList.isEmpty());
5836     }
5837
5838     void readyReadSlot() {
5839         QVERIFY(!reply->isFinished());
5840
5841         qint64 bytesAvailable = reply->bytesAvailable();
5842
5843         // bytesAvailable must never be 0
5844         QVERIFY(bytesAvailable != 0);
5845
5846         if (bytesAvailableList.length() < 5) {
5847             // We assume that the first few times the bytes available must be less than the complete size, e.g.
5848             // the bytesAvailable() function works correctly in case of a downloadBuffer.
5849             QVERIFY(bytesAvailable < uploadSize);
5850         }
5851         if (!bytesAvailableList.isEmpty()) {
5852             // Also check that the same bytesAvailable is not coming twice in a row
5853             QVERIFY(bytesAvailableList.last() != bytesAvailable);
5854         }
5855
5856         bytesAvailableList.append(bytesAvailable);
5857         // Add bytesAvailable to a list an parse
5858     }
5859
5860     void finishedSlot() {
5861         // We should have already received all readyRead
5862         QVERIFY(!bytesAvailableList.isEmpty());
5863         QVERIFY(bytesAvailableList.last() == uploadSize);
5864     }
5865 };
5866
5867 void tst_QNetworkReply::getFromHttpIntoBuffer2_data()
5868 {
5869     QTest::addColumn<bool>("useDownloadBuffer");
5870
5871     QTest::newRow("use-download-buffer") << true;
5872     QTest::newRow("do-not-use-download-buffer") << false;
5873 }
5874
5875 // This test checks mostly that signal emissions are in correct order
5876 // Please note that the whole "zero copy" download buffer API is private right now. Do not use it.
5877 void tst_QNetworkReply::getFromHttpIntoBuffer2()
5878 {
5879     QFETCH(bool, useDownloadBuffer);
5880
5881     // On my Linux Desktop the results are already visible with 128 kB, however we use this to have good results.
5882 #if defined(Q_OS_WINCE_WM)
5883     // Show some mercy to non-desktop platform/s
5884     enum {UploadSize = 4*1024*1024}; // 4 MB
5885 #else
5886     enum {UploadSize = 32*1024*1024}; // 32 MB
5887 #endif
5888
5889     GetFromHttpIntoBuffer2Server server(UploadSize, true, false);
5890
5891     QNetworkRequest request(QUrl("http://127.0.0.1:" + QString::number(server.serverPort()) + "/?bare=1"));
5892     if (useDownloadBuffer)
5893         request.setAttribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute, 1024*1024*128); // 128 MB is max allowed
5894
5895     QNetworkAccessManager manager;
5896     QNetworkReplyPtr reply(manager.get(request));
5897
5898     GetFromHttpIntoBuffer2Client client(reply.data(), useDownloadBuffer, UploadSize);
5899
5900     connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
5901     QTestEventLoop::instance().enterLoop(40);
5902     QCOMPARE(reply->error(), QNetworkReply::NoError);
5903     QVERIFY(!QTestEventLoop::instance().timeout());
5904 }
5905
5906
5907 void tst_QNetworkReply::getFromHttpIntoBufferCanReadLine()
5908 {
5909     QString header("HTTP/1.0 200 OK\r\nContent-Length: 7\r\n\r\nxxx\nxxx");
5910
5911     MiniHttpServer server(header.toAscii());
5912     server.doClose = true;
5913
5914     QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
5915     request.setAttribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute, 1024*1024*128); // 128 MB is max allowed
5916     QNetworkReplyPtr reply(manager.get(request));
5917
5918     QVERIFY(waitForFinish(reply) == Success);
5919
5920     QCOMPARE(reply->error(), QNetworkReply::NoError);
5921     QVERIFY(reply->canReadLine());
5922     QCOMPARE(reply->read(1), QByteArray("x"));
5923     QVERIFY(reply->canReadLine());
5924     QCOMPARE(reply->read(3), QByteArray("xx\n"));
5925     QVERIFY(!reply->canReadLine());
5926     QCOMPARE(reply->readAll(), QByteArray("xxx"));
5927     QVERIFY(!reply->canReadLine());
5928 }
5929
5930
5931
5932 // Is handled somewhere else too, introduced this special test to have it more accessible
5933 void tst_QNetworkReply::ioGetFromHttpWithoutContentLength()
5934 {
5935     QByteArray dataToSend("HTTP/1.0 200 OK\r\n\r\nHALLO! 123!");
5936     MiniHttpServer server(dataToSend);
5937     server.doClose = true;
5938
5939     QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
5940     QNetworkReplyPtr reply(manager.get(request));
5941
5942     QVERIFY(waitForFinish(reply) == Success);
5943
5944     QCOMPARE(reply->url(), request.url());
5945     QVERIFY(reply->isFinished());
5946     QVERIFY(reply->error() == QNetworkReply::NoError);
5947 }
5948
5949 // Is handled somewhere else too, introduced this special test to have it more accessible
5950 void tst_QNetworkReply::ioGetFromHttpBrokenChunkedEncoding()
5951 {
5952     // This is wrong chunked encoding because of the X. What actually has to follow is \r\n
5953     // and then the declaration of the final 0 chunk
5954     QByteArray dataToSend("HTTP/1.0 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n3\r\nABCX");
5955     MiniHttpServer server(dataToSend);
5956     server.doClose = false; // FIXME
5957
5958     QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
5959     QNetworkReplyPtr reply(manager.get(request));
5960
5961     connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
5962     QTestEventLoop::instance().enterLoop(10);
5963
5964     QEXPECT_FAIL(0, "We should close the socket and not just do nothing", Continue);
5965     QVERIFY(!QTestEventLoop::instance().timeout());
5966     QEXPECT_FAIL(0, "We should close the socket and not just do nothing", Continue);
5967     QVERIFY(reply->isFinished());
5968     QCOMPARE(reply->error(), QNetworkReply::NoError);
5969 }
5970
5971 // TODO:
5972 // Prepare a gzip that has one chunk that expands to the size mentioned in the bugreport.
5973 // Then have a custom HTTP server that waits after this chunk so the returning gets
5974 // triggered.
5975 void tst_QNetworkReply::qtbug12908compressedHttpReply()
5976 {
5977     QString header("HTTP/1.0 200 OK\r\nContent-Encoding: gzip\r\nContent-Length: 63\r\n\r\n");
5978
5979     // dd if=/dev/zero of=qtbug-12908 bs=16384  count=1 && gzip qtbug-12908 && base64 -w 0 qtbug-12908.gz
5980     QString encodedFile("H4sICDdDaUwAA3F0YnVnLTEyOTA4AO3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAIC3AYbSVKsAQAAA");
5981     QByteArray decodedFile = QByteArray::fromBase64(encodedFile.toAscii());
5982     QCOMPARE(decodedFile.size(), 63);
5983
5984     MiniHttpServer server(header.toAscii() + decodedFile);
5985     server.doClose = true;
5986
5987     QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
5988     QNetworkReplyPtr reply(manager.get(request));
5989
5990     QVERIFY(waitForFinish(reply) == Success);
5991
5992     QCOMPARE(reply->error(), QNetworkReply::NoError);
5993     QCOMPARE(reply->size(), qint64(16384));
5994     QCOMPARE(reply->readAll(), QByteArray(16384, '\0'));
5995 }
5996
5997 void tst_QNetworkReply::compressedHttpReplyBrokenGzip()
5998 {
5999     QString header("HTTP/1.0 200 OK\r\nContent-Encoding: gzip\r\nContent-Length: 63\r\n\r\n");
6000
6001     // dd if=/dev/zero of=qtbug-12908 bs=16384  count=1 && gzip qtbug-12908 && base64 -w 0 qtbug-12908.gz
6002     // Then change "BMQ" to "BMX"
6003     QString encodedFile("H4sICDdDaUwAA3F0YnVnLTEyOTA4AO3BMXEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAIC3AYbSVKsAQAAA");
6004     QByteArray decodedFile = QByteArray::fromBase64(encodedFile.toAscii());
6005     QCOMPARE(decodedFile.size(), 63);
6006
6007     MiniHttpServer server(header.toAscii() + decodedFile);
6008     server.doClose = true;
6009
6010     QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
6011     QNetworkReplyPtr reply(manager.get(request));
6012
6013     QVERIFY(waitForFinish(reply) == Failure);
6014
6015     QCOMPARE(reply->error(), QNetworkReply::ProtocolFailure);
6016 }
6017
6018 // TODO add similar test for FTP
6019 void tst_QNetworkReply::getFromUnreachableIp()
6020 {
6021     QNetworkAccessManager manager;
6022
6023     QNetworkRequest request(QUrl("http://255.255.255.255/42/23/narf/narf/narf"));
6024     QNetworkReplyPtr reply(manager.get(request));
6025
6026     QVERIFY(waitForFinish(reply) == Failure);
6027
6028     QVERIFY(reply->error() != QNetworkReply::NoError);
6029 }
6030
6031 void tst_QNetworkReply::qtbug4121unknownAuthentication()
6032 {
6033     MiniHttpServer server(QByteArray("HTTP/1.1 401 bla\r\nWWW-Authenticate: crap\r\nContent-Length: 0\r\n\r\n"));
6034     server.doClose = false;
6035
6036     QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
6037     QNetworkAccessManager manager;
6038     QNetworkReplyPtr reply(manager.get(request));
6039
6040     QSignalSpy authSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
6041     QSignalSpy finishedSpy(&manager, SIGNAL(finished(QNetworkReply*)));
6042     QSignalSpy errorSpy(reply.data(), SIGNAL(error(QNetworkReply::NetworkError)));
6043
6044     connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
6045     QTestEventLoop::instance().enterLoop(10);
6046     QVERIFY(!QTestEventLoop::instance().timeout());
6047
6048     QCOMPARE(authSpy.count(), 0);
6049     QCOMPARE(finishedSpy.count(), 1);
6050     QCOMPARE(errorSpy.count(), 1);
6051
6052     QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
6053 }
6054
6055 void tst_QNetworkReply::authenticationCacheAfterCancel_data()
6056 {
6057     QTest::addColumn<QNetworkProxy>("proxy");
6058     QTest::addColumn<bool>("proxyAuth");
6059     QTest::addColumn<QUrl>("url");
6060     for (int i = 0; i < proxies.count(); ++i) {
6061         QTest::newRow("http" + proxies.at(i).tag) << proxies.at(i).proxy << proxies.at(i).requiresAuthentication << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth/rfc3252.txt");
6062 #ifndef QT_NO_SSL
6063         QTest::newRow("https" + proxies.at(i).tag) << proxies.at(i).proxy << proxies.at(i).requiresAuthentication << QUrl("https://" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth/rfc3252.txt");
6064 #endif
6065     }
6066 }
6067
6068 class AuthenticationCacheHelper : public QObject
6069 {
6070     Q_OBJECT
6071 public:
6072     AuthenticationCacheHelper()
6073     {}
6074 public slots:
6075     void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth)
6076     {
6077         if (!proxyPassword.isNull()) {
6078             auth->setUser(proxyUserName);
6079             auth->setPassword(proxyPassword);
6080             //clear credentials, if they are asked again, they were bad
6081             proxyUserName.clear();
6082             proxyPassword.clear();
6083         }
6084     }
6085     void authenticationRequired(QNetworkReply*,QAuthenticator *auth)
6086     {
6087         if (!httpPassword.isNull()) {
6088             auth->setUser(httpUserName);
6089             auth->setPassword(httpPassword);
6090             //clear credentials, if they are asked again, they were bad
6091             httpUserName.clear();
6092             httpPassword.clear();
6093         }
6094     }
6095 public:
6096     QString httpUserName;
6097     QString httpPassword;
6098     QString proxyUserName;
6099     QString proxyPassword;
6100 };
6101
6102 /* Purpose of this test is to check credentials are cached correctly.
6103  - If user cancels authentication dialog (i.e. nothing is set to the QAuthenticator by the callback) then this is not cached
6104  - if user supplies a wrong password, then this is not cached
6105  - if user supplies a correct user/password combination then this is cached
6106
6107  Test is checking both the proxyAuthenticationRequired and authenticationRequired signals.
6108  */
6109 void tst_QNetworkReply::authenticationCacheAfterCancel()
6110 {
6111     QFETCH(QNetworkProxy, proxy);
6112     QFETCH(bool, proxyAuth);
6113     QFETCH(QUrl, url);
6114     QNetworkAccessManager manager;
6115 #ifndef QT_NO_SSL
6116     connect(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
6117             SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
6118 #endif
6119     manager.setProxy(proxy);
6120     QSignalSpy authSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
6121     QSignalSpy proxyAuthSpy(&manager, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)));
6122
6123     AuthenticationCacheHelper helper;
6124     connect(&manager, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), &helper, SLOT(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)));
6125     connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), &helper, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
6126
6127     QNetworkRequest request(url);
6128     QNetworkReplyPtr reply;
6129     if (proxyAuth) {
6130         //should fail due to no credentials
6131         reply.reset(manager.get(request));
6132         connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
6133         QTestEventLoop::instance().enterLoop(10);
6134         QVERIFY(!QTestEventLoop::instance().timeout());
6135
6136         QCOMPARE(reply->error(), QNetworkReply::ProxyAuthenticationRequiredError);
6137         QCOMPARE(authSpy.count(), 0);
6138         QCOMPARE(proxyAuthSpy.count(), 1);
6139         proxyAuthSpy.clear();
6140
6141         //should fail due to bad credentials
6142         helper.proxyUserName = "qsockstest";
6143         helper.proxyPassword = "badpassword";
6144         reply.reset(manager.get(request));
6145         connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
6146         QTestEventLoop::instance().enterLoop(10);
6147         QVERIFY(!QTestEventLoop::instance().timeout());
6148
6149         QEXPECT_FAIL("http+socksauth", "QTBUG-23136 - danted accepts bad authentication but blocks the connection", Continue);
6150         QEXPECT_FAIL("https+socksauth", "QTBUG-23136 - danted accepts bad authentication but blocks the connection", Continue);
6151
6152         QCOMPARE(reply->error(), QNetworkReply::ProxyAuthenticationRequiredError);
6153         QCOMPARE(authSpy.count(), 0);
6154         QVERIFY(proxyAuthSpy.count() > 0);
6155         proxyAuthSpy.clear();
6156
6157         //QTBUG-23136 workaround
6158         if (proxy.port() == 1081) {
6159 #ifdef QT_BUILD_INTERNAL
6160             QNetworkAccessManagerPrivate::clearCache(&manager);
6161 #else
6162             return; //XFAIL result above
6163 #endif
6164         }
6165
6166         //next proxy auth should succeed, due to correct credentials
6167         helper.proxyUserName = "qsockstest";
6168         helper.proxyPassword = "password";
6169     }
6170
6171     //should fail due to no credentials
6172     reply.reset(manager.get(request));
6173     connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
6174     QTestEventLoop::instance().enterLoop(10);
6175     QVERIFY(!QTestEventLoop::instance().timeout());
6176
6177     QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
6178     QVERIFY(authSpy.count() > 0);
6179     authSpy.clear();
6180     if (proxyAuth) {
6181         QVERIFY(proxyAuthSpy.count() > 0);
6182         proxyAuthSpy.clear();
6183     }
6184
6185     //should fail due to bad credentials
6186     helper.httpUserName = "baduser";
6187     helper.httpPassword = "badpassword";
6188     reply.reset(manager.get(request));
6189     connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
6190     QTestEventLoop::instance().enterLoop(10);
6191     QVERIFY(!QTestEventLoop::instance().timeout());
6192
6193     QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
6194     QVERIFY(authSpy.count() > 0);
6195     authSpy.clear();
6196     if (proxyAuth) {
6197         //should be supplied from cache
6198         QCOMPARE(proxyAuthSpy.count(), 0);
6199         proxyAuthSpy.clear();
6200     }
6201
6202     //next auth should succeed, due to correct credentials
6203     helper.httpUserName = "httptest";
6204     helper.httpPassword = "httptest";
6205
6206     reply.reset(manager.get(request));
6207     connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
6208     QTestEventLoop::instance().enterLoop(10);
6209     QVERIFY(!QTestEventLoop::instance().timeout());
6210
6211     QCOMPARE(reply->error(), QNetworkReply::NoError);
6212     QVERIFY(authSpy.count() > 0);
6213     authSpy.clear();
6214     if (proxyAuth) {
6215         //should be supplied from cache
6216         QCOMPARE(proxyAuthSpy.count(), 0);
6217         proxyAuthSpy.clear();
6218     }
6219
6220     //next auth should use cached credentials
6221     reply.reset(manager.get(request));
6222     connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
6223     QTestEventLoop::instance().enterLoop(10);
6224     QVERIFY(!QTestEventLoop::instance().timeout());
6225
6226     QCOMPARE(reply->error(), QNetworkReply::NoError);
6227     //should be supplied from cache
6228     QCOMPARE(authSpy.count(), 0);
6229     authSpy.clear();
6230     if (proxyAuth) {
6231         //should be supplied from cache
6232         QCOMPARE(proxyAuthSpy.count(), 0);
6233         proxyAuthSpy.clear();
6234     }
6235
6236 }
6237
6238 void tst_QNetworkReply::authenticationWithDifferentRealm()
6239 {
6240     AuthenticationCacheHelper helper;
6241     QNetworkAccessManager manager;
6242 #ifndef QT_NO_SSL
6243     connect(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
6244             SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
6245 #endif
6246     connect(&manager, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), &helper, SLOT(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)));
6247     connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), &helper, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
6248
6249     helper.httpUserName = "httptest";
6250     helper.httpPassword = "httptest";
6251
6252     QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth/rfc3252.txt"));
6253     QNetworkReply* reply = manager.get(request);
6254     connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
6255     QTestEventLoop::instance().enterLoop(10);
6256     QVERIFY(!QTestEventLoop::instance().timeout());
6257     QCOMPARE(reply->error(), QNetworkReply::NoError);
6258
6259     helper.httpUserName = "httptest";
6260     helper.httpPassword = "httptest";
6261
6262     request.setUrl(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/auth-digest/"));
6263     reply = manager.get(request);
6264     connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
6265     QTestEventLoop::instance().enterLoop(10);
6266     QVERIFY(!QTestEventLoop::instance().timeout());
6267     QCOMPARE(reply->error(), QNetworkReply::NoError);
6268 }
6269
6270 class QtBug13431Helper : public QObject {
6271     Q_OBJECT
6272 public:
6273     QNetworkReply* m_reply;
6274     QTimer m_dlTimer;
6275 public slots:
6276     void replyFinished(QNetworkReply*) {
6277         QTestEventLoop::instance().exitLoop();
6278     }
6279
6280     void onReadAndReschedule() {
6281         const qint64 bytesReceived = m_reply->bytesAvailable();
6282         if (bytesReceived && m_reply->readBufferSize()) {
6283            QByteArray data = m_reply->read(bytesReceived);
6284            // reschedule read
6285            const int millisecDelay = static_cast<int>(bytesReceived * 1000 / m_reply->readBufferSize());
6286            m_dlTimer.start(millisecDelay);
6287         }
6288         else {
6289            // reschedule read
6290            m_dlTimer.start(200);
6291         }
6292     }
6293 };
6294
6295 void tst_QNetworkReply::qtbug13431replyThrottling()
6296 {
6297     QtBug13431Helper helper;
6298
6299     QNetworkAccessManager nam;
6300     connect(&nam, SIGNAL(finished(QNetworkReply*)), &helper, SLOT(replyFinished(QNetworkReply*)));
6301
6302     // Download a bigger file
6303     QNetworkRequest netRequest(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/bigfile"));
6304     helper.m_reply = nam.get(netRequest);
6305     // Set the throttle
6306     helper.m_reply->setReadBufferSize(36000);
6307
6308     // Schedule a timer that tries to read
6309
6310     connect(&helper.m_dlTimer, SIGNAL(timeout()), &helper, SLOT(onReadAndReschedule()));
6311     helper.m_dlTimer.setSingleShot(true);
6312     helper.m_dlTimer.start(0);
6313
6314     QTestEventLoop::instance().enterLoop(30);
6315     QVERIFY(!QTestEventLoop::instance().timeout());
6316     QVERIFY(helper.m_reply->isFinished());
6317     QCOMPARE(helper.m_reply->error(), QNetworkReply::NoError);
6318 }
6319
6320 void tst_QNetworkReply::httpWithNoCredentialUsage()
6321 {
6322     QNetworkAccessManager manager;
6323
6324     QSignalSpy authSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
6325     QSignalSpy finishedSpy(&manager, SIGNAL(finished(QNetworkReply*)));
6326
6327     // Get with credentials, to preload authentication cache
6328     {
6329         QNetworkRequest request(QUrl("http://httptest:httptest@" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi"));
6330         QNetworkReplyPtr reply(manager.get(request));
6331         QVERIFY(waitForFinish(reply) == Success);
6332         // credentials in URL, so don't expect authentication signal
6333         QCOMPARE(authSpy.count(), 0);
6334         QCOMPARE(finishedSpy.count(), 1);
6335         finishedSpy.clear();
6336     }
6337
6338     // Get with cached credentials (normal usage)
6339     {
6340         QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi"));
6341         QNetworkReplyPtr reply(manager.get(request));
6342         QVERIFY(waitForFinish(reply) == Success);
6343         // credentials in cache, so don't expect authentication signal
6344         QCOMPARE(authSpy.count(), 0);
6345         QCOMPARE(finishedSpy.count(), 1);
6346         finishedSpy.clear();
6347     }
6348
6349     // Do not use cached credentials (webkit cross origin usage)
6350     {
6351         QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi"));
6352         request.setAttribute(QNetworkRequest::AuthenticationReuseAttribute, QNetworkRequest::Manual);
6353         QNetworkReplyPtr reply(manager.get(request));
6354
6355         QSignalSpy errorSpy(reply.data(), SIGNAL(error(QNetworkReply::NetworkError)));
6356
6357         connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
6358         QTestEventLoop::instance().enterLoop(10);
6359         QVERIFY(!QTestEventLoop::instance().timeout());
6360
6361         // We check if authenticationRequired was emitted, however we do not anything in it so it should be 401
6362         QCOMPARE(authSpy.count(), 1);
6363         QCOMPARE(finishedSpy.count(), 1);
6364         QCOMPARE(errorSpy.count(), 1);
6365
6366         QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
6367     }
6368 }
6369
6370 void tst_QNetworkReply::qtbug15311doubleContentLength()
6371 {
6372     QByteArray response("HTTP/1.0 200 OK\r\nContent-Length: 3\r\nServer: bogus\r\nContent-Length: 3\r\n\r\nABC");
6373     MiniHttpServer server(response);
6374     server.doClose = true;
6375
6376     QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
6377     QNetworkReplyPtr reply(manager.get(request));
6378
6379     QVERIFY(waitForFinish(reply) == Success);
6380
6381     QVERIFY(reply->isFinished());
6382     QCOMPARE(reply->error(), QNetworkReply::NoError);
6383     QCOMPARE(reply->size(), qint64(3));
6384     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(3));
6385     QCOMPARE(reply->rawHeader("Content-length"), QByteArray("3, 3"));
6386     QCOMPARE(reply->readAll(), QByteArray("ABC"));
6387 }
6388
6389 void tst_QNetworkReply::qtbug18232gzipContentLengthZero()
6390 {
6391     QByteArray response("HTTP/1.0 200 OK\r\nContent-Encoding: gzip\r\nContent-Length: 0\r\n\r\n");
6392     MiniHttpServer server(response);
6393     server.doClose = true;
6394
6395     QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
6396     QNetworkReplyPtr reply(manager.get(request));
6397
6398     QVERIFY(waitForFinish(reply) == Success);
6399
6400     QVERIFY(reply->isFinished());
6401     QCOMPARE(reply->error(), QNetworkReply::NoError);
6402     QCOMPARE(reply->size(), qint64(0));
6403     QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(0));
6404     QCOMPARE(reply->readAll(), QByteArray());
6405 }
6406
6407 // Reproduced a crash in QHttpNetworkReplyPrivate::gunzipBodyPartiallyEnd
6408 // where zlib inflateEnd was called for uninitialized zlib stream
6409 void tst_QNetworkReply::qtbug22660gzipNoContentLengthEmptyContent()
6410 {
6411     // Response with no Content-Length in header and empty content
6412     QByteArray response("HTTP/1.0 200 OK\r\nContent-Encoding: gzip\r\n\r\n");
6413     MiniHttpServer server(response);
6414     server.doClose = true;
6415
6416     QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
6417     QNetworkReplyPtr reply(manager.get(request));
6418
6419     QVERIFY(waitForFinish(reply) == Success);
6420
6421     QVERIFY(reply->isFinished());
6422     QCOMPARE(reply->error(), QNetworkReply::NoError);
6423     QCOMPARE(reply->size(), qint64(0));
6424     QVERIFY(!reply->header(QNetworkRequest::ContentLengthHeader).isValid());
6425     QCOMPARE(reply->readAll(), QByteArray());
6426 }
6427
6428 void tst_QNetworkReply::synchronousRequest_data()
6429 {
6430     QTest::addColumn<QUrl>("url");
6431     QTest::addColumn<QString>("expected");
6432     QTest::addColumn<bool>("checkContentLength");
6433     QTest::addColumn<QString>("mimeType");
6434
6435     // ### cache, auth, proxies
6436
6437     QTest::newRow("http")
6438         << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt")
6439         << QString("file:" + testDataDir + "/rfc3252.txt")
6440         << true
6441         << QString("text/plain");
6442
6443     QTest::newRow("http-gzip")
6444         << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/deflate/rfc3252.txt")
6445         << QString("file:" + testDataDir + "/rfc3252.txt")
6446         << false // don't check content length, because it's gzip encoded
6447         //  ### we would need to enflate (un-deflate) the file content and compare the sizes
6448         << QString("text/plain");
6449
6450 #ifndef QT_NO_SSL
6451     QTest::newRow("https")
6452         << QUrl("https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt")
6453         << QString("file:" + testDataDir + "/rfc3252.txt")
6454         << true
6455         << QString("text/plain");
6456 #endif
6457
6458     QTest::newRow("data")
6459         << QUrl(QString::fromLatin1("data:text/plain,hello world"))
6460         << QString("data:hello world")
6461         << true // check content length
6462         << QString("text/plain");
6463
6464     QTest::newRow("simple-file")
6465         << QUrl::fromLocalFile(testDataDir + "/rfc3252.txt")
6466         << QString("file:" + testDataDir + "/rfc3252.txt")
6467         << true
6468         << QString();
6469 }
6470
6471 // FIXME add testcase for failing network etc
6472 void tst_QNetworkReply::synchronousRequest()
6473 {
6474     QFETCH(QUrl, url);
6475     QFETCH(QString, expected);
6476     QFETCH(bool, checkContentLength);
6477     QFETCH(QString, mimeType);
6478
6479     QNetworkRequest request(url);
6480
6481 #ifndef QT_NO_SSL
6482     // workaround for HTTPS requests: add self-signed server cert to list of CA certs,
6483     // since we cannot react to the sslErrors() signal
6484     // to fix this properly we would need to have an ignoreSslErrors() method in the
6485     // QNetworkRequest, see http://bugreports.qt-project.org/browse/QTBUG-14774
6486     if (url.scheme() == "https") {
6487         QSslConfiguration sslConf;
6488         QList<QSslCertificate> certs = QSslCertificate::fromPath(testDataDir + "/certs/qt-test-server-cacert.pem");
6489         sslConf.setCaCertificates(certs);
6490         request.setSslConfiguration(sslConf);
6491     }
6492 #endif
6493
6494     request.setAttribute(
6495             QNetworkRequest::SynchronousRequestAttribute,
6496             true);
6497
6498     QNetworkReplyPtr reply;
6499     QSignalSpy finishedSpy(&manager, SIGNAL(finished(QNetworkReply*)));
6500     QSignalSpy sslErrorsSpy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
6501     RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply, 0));
6502     QVERIFY(reply->isFinished());
6503     QCOMPARE(finishedSpy.count(), 0);
6504     QCOMPARE(sslErrorsSpy.count(), 0);
6505
6506     QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader).toString(), mimeType);
6507
6508     QByteArray expectedContent;
6509
6510     if (expected.startsWith("file:")) {
6511         QString path = expected.mid(5);
6512         QFile file(path);
6513         file.open(QIODevice::ReadOnly);
6514         expectedContent = file.readAll();
6515     } else if (expected.startsWith("data:")) {
6516         expectedContent = expected.mid(5).toUtf8();
6517     }
6518
6519     if (checkContentLength)
6520         QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(expectedContent.size()));
6521     QCOMPARE(reply->readAll(), expectedContent);
6522
6523     reply->deleteLater();
6524 }
6525
6526 #ifndef QT_NO_SSL
6527 void tst_QNetworkReply::synchronousRequestSslFailure()
6528 {
6529     // test that SSL won't be accepted with self-signed certificate,
6530     // and that we do not emit the sslError signal (in the manager that is,
6531     // in the reply we don't care)
6532
6533     QUrl url("https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt");
6534     QNetworkRequest request(url);
6535     request.setAttribute(
6536             QNetworkRequest::SynchronousRequestAttribute,
6537             true);
6538     QNetworkReplyPtr reply;
6539     QSignalSpy sslErrorsSpy(&manager, SIGNAL(sslErrors(QNetworkReply *, const QList<QSslError> &)));
6540     runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply, 0);
6541     QVERIFY(reply->isFinished());
6542     QCOMPARE(reply->error(), QNetworkReply::SslHandshakeFailedError);
6543     QCOMPARE(sslErrorsSpy.count(), 0);
6544 }
6545 #endif
6546
6547 class HttpAbortHelper : public QObject
6548 {
6549     Q_OBJECT
6550 public:
6551     HttpAbortHelper(QNetworkReply *parent)
6552     : QObject(parent)
6553     {
6554         mReply = parent;
6555         connect(parent, SIGNAL(readyRead()), this, SLOT(readyRead()));
6556     }
6557
6558     ~HttpAbortHelper()
6559     {
6560     }
6561
6562 public slots:
6563     void readyRead()
6564     {
6565         mReply->abort();
6566         QMetaObject::invokeMethod(&QTestEventLoop::instance(), "exitLoop", Qt::QueuedConnection);
6567     }
6568
6569 private:
6570     QNetworkReply *mReply;
6571 };
6572
6573 void tst_QNetworkReply::httpAbort()
6574 {
6575     // FIXME Also implement one where we do a big upload and then abort().
6576     // It must not crash either.
6577
6578     // Abort after the first readyRead()
6579     QNetworkRequest request("http://" + QtNetworkSettings::serverName() + "/qtest/bigfile");
6580     QNetworkReplyPtr reply(manager.get(request));
6581     HttpAbortHelper replyHolder(reply.data());
6582     QTestEventLoop::instance().enterLoop(10);
6583     QVERIFY(!QTestEventLoop::instance().timeout());
6584     QCOMPARE(reply->error(), QNetworkReply::OperationCanceledError);
6585     QVERIFY(reply->isFinished());
6586
6587     // Abort immediately after the get()
6588     QNetworkReplyPtr reply2(manager.get(request));
6589     connect(reply2, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
6590     reply2->abort();
6591     QCOMPARE(reply2->error(), QNetworkReply::OperationCanceledError);
6592     QVERIFY(reply2->isFinished());
6593
6594     // Abort after the finished()
6595     QNetworkRequest request3("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt");
6596     QNetworkReplyPtr reply3(manager.get(request3));
6597
6598     QVERIFY(waitForFinish(reply3) == Success);
6599
6600     QVERIFY(reply3->isFinished());
6601     reply3->abort();
6602     QCOMPARE(reply3->error(), QNetworkReply::NoError);
6603 }
6604
6605 void tst_QNetworkReply::dontInsertPartialContentIntoTheCache()
6606 {
6607     QByteArray reply206 =
6608             "HTTP/1.0 206\r\n"
6609             "Connection: keep-alive\r\n"
6610             "Content-Type: text/plain\r\n"
6611             "Cache-control: no-cache\r\n"
6612             "Content-Range: bytes 2-6/8\r\n"
6613             "Content-length: 4\r\n"
6614             "\r\n"
6615             "load";
6616
6617     MiniHttpServer server(reply206);
6618     server.doClose = false;
6619
6620     MySpyMemoryCache *memoryCache = new MySpyMemoryCache(&manager);
6621     manager.setCache(memoryCache);
6622
6623     QUrl url = "http://localhost:" + QString::number(server.serverPort());
6624     QNetworkRequest request(url);
6625     request.setRawHeader("Range", "bytes=2-6");
6626
6627     QNetworkReplyPtr reply(manager.get(request));
6628
6629     QVERIFY(waitForFinish(reply) == Success);
6630
6631     QVERIFY(server.totalConnections > 0);
6632     QCOMPARE(reply->readAll().constData(), "load");
6633     QCOMPARE(memoryCache->m_insertedUrls.count(), 0);
6634 }
6635
6636 void tst_QNetworkReply::httpUserAgent()
6637 {
6638     QByteArray response("HTTP/1.0 200 OK\r\n\r\n");
6639     MiniHttpServer server(response);
6640     server.doClose = true;
6641
6642     QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
6643     request.setHeader(QNetworkRequest::UserAgentHeader, "abcDEFghi");
6644     QNetworkReplyPtr reply(manager.get(request));
6645
6646     QVERIFY(waitForFinish(reply) == Success);
6647
6648     QVERIFY(reply->isFinished());
6649     QCOMPARE(reply->error(), QNetworkReply::NoError);
6650     QVERIFY(server.receivedData.contains("\r\nUser-Agent: abcDEFghi\r\n"));
6651 }
6652
6653 void tst_QNetworkReply::synchronousAuthenticationCache()
6654 {
6655     class MiniAuthServer : public MiniHttpServer {
6656     public:
6657         MiniAuthServer(QThread *thread) : MiniHttpServer(QByteArray(), false, thread) {};
6658         virtual void reply() {
6659
6660             dataToTransmit =
6661                 "HTTP/1.0 401 Unauthorized\r\n"
6662                 "WWW-Authenticate: Basic realm=\"QNetworkAccessManager Test Realm\"\r\n"
6663                 "Content-Length: 4\r\n"
6664                 "Connection: close\r\n"
6665                 "Content-Type: text/plain\r\n"
6666                 "\r\n"
6667                 "auth";
6668             QRegExp rx("Authorization: Basic ([^\r\n]*)\r\n");
6669             if (rx.indexIn(receivedData) > 0) {
6670                 if (QByteArray::fromBase64(rx.cap(1).toLatin1()) == "login:password") {
6671                     dataToTransmit =
6672                           "HTTP/1.0 200 OK\r\n"
6673                           "Content-Type: text/plain\r\n"
6674                           "Content-Length: 2\r\n"
6675                           "\r\n"
6676                           "OK";
6677                 }
6678             }
6679             receivedData.clear();
6680             MiniHttpServer::reply();
6681         }
6682     };
6683
6684     // when using synchronous commands, we need a different event loop for
6685     // the server thread, because the client is never returning to the
6686     // event loop
6687     QScopedPointer<QThread, QThreadCleanup> serverThread(new QThread);
6688     QScopedPointer<MiniHttpServer, QDeleteLaterCleanup> server(new MiniAuthServer(serverThread.data()));
6689     server->doClose = true;
6690
6691     //1)  URL without credentials, we are not authenticated
6692     {
6693         QUrl url = "http://localhost:" + QString::number(server->serverPort()) + "/path";
6694         QNetworkRequest request(url);
6695         request.setAttribute(QNetworkRequest::SynchronousRequestAttribute, true);
6696
6697         QNetworkReplyPtr reply(manager.get(request));
6698         QVERIFY(reply->isFinished());
6699         QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
6700     }
6701
6702     //2)  URL with credentials, we are authenticated
6703     {
6704         QUrl url = "http://login:password@localhost:" + QString::number(server->serverPort()) + "/path2";
6705         QNetworkRequest request(url);
6706         request.setAttribute(QNetworkRequest::SynchronousRequestAttribute, true);
6707
6708         QNetworkReplyPtr reply(manager.get(request));
6709         QVERIFY(reply->isFinished());
6710         QCOMPARE(reply->error(), QNetworkReply::NoError);
6711         QCOMPARE(reply->readAll().constData(), "OK");
6712     }
6713
6714     //3)  URL without credentials, we are authenticated because they are cached
6715     {
6716         QUrl url = "http://localhost:" + QString::number(server->serverPort()) + "/path3";
6717         QNetworkRequest request(url);
6718         request.setAttribute(QNetworkRequest::SynchronousRequestAttribute, true);
6719
6720         QNetworkReplyPtr reply(manager.get(request));
6721         QVERIFY(reply->isFinished());
6722         QCOMPARE(reply->error(), QNetworkReply::NoError);
6723         QCOMPARE(reply->readAll().constData(), "OK");
6724     }
6725 }
6726
6727 void tst_QNetworkReply::pipelining()
6728 {
6729     QString urlString("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/echo.cgi?");
6730     QList<QNetworkReplyPtr> replies;
6731     for (int a = 0; a < 20; a++) {
6732         QNetworkRequest request(urlString + QString::number(a));
6733         request.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, QVariant(true));
6734         replies.append(QNetworkReplyPtr(manager.get(request)));
6735         connect(replies.at(a), SIGNAL(finished()), this, SLOT(pipeliningHelperSlot()));
6736     }
6737     QTestEventLoop::instance().enterLoop(20);
6738     QVERIFY(!QTestEventLoop::instance().timeout());
6739 }
6740
6741 void tst_QNetworkReply::pipeliningHelperSlot() {
6742     static int a = 0;
6743
6744     // check that pipelining was used in at least one of the replies
6745     static bool pipeliningWasUsed = false;
6746     QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
6747     bool pipeliningWasUsedInReply = reply->attribute(QNetworkRequest::HttpPipeliningWasUsedAttribute).toBool();
6748     if (pipeliningWasUsedInReply)
6749         pipeliningWasUsed = true;
6750
6751     // check that the contents match (the response to echo.cgi?3 should return 3 etc.)
6752     QString urlQueryString = reply->url().query();
6753     QString content = reply->readAll();
6754     QVERIFY2(urlQueryString == content, "data corruption with pipelining detected");
6755
6756     a++;
6757
6758     if (a == 20) { // all replies have finished
6759         QTestEventLoop::instance().exitLoop();
6760         QVERIFY2(pipeliningWasUsed, "pipelining was not used in any of the replies when trying to test pipelining");
6761     }
6762 }
6763
6764 void tst_QNetworkReply::closeDuringDownload_data()
6765 {
6766     QTest::addColumn<QUrl>("url");
6767     QTest::newRow("http") << QUrl("http://" + QtNetworkSettings::serverName() + "/bigfile");
6768     QTest::newRow("ftp") << QUrl("ftp://" + QtNetworkSettings::serverName() + "/qtest/bigfile");
6769 }
6770
6771 void tst_QNetworkReply::closeDuringDownload()
6772 {
6773     QFETCH(QUrl, url);
6774     QNetworkRequest request(url);
6775     QNetworkReply* reply = manager.get(request);
6776     connect(reply, SIGNAL(readyRead()), &QTestEventLoop::instance(), SLOT(exitLoop()));
6777     QTestEventLoop::instance().enterLoop(10);
6778     QVERIFY(!QTestEventLoop::instance().timeout());
6779     connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
6780     reply->close();
6781     reply->deleteLater();
6782     QTest::qWait(1000); //cancelling ftp takes some time, this avoids a warning caused by test's cleanup() destroying the connection cache before the abort is finished
6783 }
6784
6785 void tst_QNetworkReply::ftpAuthentication_data()
6786 {
6787     QTest::addColumn<QString>("referenceName");
6788     QTest::addColumn<QString>("url");
6789     QTest::addColumn<int>("error");
6790
6791     QTest::newRow("invalidPassword") << (testDataDir + "/rfc3252.txt") << "ftp://ftptest:invalid@" + QtNetworkSettings::serverName() + "/home/qt-test-server/ftp/qtest/rfc3252.txt" << int(QNetworkReply::AuthenticationRequiredError);
6792     QTest::newRow("validPassword") << (testDataDir + "/rfc3252.txt") << "ftp://ftptest:password@" + QtNetworkSettings::serverName() + "/home/qt-test-server/ftp/qtest/rfc3252.txt" << int(QNetworkReply::NoError);
6793 }
6794
6795 void tst_QNetworkReply::ftpAuthentication()
6796 {
6797     QFETCH(QString, referenceName);
6798     QFETCH(QString, url);
6799     QFETCH(int, error);
6800
6801     QFile reference(referenceName);
6802     QVERIFY(reference.open(QIODevice::ReadOnly));
6803
6804     QNetworkRequest request(url);
6805     QNetworkReplyPtr reply;
6806     runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply);
6807
6808     QCOMPARE(reply->url(), request.url());
6809     QCOMPARE(reply->error(), QNetworkReply::NetworkError(error));
6810 }
6811
6812 void tst_QNetworkReply::backgroundRequest_data()
6813 {
6814     QTest::addColumn<bool>("background");
6815     QTest::addColumn<int>("policy");
6816     QTest::addColumn<QNetworkReply::NetworkError>("error");
6817
6818     QTest::newRow("fg, normal") << false << 0 << QNetworkReply::NoError;
6819     QTest::newRow("bg, normal") << true << 0 << QNetworkReply::NoError;
6820     QTest::newRow("fg, nobg") << false << (int)QNetworkSession::NoBackgroundTrafficPolicy << QNetworkReply::NoError;
6821     QTest::newRow("bg, nobg") << true << (int)QNetworkSession::NoBackgroundTrafficPolicy << QNetworkReply::BackgroundRequestNotAllowedError;
6822
6823 }
6824
6825 void tst_QNetworkReply::backgroundRequest()
6826 {
6827 #ifdef QT_BUILD_INTERNAL
6828     QFETCH(bool, background);
6829     QFETCH(int, policy);
6830     QFETCH(QNetworkReply::NetworkError, error);
6831
6832     QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName()));
6833
6834     if (background)
6835         request.setAttribute(QNetworkRequest::BackgroundRequestAttribute, QVariant::fromValue(true));
6836
6837     //this preconstructs the session so we can change policies in advance
6838     manager.setConfiguration(networkConfiguration);
6839
6840     const QWeakPointer<const QNetworkSession> session = QNetworkAccessManagerPrivate::getNetworkSession(&manager);
6841     QVERIFY(session);
6842     QNetworkSession::UsagePolicies original = session.data()->usagePolicies();
6843     QNetworkSessionPrivate::setUsagePolicies(*const_cast<QNetworkSession *>(session.data()), QNetworkSession::UsagePolicies(policy));
6844
6845     QNetworkReplyPtr reply(manager.get(request));
6846
6847     QVERIFY(waitForFinish(reply) != Timeout);
6848     if (session)
6849         QNetworkSessionPrivate::setUsagePolicies(*const_cast<QNetworkSession *>(session.data()), original);
6850
6851     QVERIFY(reply->isFinished());
6852     QCOMPARE(reply->error(), error);
6853 #endif
6854 }
6855
6856 // NOTE: This test must be last testcase in tst_qnetworkreply!
6857 void tst_QNetworkReply::parentingRepliesToTheApp()
6858 {
6859     QNetworkRequest request (QUrl("http://" + QtNetworkSettings::serverName()));
6860     manager.get(request)->setParent(this); // parent to this object
6861     manager.get(request)->setParent(qApp); // parent to the app
6862 }
6863
6864 QTEST_MAIN(tst_QNetworkReply)
6865
6866 #include "tst_qnetworkreply.moc"