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