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