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