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