1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31#include <QtCore/QCryptographicHash>
32#include <QtCore/QDataStream>
33#include <QtCore/QUrl>
34#include <QtCore/QEventLoop>
35#include <QtCore/QElapsedTimer>
36#include <QtCore/QFile>
37#include <QtCore/QRandomGenerator>
38#include <QtCore/QRegularExpression>
39#include <QtCore/QRegularExpressionMatch>
40#include <QtCore/QSharedPointer>
41#include <QtCore/QScopedPointer>
42#include <QtCore/QTemporaryFile>
43#include <QtNetwork/QTcpServer>
44#include <QtNetwork/QTcpSocket>
45#include <QtNetwork/QLocalSocket>
46#include <QtNetwork/QLocalServer>
47#include <QtNetwork/QHostInfo>
48#include <QtNetwork/QNetworkAccessManager>
49#include <QtNetwork/QNetworkRequest>
50#include <QtNetwork/QNetworkReply>
51#include <QtNetwork/QAbstractNetworkCache>
52#include <QtNetwork/qauthenticator.h>
53#include <QtNetwork/qnetworkaccessmanager.h>
54#include <QtNetwork/qnetworkdiskcache.h>
55#include <QtNetwork/qnetworkrequest.h>
56#include <QtNetwork/qnetworkreply.h>
57#include <QtNetwork/qnetworkcookie.h>
58#include <QtNetwork/QNetworkCookieJar>
59#include <QtNetwork/QHttpPart>
60#include <QtNetwork/QHttpMultiPart>
61#include <QtNetwork/QNetworkProxyQuery>
62#ifndef QT_NO_SSL
63#include <QtNetwork/qsslerror.h>
64#include <QtNetwork/qsslconfiguration.h>
65#ifdef QT_BUILD_INTERNAL
66#include <QtNetwork/private/qsslconfiguration_p.h>
67#endif
68#endif
69#ifndef QT_NO_BEARERMANAGEMENT
70#include <QtNetwork/qnetworkconfigmanager.h>
71#include <QtNetwork/qnetworkconfiguration.h>
72#include <QtNetwork/qnetworksession.h>
73#include <QtNetwork/private/qnetworksession_p.h>
74#endif
75#ifdef QT_BUILD_INTERNAL
76#include <QtNetwork/private/qnetworkreplyimpl_p.h> // implicitly included by qnetworkaccessmanager_p.h currently, but don't rely on that being true forever
77#include <QtNetwork/private/qnetworkaccessmanager_p.h>
78#else
79Q_DECLARE_METATYPE(QSharedPointer<char>)
80#endif
81
82#ifdef Q_OS_UNIX
83# include <sys/types.h>
84# include <unistd.h> // for getuid()
85#endif
86#include <time.h>
87
88#include "../../../network-settings.h"
89
90// Non-OpenSSL backends are not able to report a specific error code
91// for self-signed certificates.
92#ifndef QT_NO_OPENSSL
93#define FLUKE_CERTIFICATE_ERROR QSslError::SelfSignedCertificate
94#else
95#define FLUKE_CERTIFICATE_ERROR QSslError::CertificateUntrusted
96#endif
97
98Q_DECLARE_METATYPE(QAuthenticator*)
99#ifndef QT_NO_NETWORKPROXY
100Q_DECLARE_METATYPE(QNetworkProxyQuery)
101#endif
102
103#include "emulationdetector.h"
104
105typedef QSharedPointer<QNetworkReply> QNetworkReplyPtr;
106
107#ifndef QT_NO_OPENSSL
108QT_BEGIN_NAMESPACE
109void qt_ForceTlsSecurityLevel();
110QT_END_NAMESPACE
111#endif
112
113class MyCookieJar;
114class tst_QNetworkReply: public QObject
115{
116 Q_OBJECT
117
118#ifndef QT_NO_NETWORKPROXY
119 struct ProxyData
120 {
121 ProxyData(const QNetworkProxy &p, const QByteArray &t, bool auth)
122 : tag(t), proxy(p), requiresAuthentication(auth) {}
123 QByteArray tag;
124 QNetworkProxy proxy;
125 bool requiresAuthentication;
126 };
127#endif // !QT_NO_NETWORKPROXY
128
129 static bool seedCreated;
130 static QString createUniqueExtension()
131 {
132 if (!seedCreated) {
133 seedCreated = true; // not thread-safe, but who cares
134 }
135 return QString::number(QTime::currentTime().msecsSinceStartOfDay())
136 + QLatin1Char('-') + QString::number(QCoreApplication::applicationPid())
137 + QLatin1Char('-') + QString::number(QRandomGenerator::global()->generate());
138 }
139
140 static QString tempRedirectReplyStr() {
141 QString s = "HTTP/1.1 307 Temporary Redirect\r\n"
142 "Content-Type: text/plain\r\n"
143 "location: %1\r\n"
144 "\r\n";
145 return s;
146 }
147
148 static const QByteArray httpEmpty200Response;
149 static const QString filePermissionFileName;
150
151 QEventLoop *loop;
152 enum RunSimpleRequestReturn { Timeout = 0, Success, Failure };
153 int returnCode;
154 QString testFileName;
155 QString echoProcessDir;
156#if !defined Q_OS_WIN
157 QString wronlyFileName;
158#endif
159 QString uniqueExtension;
160#ifndef QT_NO_NETWORKPROXY
161 QList<ProxyData> proxies;
162#endif
163 QNetworkAccessManager manager;
164 MyCookieJar *cookieJar;
165#ifndef QT_NO_SSL
166 QSslConfiguration storedSslConfiguration;
167 QList<QSslError> storedExpectedSslErrors;
168 static const QString certsFilePath;
169#endif
170#ifndef QT_NO_BEARERMANAGEMENT
171 QNetworkConfigurationManager *netConfMan;
172 QNetworkConfiguration networkConfiguration;
173 QScopedPointer<QNetworkSession> networkSession;
174#endif
175
176 using QObject::connect;
177 static bool connect(const QNetworkReplyPtr &ptr, const char *signal, const QObject *receiver, const char *slot, Qt::ConnectionType ct = Qt::AutoConnection)
178 { return connect(sender: ptr.data(), signal, receiver, member: slot, ct); }
179 bool connect(const QNetworkReplyPtr &ptr, const char *signal, const char *slot, Qt::ConnectionType ct = Qt::AutoConnection)
180 { return connect(asender: ptr.data(), asignal: signal, amember: slot, atype: ct); }
181
182public:
183 tst_QNetworkReply();
184 ~tst_QNetworkReply();
185 QString runSimpleRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request,
186 QNetworkReplyPtr &reply, const QByteArray &data = QByteArray());
187 QString runMultipartRequest(const QNetworkRequest &request, QNetworkReplyPtr &reply,
188 QHttpMultiPart *multiPart, const QByteArray &verb);
189
190 QString runCustomRequest(const QNetworkRequest &request, QNetworkReplyPtr &reply,
191 const QByteArray &verb, QIODevice *data);
192 int waitForFinish(QNetworkReplyPtr &reply);
193
194public Q_SLOTS:
195 void finished();
196 void gotError();
197 void authenticationRequired(QNetworkReply*,QAuthenticator*);
198 void proxyAuthenticationRequired(const QNetworkProxy &,QAuthenticator*);
199 void pipeliningHelperSlot();
200 void emitErrorForAllRepliesSlot();
201
202#ifndef QT_NO_SSL
203 void sslErrors(QNetworkReply*,const QList<QSslError> &);
204 void storeSslConfiguration();
205 void ignoreSslErrorListSlot(QNetworkReply *reply, const QList<QSslError> &);
206#ifdef QT_BUILD_INTERNAL
207 void sslSessionSharingHelperSlot();
208#endif
209#endif
210
211protected Q_SLOTS:
212 void nestedEventLoops_slot();
213 void notEnoughData();
214
215private Q_SLOTS:
216 void cleanup() { cleanupTestData(); }
217 void initTestCase();
218 void cleanupTestCase();
219
220 void stateChecking();
221 void invalidProtocol();
222 void getFromData_data();
223 void getFromData();
224 void getFromFile_data();
225 void getFromFile();
226 void getFromFileSpecial_data();
227 void getFromFileSpecial();
228 void getFromFtp_data();
229 void getFromFtp();
230 void getFromFtpAfterError(); // QTBUG-40797
231 void getFromHttp_data();
232 void getFromHttp();
233 void getErrors_data();
234 void getErrors();
235#ifndef QT_NO_NETWORKPROXY
236 void headFromHttp_data();
237 void headFromHttp();
238#endif // !QT_NO_NETWORKPROXY
239 void putToFile_data();
240 void putToFile();
241 void putToFtp_data();
242 void putToFtp();
243 void putToFtpWithInvalidCredentials(); // QTBUG-40622
244 void putToHttp_data();
245 void putToHttp();
246 void putToHttpSynchronous_data();
247 void putToHttpSynchronous();
248 void putToHttpMultipart_data();
249 void putToHttpMultipart();
250 void postToHttp_data();
251 void postToHttp();
252 void postToHttpSynchronous_data();
253 void postToHttpSynchronous();
254 void postToHttpMultipart_data();
255 void postToHttpMultipart();
256 void multipartSkipIndices(); // QTBUG-32534
257#ifndef QT_NO_SSL
258 void putToHttps_data();
259 void putToHttps();
260 void putToHttpsSynchronous_data();
261 void putToHttpsSynchronous();
262 void postToHttps_data();
263 void postToHttps();
264 void postToHttpsSynchronous_data();
265 void postToHttpsSynchronous();
266 void postToHttpsMultipart_data();
267 void postToHttpsMultipart();
268#endif
269 void deleteFromHttp_data();
270 void deleteFromHttp();
271 void putGetDeleteGetFromHttp_data();
272 void putGetDeleteGetFromHttp();
273 void sendCustomRequestToHttp_data();
274 void sendCustomRequestToHttp();
275 void connectToIPv6Address_data();
276 void connectToIPv6Address();
277
278 void ioGetFromData_data();
279 void ioGetFromData();
280 void ioGetFromFileSpecial_data();
281 void ioGetFromFileSpecial();
282 void ioGetFromFile_data();
283 void ioGetFromFile();
284 void ioGetFromFtp_data();
285 void ioGetFromFtp();
286 void ioGetFromFtpWithReuse();
287 void ioGetFromHttp();
288
289 void ioGetFromBuiltinHttp_data();
290 void ioGetFromBuiltinHttp();
291 void ioGetFromHttpWithReuseParallel();
292 void ioGetFromHttpWithReuseSequential();
293 void ioGetFromHttpWithAuth_data();
294 void ioGetFromHttpWithAuth();
295 void ioGetFromHttpWithAuthSynchronous();
296#ifndef QT_NO_NETWORKPROXY
297 void ioGetFromHttpWithProxyAuth();
298 void ioGetFromHttpWithProxyAuthSynchronous();
299 void ioGetFromHttpWithSocksProxy();
300#endif // !QT_NO_NETWORKPROXY
301#ifndef QT_NO_SSL
302 void ioGetFromHttpsWithSslErrors();
303 void ioGetFromHttpsWithIgnoreSslErrors();
304 void ioGetFromHttpsWithSslHandshakeError();
305#endif
306 void ioGetFromHttpBrokenServer_data();
307 void ioGetFromHttpBrokenServer();
308 void ioGetFromHttpStatus100_data();
309 void ioGetFromHttpStatus100();
310 void ioGetFromHttpNoHeaders_data();
311 void ioGetFromHttpNoHeaders();
312 void ioGetFromHttpWithCache_data();
313 void ioGetFromHttpWithCache();
314
315#ifndef QT_NO_NETWORKPROXY
316 void ioGetWithManyProxies_data();
317 void ioGetWithManyProxies();
318#endif // !QT_NO_NETWORKPROXY
319
320 void ioPutToFileFromFile_data();
321 void ioPutToFileFromFile();
322 void ioPutToFileFromSocket_data();
323 void ioPutToFileFromSocket();
324 void ioPutToFileFromLocalSocket_data();
325 void ioPutToFileFromLocalSocket();
326 void ioPutToFileFromProcess_data();
327 void ioPutToFileFromProcess();
328 void ioPutToFtpFromFile_data();
329 void ioPutToFtpFromFile();
330 void ioPutToHttpFromFile_data();
331 void ioPutToHttpFromFile();
332 void ioPostToHttpFromFile_data();
333 void ioPostToHttpFromFile();
334#ifndef QT_NO_NETWORKPROXY
335 void ioPostToHttpFromSocket_data();
336 void ioPostToHttpFromSocket();
337 void ioPostToHttpFromSocketSynchronous();
338 void ioPostToHttpFromSocketSynchronous_data();
339#endif // !QT_NO_NETWORKPROXY
340 void ioPostToHttpFromMiddleOfFileToEnd();
341 void ioPostToHttpFromMiddleOfFileFiveBytes();
342 void ioPostToHttpFromMiddleOfQBufferFiveBytes();
343 void ioPostToHttpNoBufferFlag();
344 void ioPostToHttpUploadProgress();
345 void emitAllUploadProgressSignals();
346 void ioPostToHttpEmptyUploadProgress();
347
348 void lastModifiedHeaderForFile();
349 void lastModifiedHeaderForHttp();
350
351 void httpCanReadLine();
352
353#ifdef QT_BUILD_INTERNAL
354 void rateControl_data();
355 void rateControl();
356#endif
357
358 void downloadProgress_data();
359 void downloadProgress();
360#ifdef QT_BUILD_INTERNAL
361 void uploadProgress_data();
362 void uploadProgress();
363#endif
364
365 void chaining_data();
366 void chaining();
367
368 void receiveCookiesFromHttp_data();
369 void receiveCookiesFromHttp();
370 void receiveCookiesFromHttpSynchronous_data();
371 void receiveCookiesFromHttpSynchronous();
372 void sendCookies_data();
373 void sendCookies();
374 void sendCookiesSynchronous_data();
375 void sendCookiesSynchronous();
376
377 void nestedEventLoops();
378
379#ifndef QT_NO_NETWORKPROXY
380 void httpProxyCommands_data();
381 void httpProxyCommands();
382 void httpProxyCommandsSynchronous_data();
383 void httpProxyCommandsSynchronous();
384 void proxyChange();
385#endif // !QT_NO_NETWORKPROXY
386 void authorizationError_data();
387 void authorizationError();
388
389 void httpConnectionCount();
390
391 void httpReUsingConnectionSequential_data();
392 void httpReUsingConnectionSequential();
393 void httpReUsingConnectionFromFinishedSlot_data();
394 void httpReUsingConnectionFromFinishedSlot();
395
396 void httpRecursiveCreation();
397
398#ifndef QT_NO_SSL
399 void ioPostToHttpsUploadProgress();
400 void ignoreSslErrorsList_data();
401 void ignoreSslErrorsList();
402 void ignoreSslErrorsListWithSlot_data();
403 void ignoreSslErrorsListWithSlot();
404 void encrypted();
405 void abortOnEncrypted();
406 void sslConfiguration_data();
407 void sslConfiguration();
408#ifdef QT_BUILD_INTERNAL
409 void sslSessionSharing_data();
410 void sslSessionSharing();
411 void sslSessionSharingFromPersistentSession_data();
412 void sslSessionSharingFromPersistentSession();
413#endif
414#endif
415
416 void getAndThenDeleteObject_data();
417 void getAndThenDeleteObject();
418
419 void symbianOpenCDataUrlCrash();
420
421 void getFromHttpIntoBuffer_data();
422 void getFromHttpIntoBuffer();
423 void getFromHttpIntoBuffer2_data();
424 void getFromHttpIntoBuffer2();
425 void getFromHttpIntoBufferCanReadLine();
426
427 void ioGetFromHttpWithoutContentLength();
428
429 void ioGetFromHttpBrokenChunkedEncoding();
430 void qtbug12908compressedHttpReply();
431 void compressedHttpReplyBrokenGzip();
432
433 void getFromUnreachableIp();
434
435 void qtbug4121unknownAuthentication();
436
437 void qtbug13431replyThrottling();
438
439 void httpWithNoCredentialUsage();
440
441 void qtbug15311doubleContentLength();
442
443 void qtbug18232gzipContentLengthZero();
444 void qtbug22660gzipNoContentLengthEmptyContent();
445
446 void qtbug27161httpHeaderMayBeDamaged_data();
447 void qtbug27161httpHeaderMayBeDamaged();
448
449 void qtbug28035browserDoesNotLoadQtProjectOrgCorrectly();
450
451 void qtbug45581WrongReplyStatusCode();
452
453 void synchronousRequest_data();
454 void synchronousRequest();
455#ifndef QT_NO_SSL
456 void synchronousRequestSslFailure();
457#endif
458
459 void httpAbort();
460
461 void dontInsertPartialContentIntoTheCache();
462
463 void httpUserAgent();
464#ifndef QT_NO_NETWORKPROXY
465 void authenticationCacheAfterCancel_data();
466 void authenticationCacheAfterCancel();
467 void authenticationWithDifferentRealm();
468#endif // !QT_NO_NETWORKPROXY
469 void synchronousAuthenticationCache();
470 void pipelining();
471
472 void closeDuringDownload_data();
473 void closeDuringDownload();
474
475 void ftpAuthentication_data();
476 void ftpAuthentication();
477
478 void emitErrorForAllReplies(); // QTBUG-36890
479
480#ifdef QT_BUILD_INTERNAL
481 void backgroundRequest_data();
482 void backgroundRequest();
483 void backgroundRequestInterruption_data();
484 void backgroundRequestInterruption();
485 void backgroundRequestConnectInBackground_data();
486 void backgroundRequestConnectInBackground();
487#endif
488
489 void putWithRateLimiting();
490
491 void ioHttpSingleRedirect();
492 void ioHttpChangeMaxRedirects();
493 void ioHttpRedirectErrors_data();
494 void ioHttpRedirectErrors();
495 void ioHttpRedirectPolicy_data();
496 void ioHttpRedirectPolicy();
497 void ioHttpRedirectPolicyErrors_data();
498 void ioHttpRedirectPolicyErrors();
499 void ioHttpUserVerifiedRedirect_data();
500 void ioHttpUserVerifiedRedirect();
501 void ioHttpCookiesDuringRedirect();
502 void ioHttpRedirect_data();
503 void ioHttpRedirect();
504 void ioHttpRedirectFromLocalToRemote();
505 void ioHttpRedirectPostPut_data();
506 void ioHttpRedirectPostPut();
507 void ioHttpRedirectMultipartPost_data();
508 void ioHttpRedirectMultipartPost();
509 void ioHttpRedirectDelete();
510 void ioHttpRedirectCustom();
511 void ioHttpRedirectWithUploadDevice_data();
512 void ioHttpRedirectWithUploadDevice();
513#ifndef QT_NO_SSL
514 void putWithServerClosingConnectionImmediately();
515#endif
516
517 void autoDeleteRepliesAttribute_data();
518 void autoDeleteRepliesAttribute();
519 void autoDeleteReplies_data();
520 void autoDeleteReplies();
521
522 void getWithTimeout();
523 void postWithTimeout();
524 // NOTE: This test must be last!
525 void parentingRepliesToTheApp();
526private:
527 void cleanupTestData();
528
529 QString testDataDir;
530 bool notEnoughDataForFastSender;
531};
532
533const QByteArray tst_QNetworkReply::httpEmpty200Response =
534 "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n";
535const QString tst_QNetworkReply::filePermissionFileName = "/etc/shadow";
536
537bool tst_QNetworkReply::seedCreated = false;
538
539#define RUN_REQUEST(call) \
540 do { \
541 QString errorMsg = call; \
542 if (!errorMsg.isEmpty()) \
543 QFAIL(qPrintable(errorMsg)); \
544 } while (0)
545
546static bool validateRedirectedResponseHeaders(QNetworkReplyPtr reply)
547{
548 // QTBUG-61300: previously we were mixing 'raw' headers from all responses
549 // along the redirect chain. The simplest test is to check/verify we have
550 // no 'location' header anymore.
551 Q_ASSERT(reply.data());
552
553 return !reply->hasRawHeader(headerName: "location")
554 && !reply->header(header: QNetworkRequest::LocationHeader).isValid();
555}
556
557#ifndef QT_NO_SSL
558static void setupSslServer(QSslSocket* serverSocket)
559{
560 QString testDataDir = QFileInfo(QFINDTESTDATA("rfc3252.txt")).absolutePath();
561 if (testDataDir.isEmpty())
562 testDataDir = QCoreApplication::applicationDirPath();
563
564 serverSocket->setProtocol(QSsl::AnyProtocol);
565 serverSocket->setLocalCertificate(fileName: testDataDir + "/certs/server.pem");
566 serverSocket->setPrivateKey(fileName: testDataDir + "/certs/server.key");
567 serverSocket->startServerEncryption();
568}
569
570#ifdef QT_TEST_SERVER
571#ifdef QT_TEST_SERVER_NAME
572// In this case, each server is assigned a unique hostname. Use the wildcard SSL
573// certificate (*.test-net.qt.local).
574const QString tst_QNetworkReply::certsFilePath = "/certs/qt-test-net-cacert.pem";
575#else
576// Otherwise, select the single-name SSL certificate (qt-test-server.local) instead.
577const QString tst_QNetworkReply::certsFilePath = "/certs/qt-test-server-host-network-cacert.pem";
578#endif // QT_TEST_SERVER_NAME
579#else
580const QString tst_QNetworkReply::certsFilePath = "/certs/qt-test-server-cacert.pem";
581#endif
582
583#endif // !QT_NO_SSL
584
585// NOTE: MiniHttpServer has a very limited support of PUT/POST requests! Make
586// sure you understand the server's code before PUTting/POSTing data (and
587// probably you'll have to update the logic).
588class MiniHttpServer: public QTcpServer
589{
590 Q_OBJECT
591public:
592 QPointer<QTcpSocket> client; // always the last one that was received
593 QByteArray dataToTransmit;
594 QByteArray receivedData;
595 QSemaphore ready;
596 bool doClose;
597 bool doSsl;
598 bool ipv6;
599 bool multiple;
600 int totalConnections;
601
602 bool stopTransfer = false;
603 bool hasContent = false;
604 int contentRead = 0;
605 int contentLength = 0;
606
607 MiniHttpServer(const QByteArray &data, bool ssl = false, QThread *thread = 0, bool useipv6 = false)
608 : dataToTransmit(data), doClose(true), doSsl(ssl), ipv6(useipv6),
609 multiple(false), totalConnections(0)
610 {
611 if (useipv6) {
612 if (!listen(address: QHostAddress::AnyIPv6))
613 qWarning() << "listen() IPv6 failed" << errorString();
614 } else {
615 if (!listen(address: QHostAddress::AnyIPv4))
616 qWarning() << "listen() IPv4 failed" << errorString();
617 }
618 if (thread) {
619 connect(sender: thread, SIGNAL(started()), receiver: this, SLOT(threadStartedSlot()));
620 moveToThread(thread);
621 thread->start();
622 ready.acquire();
623 }
624 }
625
626 void setDataToTransmit(const QByteArray &data)
627 {
628 dataToTransmit = data;
629 }
630
631 void clearHeaderParserState()
632 {
633 contentLength = 0;
634 receivedData.clear();
635 }
636
637protected:
638 void incomingConnection(qintptr socketDescriptor)
639 {
640 //qDebug() << "incomingConnection" << socketDescriptor << "doSsl:" << doSsl << "ipv6:" << ipv6;
641#ifndef QT_NO_SSL
642 if (doSsl) {
643 QSslSocket *serverSocket = new QSslSocket(this);
644 if (!serverSocket->setSocketDescriptor(socketDescriptor)) {
645 delete serverSocket;
646 return;
647 }
648 connect(sender: serverSocket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(slotSslErrors(QList<QSslError>)));
649 // connect(serverSocket, &QSslSocket::encrypted, this, &SslServer::ready); ?
650 setupSslServer(serverSocket);
651 client = serverSocket;
652 } else
653#endif
654 {
655 client = new QTcpSocket;
656 client->setSocketDescriptor(socketDescriptor);
657 }
658 connectSocketSignals();
659 client->setParent(this);
660 ++totalConnections;
661 }
662
663 virtual void reply()
664 {
665 Q_ASSERT(!client.isNull());
666 // we need to emulate the bytesWrittenSlot call if the data is empty.
667 if (dataToTransmit.size() == 0) {
668 emit client->bytesWritten(bytes: 0);
669 } else if (!stopTransfer) {
670 client->write(data: dataToTransmit);
671 // FIXME: For SSL connections, if we don't flush the socket, the
672 // client never receives the data and since we're doing a disconnect
673 // immediately afterwards, it causes a RemoteHostClosedError for the
674 // client
675 client->flush();
676 }
677 }
678private:
679 void connectSocketSignals()
680 {
681 Q_ASSERT(!client.isNull());
682 //qDebug() << "connectSocketSignals" << client;
683 connect(sender: client.data(), SIGNAL(readyRead()), receiver: this, SLOT(readyReadSlot()));
684 connect(sender: client.data(), SIGNAL(bytesWritten(qint64)), receiver: this, SLOT(bytesWrittenSlot()));
685 connect(sender: client.data(), SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
686 receiver: this, SLOT(slotError(QAbstractSocket::SocketError)));
687 }
688
689 void parseContentLength()
690 {
691 int index = receivedData.indexOf(c: "Content-Length:");
692 index += sizeof("Content-Length:") - 1;
693 const auto end = std::find(first: receivedData.cbegin() + index, last: receivedData.cend(), val: '\r');
694 auto num = receivedData.mid(index, len: std::distance(first: receivedData.cbegin() + index, last: end));
695 bool ok;
696 contentLength = num.toInt(ok: &ok);
697 if (!ok)
698 contentLength = -1;
699 }
700
701private slots:
702#ifndef QT_NO_SSL
703 void slotSslErrors(const QList<QSslError>& errors)
704 {
705 QTcpSocket *currentClient = qobject_cast<QTcpSocket *>(object: sender());
706 Q_ASSERT(currentClient);
707 qDebug() << "slotSslErrors" << currentClient->errorString() << errors;
708 }
709#endif
710 void slotError(QAbstractSocket::SocketError err)
711 {
712 QTcpSocket *currentClient = qobject_cast<QTcpSocket *>(object: sender());
713 Q_ASSERT(currentClient);
714 qDebug() << "slotError" << err << currentClient->errorString();
715 }
716
717public slots:
718
719 void readyReadSlot()
720 {
721 QTcpSocket *currentClient = qobject_cast<QTcpSocket *>(object: sender());
722 Q_ASSERT(currentClient);
723 if (currentClient != client)
724 client = currentClient;
725 if (stopTransfer)
726 return;
727 receivedData += client->readAll();
728 const int doubleEndlPos = receivedData.indexOf(c: "\r\n\r\n");
729
730 if (doubleEndlPos != -1) {
731 const int endOfHeader = doubleEndlPos + 4;
732 hasContent = receivedData.startsWith(c: "POST") || receivedData.startsWith(c: "PUT")
733 || receivedData.startsWith(c: "CUSTOM_WITH_PAYLOAD");
734 if (hasContent && contentLength == 0)
735 parseContentLength();
736 contentRead = receivedData.length() - endOfHeader;
737 if (hasContent && contentRead < contentLength)
738 return;
739
740 // multiple requests incoming. remove the bytes of the current one
741 if (multiple)
742 receivedData.remove(index: 0, len: endOfHeader);
743
744 reply();
745 }
746 }
747
748 void bytesWrittenSlot()
749 {
750 Q_ASSERT(!client.isNull());
751 // Disconnect and delete in next cycle (else Windows clients will fail with RemoteHostClosedError).
752 if (doClose && client->bytesToWrite() == 0) {
753 disconnect(sender: client, signal: 0, receiver: this, member: 0);
754 client->deleteLater();
755 }
756 }
757
758 void threadStartedSlot()
759 {
760 ready.release();
761 }
762};
763
764class MyCookieJar: public QNetworkCookieJar
765{
766public:
767 inline QList<QNetworkCookie> allCookies() const
768 { return QNetworkCookieJar::allCookies(); }
769 inline void setAllCookies(const QList<QNetworkCookie> &cookieList)
770 { QNetworkCookieJar::setAllCookies(cookieList); }
771};
772
773#ifndef QT_NO_NETWORKPROXY
774class MyProxyFactory: public QNetworkProxyFactory
775{
776public:
777 int callCount;
778 QList<QNetworkProxy> toReturn;
779 QNetworkProxyQuery lastQuery;
780 inline MyProxyFactory() { clear(); }
781
782 inline void clear()
783 {
784 callCount = 0;
785 toReturn = QList<QNetworkProxy>() << QNetworkProxy::DefaultProxy;
786 lastQuery = QNetworkProxyQuery();
787 }
788
789 virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query)
790 {
791 lastQuery = query;
792 ++callCount;
793 return toReturn;
794 }
795};
796#endif // !QT_NO_NETWORKPROXY
797
798class MyMemoryCache: public QAbstractNetworkCache
799{
800public:
801 typedef QPair<QNetworkCacheMetaData, QByteArray> CachedContent;
802 typedef QHash<QByteArray, CachedContent> CacheData;
803 CacheData cache;
804
805 MyMemoryCache(QObject *parent) : QAbstractNetworkCache(parent) {}
806
807 QNetworkCacheMetaData metaData(const QUrl &url)
808 {
809 return cache.value(key: url.toEncoded()).first;
810 }
811
812 void updateMetaData(const QNetworkCacheMetaData &metaData)
813 {
814 cache[metaData.url().toEncoded()].first = metaData;
815 }
816
817 QIODevice *data(const QUrl &url)
818 {
819 CacheData::ConstIterator it = cache.find(key: url.toEncoded());
820 if (it == cache.constEnd())
821 return 0;
822 QBuffer *io = new QBuffer(this);
823 io->setData(it->second);
824 io->open(openMode: QIODevice::ReadOnly);
825 io->seek(off: 0);
826 return io;
827 }
828
829 bool remove(const QUrl &url)
830 {
831 cache.remove(key: url.toEncoded());
832 return true;
833 }
834
835 qint64 cacheSize() const
836 {
837 qint64 total = 0;
838 foreach (const CachedContent &entry, cache)
839 total += entry.second.size();
840 return total;
841 }
842
843 QIODevice *prepare(const QNetworkCacheMetaData &)
844 {
845 qFatal(msg: "%s: Should not have tried to add to the cache", Q_FUNC_INFO);
846 return 0;
847 }
848 void insert(QIODevice *)
849 {
850 qFatal(msg: "%s: Should not have tried to add to the cache", Q_FUNC_INFO);
851 }
852
853 void clear() { cache.clear(); }
854};
855Q_DECLARE_METATYPE(MyMemoryCache::CachedContent)
856Q_DECLARE_METATYPE(MyMemoryCache::CacheData)
857
858class MySpyMemoryCache: public QAbstractNetworkCache
859{
860public:
861 MySpyMemoryCache(QObject *parent) : QAbstractNetworkCache(parent) {}
862 ~MySpyMemoryCache()
863 {
864 qDeleteAll(c: m_buffers);
865 m_buffers.clear();
866 }
867
868 QHash<QUrl, QIODevice*> m_buffers;
869 QList<QUrl> m_insertedUrls;
870
871 QNetworkCacheMetaData metaData(const QUrl &)
872 {
873 return QNetworkCacheMetaData();
874 }
875
876 void updateMetaData(const QNetworkCacheMetaData &)
877 {
878 }
879
880 QIODevice *data(const QUrl &)
881 {
882 return 0;
883 }
884
885 bool remove(const QUrl &url)
886 {
887 delete m_buffers.take(key: url);
888 return m_insertedUrls.removeAll(t: url) > 0;
889 }
890
891 qint64 cacheSize() const
892 {
893 return 0;
894 }
895
896 QIODevice *prepare(const QNetworkCacheMetaData &metaData)
897 {
898 QBuffer* buffer = new QBuffer;
899 buffer->open(openMode: QIODevice::ReadWrite);
900 buffer->setProperty(name: "url", value: metaData.url());
901 m_buffers.insert(key: metaData.url(), value: buffer);
902 return buffer;
903 }
904
905 void insert(QIODevice *buffer)
906 {
907 QUrl url = buffer->property(name: "url").toUrl();
908 m_insertedUrls << url;
909 delete m_buffers.take(key: url);
910 }
911
912 void clear() { m_insertedUrls.clear(); }
913};
914
915class DataReader: public QObject
916{
917 Q_OBJECT
918public:
919 qint64 totalBytes;
920 QByteArray data;
921 QIODevice *device;
922 bool accumulate;
923 DataReader(const QNetworkReplyPtr &dev, bool acc = true) : totalBytes(0), device(dev.data()), accumulate(acc)
924 { connect(asender: device, SIGNAL(readyRead()), SLOT(doRead()) ); }
925 DataReader(QIODevice *dev, bool acc = true) : totalBytes(0), device(dev), accumulate(acc)
926 {
927 connect(asender: device, SIGNAL(readyRead()), SLOT(doRead()));
928 }
929
930public slots:
931 void doRead()
932 {
933 QByteArray buffer;
934 buffer.resize(size: device->bytesAvailable());
935 qint64 bytesRead = device->read(data: buffer.data(), maxlen: device->bytesAvailable());
936 if (bytesRead == -1) {
937 QTestEventLoop::instance().exitLoop();
938 return;
939 }
940 buffer.truncate(pos: bytesRead);
941 totalBytes += bytesRead;
942
943 if (accumulate)
944 data += buffer;
945 }
946};
947
948
949class SocketPair: public QObject
950{
951 Q_OBJECT
952public:
953 QIODevice *endPoints[2];
954
955 SocketPair(QObject *parent = 0)
956 : QObject(parent)
957 {
958 endPoints[0] = endPoints[1] = 0;
959 }
960
961 bool create()
962 {
963 QTcpServer server;
964 server.listen();
965
966 QTcpSocket *active = new QTcpSocket(this);
967 active->connectToHost(hostName: "127.0.0.1", port: server.serverPort());
968
969 // need more time as working with embedded
970 // device and testing from emualtor
971 // things tend to get slower
972 if (!active->waitForConnected(msecs: 1000))
973 return false;
974
975 if (!server.waitForNewConnection(msec: 1000))
976 return false;
977
978 QTcpSocket *passive = server.nextPendingConnection();
979 passive->setParent(this);
980
981 endPoints[0] = active;
982 endPoints[1] = passive;
983 return true;
984 }
985};
986
987// A blocking tcp server (must be used in a thread) which supports SSL.
988class BlockingTcpServer : public QTcpServer
989{
990 Q_OBJECT
991public:
992 BlockingTcpServer(bool ssl) : doSsl(ssl), sslSocket(0) {}
993
994 QTcpSocket* waitForNextConnectionSocket()
995 {
996 waitForNewConnection(msec: -1);
997 if (doSsl) {
998 if (!sslSocket)
999 qFatal(msg: "%s: sslSocket should not be null after calling waitForNewConnection()",
1000 Q_FUNC_INFO);
1001 return sslSocket;
1002 } else {
1003 //qDebug() << "returning nextPendingConnection";
1004 return nextPendingConnection();
1005 }
1006 }
1007 virtual void incomingConnection(qintptr socketDescriptor)
1008 {
1009#ifndef QT_NO_SSL
1010 if (doSsl) {
1011 QSslSocket *serverSocket = new QSslSocket;
1012 serverSocket->setParent(this);
1013 serverSocket->setSocketDescriptor(socketDescriptor);
1014 connect(sender: serverSocket, SIGNAL(sslErrors(QList<QSslError>)), receiver: this, SLOT(slotSslErrors(QList<QSslError>)));
1015 setupSslServer(serverSocket);
1016 sslSocket = serverSocket;
1017 } else
1018#endif
1019 {
1020 QTcpServer::incomingConnection(handle: socketDescriptor);
1021 }
1022 }
1023private slots:
1024
1025#ifndef QT_NO_SSL
1026 void slotSslErrors(const QList<QSslError>& errors)
1027 {
1028 qDebug() << "slotSslErrors" << sslSocket->errorString() << errors;
1029 }
1030#endif
1031
1032private:
1033 const bool doSsl;
1034 QTcpSocket* sslSocket;
1035};
1036
1037// This server tries to send data as fast as possible (like most servers)
1038// but it measures how fast it was able to send it, which shows at which
1039// rate the reader is processing the data.
1040class FastSender: public QThread
1041{
1042 Q_OBJECT
1043 QSemaphore ready;
1044 qint64 wantedSize;
1045 int port;
1046 enum Protocol { DebugPipe, ProvidedData };
1047 const Protocol protocol;
1048 const bool doSsl;
1049 const bool fillKernelBuffer;
1050public:
1051 int transferRate;
1052 QWaitCondition cond;
1053
1054 QByteArray dataToTransmit;
1055 int dataIndex;
1056
1057 // a server that sends debugpipe data
1058 FastSender(qint64 size)
1059 : wantedSize(size), port(-1), protocol(DebugPipe),
1060 doSsl(false), fillKernelBuffer(true), transferRate(-1),
1061 dataIndex(0)
1062 {
1063 start();
1064 ready.acquire();
1065 }
1066
1067 // a server that sends the data provided at construction time, useful for HTTP
1068 FastSender(const QByteArray& data, bool https, bool fillBuffer, tst_QNetworkReply *listener = 0)
1069 : wantedSize(data.size()), port(-1), protocol(ProvidedData),
1070 doSsl(https), fillKernelBuffer(fillBuffer), transferRate(-1),
1071 dataToTransmit(data), dataIndex(0)
1072 {
1073 if (listener)
1074 connect(sender: this, SIGNAL(notEnoughData()), receiver: listener, SLOT(notEnoughData()));
1075 start();
1076 ready.acquire();
1077 }
1078
1079 inline int serverPort() const { return port; }
1080
1081 int writeNextData(QTcpSocket* socket, qint32 size)
1082 {
1083 if (protocol == DebugPipe) {
1084 QByteArray data;
1085 QDataStream stream(&data, QIODevice::WriteOnly);
1086 stream << QVariantMap() << QByteArray(size, 'a');
1087 socket->write(data: (char*)&size, len: sizeof size);
1088 socket->write(data);
1089 dataIndex += size;
1090 return size;
1091 } else {
1092 const QByteArray data = dataToTransmit.mid(index: dataIndex, len: size);
1093 socket->write(data);
1094 dataIndex += data.size();
1095 //qDebug() << "wrote" << dataIndex << "/" << dataToTransmit.size();
1096 return data.size();
1097 }
1098 }
1099 void writeLastData(QTcpSocket* socket)
1100 {
1101 if (protocol == DebugPipe) {
1102 QByteArray data;
1103 QDataStream stream(&data, QIODevice::WriteOnly);
1104 stream << QVariantMap() << QByteArray();
1105 const qint32 size = data.size();
1106 socket->write(data: (char*)&size, len: sizeof size);
1107 socket->write(data);
1108 }
1109 }
1110
1111protected:
1112 void run()
1113 {
1114 BlockingTcpServer server(doSsl);
1115 server.listen();
1116 port = server.serverPort();
1117 ready.release();
1118
1119 QTcpSocket *client = server.waitForNextConnectionSocket();
1120
1121 // get the "request" packet
1122 if (!client->waitForReadyRead(msecs: 2000)) {
1123 qDebug() << "FastSender:" << client->error() << "waiting for \"request\" packet";
1124 return;
1125 }
1126 client->readAll(); // we're not interested in the actual contents (e.g. HTTP request)
1127
1128 enum { BlockSize = 1024 };
1129
1130 if (fillKernelBuffer) {
1131
1132 // write a bunch of bytes to fill up the buffers
1133 bool done = false;
1134 do {
1135 if (writeNextData(socket: client, size: BlockSize) < BlockSize) {
1136 qDebug() << "ERROR: FastSender: not enough data to write in order to fill buffers; or client is reading too fast";
1137 emit notEnoughData();
1138 return;
1139 }
1140 while (client->bytesToWrite() > 0) {
1141 if (!client->waitForBytesWritten(msecs: 0)) {
1142 done = true;
1143 break;
1144 }
1145 }
1146 //qDebug() << "Filling kernel buffer: wrote" << dataIndex << "bytes";
1147 } while (!done);
1148
1149 qDebug() << "FastSender: ok, kernel buffer is full after writing" << dataIndex << "bytes";
1150 }
1151
1152 // Tell the client to start reading
1153 emit dataReady();
1154
1155 // the kernel buffer is full
1156 // clean up QAbstractSocket's residue:
1157 while (client->bytesToWrite() > 0) {
1158 qDebug() << "Still having" << client->bytesToWrite() << "bytes to write, doing that now";
1159 if (!client->waitForBytesWritten(msecs: 10000)) {
1160 qDebug() << "ERROR: FastSender:" << client->error() << "cleaning up residue";
1161 return;
1162 }
1163 }
1164
1165 // now write in "blocking mode", this is where the rate measuring starts
1166 QElapsedTimer timer;
1167 timer.start();
1168 //const qint64 writtenBefore = dataIndex;
1169 //qint64 measuredTotalBytes = wantedSize - writtenBefore;
1170 qint64 measuredSentBytes = 0;
1171 while (dataIndex < wantedSize) {
1172 const int remainingBytes = wantedSize - measuredSentBytes;
1173 const int bytesToWrite = qMin(a: remainingBytes, b: static_cast<int>(BlockSize));
1174 if (bytesToWrite <= 0)
1175 qFatal(msg: "%s: attempt to write %d bytes", Q_FUNC_INFO, bytesToWrite);
1176 measuredSentBytes += writeNextData(socket: client, size: bytesToWrite);
1177
1178 while (client->bytesToWrite() > 0) {
1179 if (!client->waitForBytesWritten(msecs: 10000)) {
1180 qDebug() << "ERROR: FastSender:" << client->error() << "during blocking write";
1181 return;
1182 }
1183 }
1184 /*qDebug() << "FastSender:" << bytesToWrite << "bytes written now;"
1185 << measuredSentBytes << "measured bytes" << measuredSentBytes + writtenBefore << "total ("
1186 << measuredSentBytes*100/measuredTotalBytes << "% complete);"
1187 << timer.elapsed() << "ms elapsed";*/
1188 }
1189
1190 transferRate = measuredSentBytes * 1000 / timer.elapsed();
1191 qDebug() << "FastSender: flushed" << measuredSentBytes << "bytes in" << timer.elapsed() << "ms: rate =" << transferRate << "B/s";
1192
1193 // write a "close connection" packet, if the protocol needs it
1194 writeLastData(socket: client);
1195 }
1196signals:
1197 void dataReady();
1198 void notEnoughData();
1199};
1200
1201class RateControlledReader: public QObject
1202{
1203 Q_OBJECT
1204 QIODevice *device;
1205 int bytesToRead;
1206 int interval;
1207 int readBufferSize;
1208public:
1209 QByteArray data;
1210 qint64 totalBytesRead;
1211 RateControlledReader(QObject& senderObj, QIODevice *dev, int kbPerSec, int maxBufferSize = 0)
1212 : device(dev), readBufferSize(maxBufferSize), totalBytesRead(0)
1213 {
1214 // determine how often we have to wake up
1215 int timesPerSecond;
1216 if (readBufferSize == 0) {
1217 // The requirement is simply "N KB per seconds"
1218 timesPerSecond = 20;
1219 bytesToRead = kbPerSec * 1024 / timesPerSecond;
1220 } else {
1221 // The requirement also includes "<readBufferSize> bytes at a time"
1222 bytesToRead = readBufferSize;
1223 timesPerSecond = kbPerSec * 1024 / readBufferSize;
1224 }
1225 interval = 1000 / timesPerSecond; // in ms
1226
1227 qDebug() << "RateControlledReader: going to read" << bytesToRead
1228 << "bytes every" << interval << "ms";
1229 qDebug() << "actual read rate will be"
1230 << (bytesToRead * 1000 / interval) << "bytes/sec (wanted"
1231 << kbPerSec * 1024 << "bytes/sec)";
1232
1233 // Wait for data to be readyRead
1234 bool ok = connect(sender: &senderObj, SIGNAL(dataReady()), receiver: this, SLOT(slotDataReady()));
1235 if (!ok)
1236 qFatal(msg: "%s: Cannot connect dataReady signal", Q_FUNC_INFO);
1237 }
1238
1239 void wrapUp()
1240 {
1241 QByteArray someData = device->read(maxlen: device->bytesAvailable());
1242 data += someData;
1243 totalBytesRead += someData.size();
1244 qDebug() << "wrapUp: found" << someData.size() << "bytes left. progress" << data.size();
1245 //qDebug() << "wrapUp: now bytesAvailable=" << device->bytesAvailable();
1246 }
1247
1248private slots:
1249 void slotDataReady()
1250 {
1251 //qDebug() << "RateControlledReader: ready to go";
1252 startTimer(interval);
1253 }
1254
1255protected:
1256 void timerEvent(QTimerEvent *)
1257 {
1258 //qDebug() << "RateControlledReader: timerEvent bytesAvailable=" << device->bytesAvailable();
1259 if (readBufferSize > 0 && device->bytesAvailable() > readBufferSize) {
1260 // This passes all the time, except in the final flush.
1261 //qFatal("%s: Too many bytes available", Q_FUNC_INFO);
1262 }
1263
1264 qint64 bytesRead = 0;
1265 QElapsedTimer stopWatch;
1266 stopWatch.start();
1267 do {
1268 if (device->bytesAvailable() == 0) {
1269 if (stopWatch.elapsed() > 20) {
1270 qDebug() << "RateControlledReader: Not enough data available for reading, waited too much, timing out";
1271 break;
1272 }
1273 if (!device->waitForReadyRead(msecs: 5)) {
1274 qDebug() << "RateControlledReader: Not enough data available for reading, even after waiting 5ms, bailing out";
1275 break;
1276 }
1277 }
1278 QByteArray someData = device->read(maxlen: bytesToRead - bytesRead);
1279 data += someData;
1280 bytesRead += someData.size();
1281 //qDebug() << "RateControlledReader: successfully read" << someData.size() << "progress:" << data.size();
1282 } while (bytesRead < bytesToRead);
1283 totalBytesRead += bytesRead;
1284
1285 if (bytesRead < bytesToRead)
1286 qWarning() << "RateControlledReader: WARNING:" << bytesToRead - bytesRead << "bytes not read";
1287 }
1288};
1289
1290
1291tst_QNetworkReply::tst_QNetworkReply()
1292{
1293 qRegisterMetaType<QNetworkReply *>(); // for QSignalSpy
1294 qRegisterMetaType<QAuthenticator *>();
1295#ifndef QT_NO_NETWORKPROXY
1296 qRegisterMetaType<QNetworkProxy>();
1297#endif
1298#ifndef QT_NO_SSL
1299 qRegisterMetaType<QList<QSslError> >();
1300#endif
1301 qRegisterMetaType<QNetworkReply::NetworkError>();
1302
1303 uniqueExtension = createUniqueExtension();
1304 testFileName = QDir::currentPath() + "/testfile" + uniqueExtension;
1305 cookieJar = new MyCookieJar;
1306 manager.setCookieJar(cookieJar);
1307
1308#ifndef QT_NO_NETWORKPROXY
1309 QHostInfo hostInfo = QHostInfo::fromName(name: QtNetworkSettings::httpProxyServerName());
1310
1311 proxies << ProxyData(QNetworkProxy::NoProxy, "", false);
1312
1313 if (hostInfo.error() == QHostInfo::NoError && !hostInfo.addresses().isEmpty()) {
1314 QString httpProxy = QtNetworkSettings::httpProxyServerName();
1315 QString socksProxy = QtNetworkSettings::socksProxyServerName();
1316 proxies << ProxyData(QNetworkProxy(QNetworkProxy::HttpProxy, httpProxy, 3128),
1317 "+proxy", false)
1318 << ProxyData(QNetworkProxy(QNetworkProxy::HttpProxy, httpProxy, 3129),
1319 "+proxyauth", true)
1320 // currently unsupported
1321 // << ProxyData(QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3130), "+proxyauth-ntlm", true);
1322 << ProxyData(QNetworkProxy(QNetworkProxy::Socks5Proxy, socksProxy, 1080),
1323 "+socks", false)
1324 << ProxyData(QNetworkProxy(QNetworkProxy::Socks5Proxy, socksProxy, 1081),
1325 "+socksauth", true);
1326 } else {
1327#endif // !QT_NO_NETWORKPROXY
1328 fprintf(stderr, format: "==================================================================\n");
1329 fprintf(stderr, format: "Proxy could not be looked up. No proxy will be used while testing!\n");
1330 fprintf(stderr, format: "==================================================================\n");
1331#ifndef QT_NO_NETWORKPROXY
1332 }
1333#endif // !QT_NO_NETWORKPROXY
1334}
1335
1336tst_QNetworkReply::~tst_QNetworkReply()
1337{
1338}
1339
1340
1341void tst_QNetworkReply::authenticationRequired(QNetworkReply*, QAuthenticator* auth)
1342{
1343 auth->setUser("httptest");
1344 auth->setPassword("httptest");
1345}
1346
1347void tst_QNetworkReply::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator* auth)
1348{
1349 auth->setUser("qsockstest");
1350 auth->setPassword("password");
1351}
1352
1353#ifndef QT_NO_SSL
1354void tst_QNetworkReply::sslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
1355{
1356 reply->ignoreSslErrors();
1357 QVERIFY(!errors.isEmpty());
1358 QVERIFY(!reply->sslConfiguration().isNull());
1359}
1360
1361void tst_QNetworkReply::storeSslConfiguration()
1362{
1363 storedSslConfiguration = QSslConfiguration();
1364 QNetworkReply *reply = qobject_cast<QNetworkReply *>(object: sender());
1365 if (reply)
1366 storedSslConfiguration = reply->sslConfiguration();
1367}
1368#endif
1369
1370QString tst_QNetworkReply::runMultipartRequest(const QNetworkRequest &request,
1371 QNetworkReplyPtr &reply,
1372 QHttpMultiPart *multiPart,
1373 const QByteArray &verb)
1374{
1375 if (verb == "POST")
1376 reply.reset(t: manager.post(request, multiPart));
1377 else
1378 reply.reset(t: manager.put(request, multiPart));
1379
1380 // the code below is copied from tst_QNetworkReply::runSimpleRequest, see below
1381 reply->setParent(this);
1382 connect(ptr: reply, SIGNAL(finished()), SLOT(finished()));
1383 connect(ptr: reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), SLOT(gotError()));
1384 multiPart->setParent(reply.data());
1385
1386 returnCode = Timeout;
1387 loop = new QEventLoop;
1388 QTimer::singleShot(msec: 25000, receiver: loop, SLOT(quit()));
1389 int code = returnCode == Timeout ? loop->exec() : returnCode;
1390 delete loop;
1391 loop = 0;
1392
1393 switch (code) {
1394 case Failure:
1395 return "Request failed: " + reply->errorString();
1396 case Timeout:
1397 return "Network timeout";
1398 }
1399 return QString();
1400}
1401
1402QString tst_QNetworkReply::runSimpleRequest(QNetworkAccessManager::Operation op,
1403 const QNetworkRequest &request,
1404 QNetworkReplyPtr &reply,
1405 const QByteArray &data)
1406{
1407 switch (op) {
1408 case QNetworkAccessManager::HeadOperation:
1409 reply.reset(t: manager.head(request));
1410 break;
1411
1412 case QNetworkAccessManager::GetOperation:
1413 reply.reset(t: manager.get(request));
1414 break;
1415
1416 case QNetworkAccessManager::PutOperation:
1417 reply.reset(t: manager.put(request, data));
1418 break;
1419
1420 case QNetworkAccessManager::PostOperation:
1421 reply.reset(t: manager.post(request, data));
1422 break;
1423
1424 case QNetworkAccessManager::DeleteOperation:
1425 reply.reset(t: manager.deleteResource(request));
1426 break;
1427
1428 default:
1429 qFatal(msg: "%s: Invalid/unknown operation requested", Q_FUNC_INFO);
1430 }
1431 reply->setParent(this);
1432
1433 returnCode = Timeout;
1434 int code = Success;
1435
1436 if (request.attribute(code: QNetworkRequest::SynchronousRequestAttribute).toBool()) {
1437 if (reply->isFinished())
1438 code = reply->error() != QNetworkReply::NoError ? Failure : Success;
1439 else
1440 code = Failure;
1441 } else {
1442 connect(ptr: reply, SIGNAL(finished()), SLOT(finished()));
1443 connect(ptr: reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), SLOT(gotError()));
1444
1445 int count = 0;
1446 loop = new QEventLoop;
1447 QSignalSpy spy(reply.data(), SIGNAL(downloadProgress(qint64,qint64)));
1448 while (!reply->isFinished()) {
1449 QTimer::singleShot(msec: 20000, receiver: loop, SLOT(quit()));
1450 code = loop->exec();
1451 if (count == spy.count() && !reply->isFinished()) {
1452 code = Timeout;
1453 break;
1454 }
1455 count = spy.count();
1456 }
1457 delete loop;
1458 loop = 0;
1459 }
1460
1461 switch (code) {
1462 case Failure:
1463 return "Request failed: " + reply->errorString();
1464 case Timeout:
1465 return "Network timeout";
1466 }
1467 return QString();
1468}
1469
1470QString tst_QNetworkReply::runCustomRequest(const QNetworkRequest &request,
1471 QNetworkReplyPtr &reply,
1472 const QByteArray &verb,
1473 QIODevice *data)
1474{
1475 reply.reset(t: manager.sendCustomRequest(request, verb, data));
1476 reply->setParent(this);
1477 connect(ptr: reply, SIGNAL(finished()), SLOT(finished()));
1478 connect(ptr: reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), SLOT(gotError()));
1479
1480 returnCode = Timeout;
1481 loop = new QEventLoop;
1482 QTimer::singleShot(msec: 20000, receiver: loop, SLOT(quit()));
1483 int code = returnCode == Timeout ? loop->exec() : returnCode;
1484 delete loop;
1485 loop = 0;
1486
1487 switch (code) {
1488 case Failure:
1489 return "Request failed: " + reply->errorString();
1490 case Timeout:
1491 return "Network timeout";
1492 }
1493 return QString();
1494}
1495
1496static QByteArray msgWaitForFinished(QNetworkReplyPtr &reply)
1497{
1498 QString result;
1499 QDebug debug(&result);
1500 debug << reply->url();
1501 if (!reply->isFinished())
1502 debug << "timed out.";
1503 else if (reply->error() == QNetworkReply::NoError)
1504 debug << "finished.";
1505 else
1506 debug << "failed: #" << reply->error() << reply->errorString();
1507 return result.toLocal8Bit();
1508}
1509
1510int tst_QNetworkReply::waitForFinish(QNetworkReplyPtr &reply)
1511{
1512 int count = 0;
1513
1514 connect(ptr: reply, SIGNAL(finished()), SLOT(finished()));
1515 connect(ptr: reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), SLOT(gotError()));
1516 returnCode = Success;
1517 loop = new QEventLoop;
1518 QSignalSpy spy(reply.data(), SIGNAL(downloadProgress(qint64,qint64)));
1519 while (!reply->isFinished()) {
1520 QTimer::singleShot(msec: 5000, receiver: loop, SLOT(quit()));
1521 if (loop->exec() == Timeout && count == spy.count() && !reply->isFinished()) {
1522 returnCode = Timeout;
1523 break;
1524 }
1525 count = spy.count();
1526 }
1527 delete loop;
1528 loop = 0;
1529
1530 return returnCode;
1531}
1532
1533void tst_QNetworkReply::finished()
1534{
1535 if (loop)
1536 loop->exit(returnCode: returnCode = Success);
1537}
1538
1539void tst_QNetworkReply::gotError()
1540{
1541 if (loop)
1542 loop->exit(returnCode: returnCode = Failure);
1543 disconnect(sender: QObject::sender(), SIGNAL(finished()), receiver: this, member: 0);
1544}
1545
1546void tst_QNetworkReply::initTestCase()
1547{
1548 testDataDir = QFileInfo(QFINDTESTDATA("rfc3252.txt")).absolutePath();
1549 if (testDataDir.isEmpty())
1550 testDataDir = QCoreApplication::applicationDirPath();
1551
1552#if defined(QT_TEST_SERVER)
1553 QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::ftpServerName(), 21));
1554 QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::ftpProxyServerName(), 2121));
1555 QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpServerName(), 80));
1556 QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpServerName(), 443));
1557 QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpProxyServerName(), 3128));
1558 QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpProxyServerName(), 3129));
1559 QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpProxyServerName(), 3130));
1560 QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::socksProxyServerName(), 1080));
1561 QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::socksProxyServerName(), 1081));
1562#else
1563 if (!QtNetworkSettings::verifyTestNetworkSettings())
1564 QSKIP("No network test server available");
1565#endif
1566#if !defined Q_OS_WIN
1567 wronlyFileName = testDataDir + "/write-only" + uniqueExtension;
1568 QFile wr(wronlyFileName);
1569 QVERIFY(wr.open(QIODevice::WriteOnly | QIODevice::Truncate));
1570 wr.setPermissions(QFile::WriteOwner | QFile::WriteUser);
1571 wr.close();
1572#endif
1573
1574 QDir::setSearchPaths(prefix: "testdata", searchPaths: QStringList() << testDataDir);
1575#ifndef QT_NO_SSL
1576 QSslConfiguration::defaultConfiguration().caCertificates(); //preload certificates
1577#endif
1578#ifndef QT_NO_BEARERMANAGEMENT
1579 netConfMan = new QNetworkConfigurationManager(this);
1580 networkConfiguration = netConfMan->defaultConfiguration();
1581 networkSession.reset(other: new QNetworkSession(networkConfiguration));
1582 if (!networkSession->isOpen()) {
1583 networkSession->open();
1584 QVERIFY(networkSession->waitForOpened(30000));
1585 }
1586#endif
1587
1588 echoProcessDir = QFINDTESTDATA("echo");
1589 QVERIFY2(!echoProcessDir.isEmpty(), qPrintable(
1590 QString::fromLatin1("Couldn't find echo dir starting from %1.").arg(QDir::currentPath())));
1591
1592 cleanupTestData();
1593#ifndef QT_NO_OPENSSL
1594 QT_PREPEND_NAMESPACE(qt_ForceTlsSecurityLevel)();
1595#endif // QT_NO_OPENSSL
1596
1597}
1598
1599void tst_QNetworkReply::cleanupTestCase()
1600{
1601#if !defined Q_OS_WIN
1602 if (!wronlyFileName.isNull())
1603 QFile::remove(fileName: wronlyFileName);
1604#endif
1605#ifndef QT_NO_BEARERMANAGEMENT
1606 if (networkSession && networkSession->isOpen()) {
1607 networkSession->close();
1608 }
1609#endif
1610}
1611
1612void tst_QNetworkReply::cleanupTestData()
1613{
1614 QFile file(testFileName);
1615 QVERIFY(!file.exists() || file.remove());
1616
1617 // clear the internal cache
1618 manager.clearAccessCache();
1619#ifndef QT_NO_NETWORKPROXY
1620 manager.setProxy(QNetworkProxy());
1621#endif
1622 manager.setCache(0);
1623
1624 // clear cookies
1625 cookieJar->setAllCookies(QList<QNetworkCookie>());
1626
1627 // disconnect manager signals
1628#ifndef QT_NO_SSL
1629 manager.disconnect(SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
1630#endif
1631 manager.disconnect(SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
1632 manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
1633}
1634
1635void tst_QNetworkReply::stateChecking()
1636{
1637 QUrl url = QUrl("file:///");
1638 QNetworkRequest req(url); // you can't open this file, I know
1639 QNetworkReplyPtr reply(manager.get(request: req));
1640
1641 QVERIFY(reply.data());
1642 QVERIFY(reply->isOpen());
1643 QVERIFY(reply->isReadable());
1644 QVERIFY(!reply->isWritable());
1645
1646 // both behaviours are OK since we might change underlying behaviour again
1647 if (!reply->isFinished())
1648 QCOMPARE(reply->errorString(), QString("Unknown error"));
1649 else
1650 QVERIFY(!reply->errorString().isEmpty());
1651
1652
1653 QCOMPARE(reply->manager(), &manager);
1654 QCOMPARE(reply->request(), req);
1655 QCOMPARE(int(reply->operation()), int(QNetworkAccessManager::GetOperation));
1656 // error and not error are OK since we might change underlying behaviour again
1657 if (!reply->isFinished())
1658 QCOMPARE(reply->error(), QNetworkReply::NoError);
1659 QCOMPARE(reply->url(), url);
1660
1661 reply->abort();
1662}
1663
1664void tst_QNetworkReply::invalidProtocol()
1665{
1666 QUrl url = QUrl::fromEncoded(url: "not-a-known-protocol://foo/bar");
1667 QNetworkRequest req(url);
1668 QNetworkReplyPtr reply;
1669
1670 QString errorMsg = "Request failed: Protocol \"not-a-known-protocol\" is unknown";
1671 QString result = runSimpleRequest(op: QNetworkAccessManager::GetOperation, request: req, reply);
1672 QCOMPARE(result, errorMsg);
1673
1674 QCOMPARE(reply->url(), url);
1675 QCOMPARE(reply->error(), QNetworkReply::ProtocolUnknownError);
1676}
1677
1678void tst_QNetworkReply::getFromData_data()
1679{
1680 QTest::addColumn<QString>(name: "request");
1681 QTest::addColumn<QByteArray>(name: "expected");
1682 QTest::addColumn<QString>(name: "mimeType");
1683
1684 const QString defaultMimeType("text/plain;charset=US-ASCII");
1685
1686 //QTest::newRow("empty") << "data:" << QByteArray() << defaultMimeType;
1687 QTest::newRow(dataTag: "empty2") << "data:," << QByteArray() << defaultMimeType;
1688 QTest::newRow(dataTag: "just-charset_1") << "data:charset=iso-8859-1,"
1689 << QByteArray() << "text/plain;charset=iso-8859-1";
1690 QTest::newRow(dataTag: "just-charset_2") << "data:charset = iso-8859-1 ,"
1691 << QByteArray() << "text/plain;charset = iso-8859-1";
1692 //QTest::newRow("just-media") << "data:text/xml" << QByteArray() << "text/xml";
1693 QTest::newRow(dataTag: "just-media2") << "data:text/xml," << QByteArray() << "text/xml";
1694
1695 QTest::newRow(dataTag: "plain_1") << "data:,foo" << QByteArray("foo") << defaultMimeType;
1696 QTest::newRow(dataTag: "plain_2") << "data:text/html,Hello World" << QByteArray("Hello World")
1697 << "text/html";
1698 QTest::newRow(dataTag: "plain_3") << "data:text/html;charset=utf-8,Hello World"
1699 << QByteArray("Hello World") << "text/html;charset=utf-8";
1700
1701 QTest::newRow(dataTag: "pct_1") << "data:,%3Cbody%20contentEditable%3Dtrue%3E%0D%0A"
1702 << QByteArray("<body contentEditable=true>\r\n") << defaultMimeType;
1703 QTest::newRow(dataTag: "pct_2") << "data:text/html;charset=utf-8,%3Cbody%20contentEditable%3Dtrue%3E%0D%0A"
1704 << QByteArray("<body contentEditable=true>\r\n")
1705 << "text/html;charset=utf-8";
1706
1707 QTest::newRow(dataTag: "base64-empty_1") << "data:;base64," << QByteArray() << defaultMimeType;
1708 QTest::newRow(dataTag: "base64-empty_2") << "data:charset=utf-8;base64," << QByteArray()
1709 << "text/plain;charset=utf-8";
1710 QTest::newRow(dataTag: "base64-empty_3") << "data:text/html;charset=utf-8;base64,"
1711 << QByteArray() << "text/html;charset=utf-8";
1712
1713 QTest::newRow(dataTag: "base64_1") << "data:;base64,UXQgaXMgZ3JlYXQh" << QByteArray("Qt is great!")
1714 << defaultMimeType;
1715 QTest::newRow(dataTag: "base64_2") << "data:charset=utf-8;base64,UXQgaXMgZ3JlYXQh"
1716 << QByteArray("Qt is great!") << "text/plain;charset=utf-8";
1717 QTest::newRow(dataTag: "base64_3") << "data:text/html;charset=utf-8;base64,UXQgaXMgZ3JlYXQh"
1718 << QByteArray("Qt is great!") << "text/html;charset=utf-8";
1719
1720 QTest::newRow(dataTag: "pct-nul") << "data:,a%00g" << QByteArray("a\0g", 3) << defaultMimeType;
1721 QTest::newRow(dataTag: "base64-nul") << "data:;base64,YQBn" << QByteArray("a\0g", 3) << defaultMimeType;
1722 QTest::newRow(dataTag: "pct-nonutf8") << "data:,a%E1g" << QByteArray("a\xE1g", 3) << defaultMimeType;
1723
1724 QTest::newRow(dataTag: "base64")
1725 << QString::fromLatin1(str: "data:application/xml;base64,PGUvPg==")
1726 << QByteArray("<e/>")
1727 << "application/xml";
1728
1729 QTest::newRow(dataTag: "base64, no media type")
1730 << QString::fromLatin1(str: "data:;base64,PGUvPg==")
1731 << QByteArray("<e/>")
1732 << defaultMimeType;
1733
1734 QTest::newRow(dataTag: "Percent encoding")
1735 << QString::fromLatin1(str: "data:application/xml,%3Ce%2F%3E")
1736 << QByteArray("<e/>")
1737 << "application/xml";
1738
1739 QTest::newRow(dataTag: "Percent encoding, no media type")
1740 << QString::fromLatin1(str: "data:,%3Ce%2F%3E")
1741 << QByteArray("<e/>")
1742 << defaultMimeType;
1743
1744 QTest::newRow(dataTag: "querychars")
1745 << QString::fromLatin1(str: "data:,foo?x=0&y=0")
1746 << QByteArray("foo?x=0&y=0")
1747 << defaultMimeType;
1748
1749 QTest::newRow(dataTag: "css") << "data:text/css,div%20{%20border-right:%20solid;%20}"
1750 << QByteArray("div { border-right: solid; }")
1751 << "text/css";
1752}
1753
1754void tst_QNetworkReply::getFromData()
1755{
1756 QFETCH(QString, request);
1757 QFETCH(QByteArray, expected);
1758 QFETCH(QString, mimeType);
1759
1760 QUrl url = QUrl::fromEncoded(url: request.toLatin1());
1761 QNetworkRequest req(url);
1762 QNetworkReplyPtr reply;
1763
1764 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, req, reply));
1765
1766 QCOMPARE(reply->url(), url);
1767 QCOMPARE(reply->error(), QNetworkReply::NoError);
1768
1769 QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader).toString(), mimeType);
1770 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(expected.size()));
1771 QCOMPARE(reply->readAll(), expected);
1772}
1773
1774void tst_QNetworkReply::getFromFile_data()
1775{
1776 QTest::addColumn<bool>(name: "backgroundAttribute");
1777
1778 QTest::newRow(dataTag: "no-background-attribute") << false;
1779 QTest::newRow(dataTag: "background-attribute") << true;
1780}
1781
1782void tst_QNetworkReply::getFromFile()
1783{
1784 QFETCH(bool, backgroundAttribute);
1785
1786 // create the file:
1787 QTemporaryFile file(QDir::currentPath() + "/temp-XXXXXX");
1788 file.setAutoRemove(true);
1789 QVERIFY2(file.open(), qPrintable(file.errorString()));
1790
1791 QNetworkRequest request(QUrl::fromLocalFile(localfile: file.fileName()));
1792 if (backgroundAttribute)
1793 request.setAttribute(code: QNetworkRequest::BackgroundRequestAttribute, value: QVariant::fromValue(value: true));
1794 QNetworkReplyPtr reply;
1795
1796 static const char fileData[] = "This is some data that is in the file.\r\n";
1797 QByteArray data = QByteArray::fromRawData(fileData, size: sizeof fileData - 1);
1798 QCOMPARE(file.write(data), data.size());
1799 file.flush();
1800 QCOMPARE(file.size(), qint64(data.size()));
1801
1802 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
1803 QVERIFY(waitForFinish(reply) != Timeout);
1804
1805 QCOMPARE(reply->url(), request.url());
1806 QCOMPARE(reply->error(), QNetworkReply::NoError);
1807
1808 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), file.size());
1809 QCOMPARE(reply->readAll(), data);
1810
1811 // make the file bigger
1812 file.resize(sz: 0);
1813 const int multiply = (128 * 1024) / (sizeof fileData - 1);
1814 for (int i = 0; i < multiply; ++i)
1815 file.write(data: fileData, len: sizeof fileData - 1);
1816 file.flush();
1817
1818 // run again
1819 reply.clear();
1820
1821 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
1822 QCOMPARE(reply->url(), request.url());
1823 QCOMPARE(reply->error(), QNetworkReply::NoError);
1824
1825 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), file.size());
1826 QCOMPARE(qint64(reply->readAll().size()), file.size());
1827}
1828
1829void tst_QNetworkReply::getFromFileSpecial_data()
1830{
1831 QTest::addColumn<QString>(name: "fileName");
1832 QTest::addColumn<QString>(name: "url");
1833
1834 QTest::newRow(dataTag: "resource") << ":/resource" << "qrc:/resource";
1835 QTest::newRow(dataTag: "search-path") << "testdata:/rfc3252.txt" << "testdata:/rfc3252.txt";
1836 QTest::newRow(dataTag: "bigfile-path") << "testdata:/bigfile" << "testdata:/bigfile";
1837#ifdef Q_OS_WIN
1838 QTest::newRow("smb-path") << "testdata:/smb-file.txt" << "file://" + QtNetworkSettings::winServerName() + "/testshare/test.pri";
1839#endif
1840}
1841
1842void tst_QNetworkReply::getFromFileSpecial()
1843{
1844#if defined(QT_TEST_SERVER) && defined(Q_OS_WIN)
1845 if (qstrcmp(QTest::currentDataTag(), "smb-path") == 0)
1846 QSKIP("Docker-based test server doesn't support smb protocol yet");
1847#endif
1848
1849 QFETCH(QString, fileName);
1850 QFETCH(QString, url);
1851
1852 // open the resource so we can find out its size
1853 QFile resource(fileName);
1854 QVERIFY(resource.open(QIODevice::ReadOnly));
1855
1856 QNetworkRequest request;
1857 QNetworkReplyPtr reply;
1858 request.setUrl(url);
1859 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
1860
1861 QCOMPARE(reply->url(), request.url());
1862 QCOMPARE(reply->error(), QNetworkReply::NoError);
1863
1864 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), resource.size());
1865 QCOMPARE(reply->readAll(), resource.readAll());
1866}
1867
1868void tst_QNetworkReply::getFromFtp_data()
1869{
1870 QTest::addColumn<QString>(name: "referenceName");
1871 QTest::addColumn<QString>(name: "url");
1872
1873 QTest::newRow(dataTag: "rfc3252.txt")
1874 << testDataDir + "/rfc3252.txt"
1875 << "ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/rfc3252.txt";
1876
1877 QTest::newRow(dataTag: "bigfile")
1878 << testDataDir + "/bigfile"
1879 << "ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/bigfile";
1880}
1881
1882void tst_QNetworkReply::getFromFtp()
1883{
1884 QFETCH(QString, referenceName);
1885 QFETCH(QString, url);
1886
1887 QFile reference(referenceName);
1888 QVERIFY(reference.open(QIODevice::ReadOnly));
1889
1890 QNetworkRequest request(url);
1891 QNetworkReplyPtr reply;
1892 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
1893
1894 QCOMPARE(reply->url(), request.url());
1895 QCOMPARE(reply->error(), QNetworkReply::NoError);
1896
1897 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
1898 QCOMPARE(reply->readAll(), reference.readAll());
1899}
1900
1901void tst_QNetworkReply::getFromFtpAfterError()
1902{
1903 QNetworkRequest invalidRequest(QUrl("ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/invalid.txt"));
1904 QNetworkReplyPtr invalidReply;
1905 invalidReply.reset(t: manager.get(request: invalidRequest));
1906 QSignalSpy spy(invalidReply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
1907 QVERIFY(spy.wait());
1908 QCOMPARE(invalidReply->error(), QNetworkReply::ContentNotFoundError);
1909
1910 QFile reference(testDataDir + "/rfc3252.txt");
1911 QVERIFY(reference.open(QIODevice::ReadOnly));
1912 QNetworkRequest validRequest(QUrl("ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/rfc3252.txt"));
1913 QNetworkReplyPtr validReply;
1914 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, validRequest, validReply));
1915 QCOMPARE(validReply->url(), validRequest.url());
1916 QCOMPARE(validReply->error(), QNetworkReply::NoError);
1917 QCOMPARE(validReply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
1918 QCOMPARE(validReply->readAll(), reference.readAll());
1919}
1920
1921void tst_QNetworkReply::getFromHttp_data()
1922{
1923 QTest::addColumn<QString>(name: "referenceName");
1924 QTest::addColumn<QString>(name: "url");
1925
1926 QTest::newRow(dataTag: "success-internal")
1927 << testDataDir + "/rfc3252.txt"
1928 << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt";
1929
1930 QTest::newRow(dataTag: "success-external")
1931 << testDataDir + "/rfc3252.txt"
1932 << "http://www.ietf.org/rfc/rfc3252.txt";
1933
1934 QTest::newRow(dataTag: "bigfile-internal")
1935 << testDataDir + "/bigfile"
1936 << "http://" + QtNetworkSettings::httpServerName() + "/qtest/bigfile";
1937}
1938
1939void tst_QNetworkReply::getFromHttp()
1940{
1941 QFETCH(QString, referenceName);
1942 QFETCH(QString, url);
1943
1944 QFile reference(referenceName);
1945 QVERIFY(reference.open(QIODevice::ReadOnly));
1946
1947 QNetworkRequest request(url);
1948 QNetworkReplyPtr reply;
1949 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
1950
1951 QCOMPARE(reply->url(), request.url());
1952 QCOMPARE(reply->error(), QNetworkReply::NoError);
1953 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
1954 QCOMPARE(reply->size(), reference.size());
1955 // only compare when the header is set.
1956 if (reply->header(header: QNetworkRequest::ContentLengthHeader).isValid())
1957 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
1958
1959 // We know our internal server is apache..
1960 if (qstrcmp(str1: QTest::currentDataTag(), str2: "success-internal") == 0)
1961 QVERIFY(reply->header(QNetworkRequest::ServerHeader).toString().contains("Apache"));
1962
1963 QCOMPARE(reply->readAll(), reference.readAll());
1964}
1965
1966#ifndef QT_NO_NETWORKPROXY
1967void tst_QNetworkReply::headFromHttp_data()
1968{
1969 QTest::addColumn<qint64>(name: "referenceSize");
1970 QTest::addColumn<QUrl>(name: "url");
1971 QTest::addColumn<QString>(name: "contentType");
1972 QTest::addColumn<QNetworkProxy>(name: "proxy");
1973
1974 qint64 rfcsize = QFileInfo(testDataDir + "/rfc3252.txt").size();
1975 qint64 bigfilesize = QFileInfo(testDataDir + "/bigfile").size();
1976
1977#if defined(QT_TEST_SERVER)
1978 qint64 indexsize = QFileInfo(testDataDir + "/testserver_index.html").size();
1979#else
1980 qint64 indexsize = QFileInfo(testDataDir + "/index.html").size();
1981#endif
1982
1983 QString httpServer = QtNetworkSettings::httpServerName();
1984 //testing proxies, mainly for the 407 response from http proxy
1985 for (int i = 0; i < proxies.count(); ++i) {
1986 QTest::newRow(dataTag: "rfc" + proxies.at(i).tag)
1987 << rfcsize
1988 << QUrl("http://" + httpServer + "/qtest/rfc3252.txt")
1989 << "text/plain"
1990 << proxies.at(i).proxy;
1991
1992 QTest::newRow(dataTag: "bigfile" + proxies.at(i).tag)
1993 << bigfilesize
1994 << QUrl("http://" + httpServer + "/qtest/bigfile")
1995 << "text/plain"
1996 << proxies.at(i).proxy;
1997
1998 QTest::newRow(dataTag: "index" + proxies.at(i).tag)
1999 << indexsize
2000 << QUrl("http://" + httpServer + "/qtest/")
2001 << "text/html"
2002 << proxies.at(i).proxy;
2003
2004 QTest::newRow(dataTag: "with-authentication" + proxies.at(i).tag)
2005 << rfcsize
2006 << QUrl("http://" + httpServer + "/qtest/rfcs-auth/rfc3252.txt")
2007 << "text/plain"
2008 << proxies.at(i).proxy;
2009
2010 QTest::newRow(dataTag: "cgi" + proxies.at(i).tag)
2011 << (qint64)-1
2012 << QUrl("http://" + httpServer + "/qtest/cgi-bin/httpcachetest_expires500.cgi")
2013 << "text/html"
2014 << proxies.at(i).proxy;
2015 }
2016}
2017
2018void tst_QNetworkReply::headFromHttp()
2019{
2020 QFETCH(qint64, referenceSize);
2021 QFETCH(QUrl, url);
2022 QFETCH(QString, contentType);
2023 QFETCH(QNetworkProxy, proxy);
2024
2025 QNetworkRequest request(url);
2026 QNetworkReplyPtr reply;
2027
2028 QElapsedTimer time;
2029 time.start();
2030
2031 manager.setProxy(proxy);
2032 connect(asender: &manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
2033 SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
2034 connect(asender: &manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
2035 SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
2036
2037 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::HeadOperation, request, reply));
2038
2039 manager.disconnect(SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
2040 receiver: this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
2041 manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
2042 receiver: this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
2043
2044 QVERIFY(time.elapsed() < 8000); //check authentication didn't wait for the server to timeout the http connection (15s on qt test server)
2045
2046 QCOMPARE(reply->url(), request.url());
2047 QCOMPARE(reply->error(), QNetworkReply::NoError);
2048 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
2049 // only compare when the header is set.
2050 if (reply->header(header: QNetworkRequest::ContentLengthHeader).isValid() && referenceSize >= 0)
2051 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), referenceSize);
2052 if (reply->header(header: QNetworkRequest::ContentTypeHeader).isValid())
2053 QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader).toString(), contentType);
2054}
2055#endif // !QT_NO_NETWORKPROXY
2056
2057void tst_QNetworkReply::getErrors_data()
2058{
2059 QTest::addColumn<QString>(name: "url");
2060 QTest::addColumn<int>(name: "error");
2061 QTest::addColumn<int>(name: "httpStatusCode");
2062 QTest::addColumn<bool>(name: "dataIsEmpty");
2063
2064 // empties
2065 QTest::newRow(dataTag: "empty-url") << QString() << int(QNetworkReply::ProtocolUnknownError) << 0 << true;
2066 QTest::newRow(dataTag: "empty-scheme-host") << (testDataDir + "/rfc3252.txt") << int(QNetworkReply::ProtocolUnknownError) << 0 << true;
2067 QTest::newRow(dataTag: "empty-scheme") << "//" + QtNetworkSettings::winServerName() + "/testshare/test.pri"
2068 << int(QNetworkReply::ProtocolUnknownError) << 0 << true;
2069
2070 // file: errors
2071 QTest::newRow(dataTag: "file-host") << "file://invalid.test.qt-project.org/foo.txt"
2072#if !defined Q_OS_WIN
2073 << int(QNetworkReply::ProtocolInvalidOperationError) << 0 << true;
2074#else
2075 << int(QNetworkReply::ContentNotFoundError) << 0 << true;
2076#endif
2077 QTest::newRow(dataTag: "file-no-path") << "file://localhost"
2078 << int(QNetworkReply::ContentOperationNotPermittedError) << 0 << true;
2079 QTest::newRow(dataTag: "file-is-dir") << QUrl::fromLocalFile(localfile: QDir::currentPath()).toString()
2080 << int(QNetworkReply::ContentOperationNotPermittedError) << 0 << true;
2081 QTest::newRow(dataTag: "file-exist") << QUrl::fromLocalFile(localfile: QDir::currentPath() + "/this-file-doesnt-exist.txt").toString()
2082 << int(QNetworkReply::ContentNotFoundError) << 0 << true;
2083#if !defined Q_OS_WIN
2084 QTest::newRow(dataTag: "file-is-wronly") << QUrl::fromLocalFile(localfile: wronlyFileName).toString()
2085 << int(QNetworkReply::ContentAccessDenied) << 0 << true;
2086#endif
2087
2088
2089 if (QFile::exists(fileName: filePermissionFileName))
2090 QTest::newRow(dataTag: "file-permissions") << "file:" + filePermissionFileName
2091 << int(QNetworkReply::ContentAccessDenied) << 0 << true;
2092
2093 // ftp: errors
2094 QTest::newRow(dataTag: "ftp-host") << "ftp://invalid.test.qt-project.org/foo.txt"
2095 << int(QNetworkReply::HostNotFoundError) << 0 << true;
2096 QTest::newRow(dataTag: "ftp-no-path") << "ftp://" + QtNetworkSettings::ftpServerName()
2097 << int(QNetworkReply::ContentOperationNotPermittedError) << 0 << true;
2098 QTest::newRow(dataTag: "ftp-is-dir") << "ftp://" + QtNetworkSettings::ftpServerName() + "/qtest"
2099 << int(QNetworkReply::ContentOperationNotPermittedError) << 0 << true;
2100 QTest::newRow(dataTag: "ftp-dir-not-readable") << "ftp://" + QtNetworkSettings::ftpServerName() + "/pub/dir-not-readable/foo.txt"
2101 << int(QNetworkReply::ContentAccessDenied) << 0 << true;
2102 QTest::newRow(dataTag: "ftp-file-not-readable") << "ftp://" + QtNetworkSettings::ftpServerName() + "/pub/file-not-readable.txt"
2103 << int(QNetworkReply::ContentAccessDenied) << 0 << true;
2104 QTest::newRow(dataTag: "ftp-exist") << "ftp://" + QtNetworkSettings::ftpServerName() + "/pub/this-file-doesnt-exist.txt"
2105 << int(QNetworkReply::ContentNotFoundError) << 0 << true;
2106
2107 // http: errors
2108 QTest::newRow(dataTag: "http-host") << "http://invalid.test.qt-project.org/"
2109 << int(QNetworkReply::HostNotFoundError) << 0 << true;
2110 QTest::newRow(dataTag: "http-exist") << "http://" + QtNetworkSettings::httpServerName() + "/this-file-doesnt-exist.txt"
2111 << int(QNetworkReply::ContentNotFoundError) << 404 << false;
2112 QTest::newRow(dataTag: "http-authentication") << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfcs-auth"
2113 << int(QNetworkReply::AuthenticationRequiredError) << 401 << false;
2114}
2115
2116static QByteArray msgGetErrors(int waitResult, const QNetworkReplyPtr &reply)
2117{
2118 QByteArray result ="waitResult=" + QByteArray::number(waitResult);
2119 if (reply->isFinished())
2120 result += ", finished";
2121 if (reply->error() != QNetworkReply::NoError)
2122 result += ", error: " + QByteArray::number(int(reply->error()));
2123 return result;
2124}
2125
2126void tst_QNetworkReply::getErrors()
2127{
2128 QFETCH(QString, url);
2129 QNetworkRequest request(url);
2130
2131#ifdef Q_OS_UNIX
2132 if ((qstrcmp(str1: QTest::currentDataTag(), str2: "file-is-wronly") == 0) ||
2133 (qstrcmp(str1: QTest::currentDataTag(), str2: "file-permissions") == 0)) {
2134 if (::getuid() == 0)
2135 QSKIP("Running this test as root doesn't make sense");
2136
2137 }
2138
2139 if (EmulationDetector::isRunningArmOnX86()
2140 && qstrcmp(str1: QTest::currentDataTag(), str2: "file-permissions") == 0) {
2141 QFileInfo filePermissionFile = QFileInfo(filePermissionFileName.toLatin1());
2142 if (filePermissionFile.ownerId() == ::geteuid()) {
2143 QSKIP("Sysroot directories are owned by the current user");
2144 }
2145 }
2146#endif
2147
2148 QNetworkReplyPtr reply(manager.get(request));
2149 reply->setParent(this); // we have expect-fails
2150
2151 if (!reply->isFinished())
2152 QCOMPARE(reply->error(), QNetworkReply::NoError);
2153
2154 // now run the request:
2155 const int waitResult = waitForFinish(reply);
2156 QVERIFY2(waitResult != Timeout, msgGetErrors(waitResult, reply));
2157
2158 QFETCH(int, error);
2159 QEXPECT_FAIL("ftp-is-dir", "QFtp cannot provide enough detail", Abort);
2160 // the line below is not necessary
2161 QEXPECT_FAIL("ftp-dir-not-readable", "QFtp cannot provide enough detail", Abort);
2162 QCOMPARE(reply->error(), QNetworkReply::NetworkError(error));
2163
2164 QTEST(reply->readAll().isEmpty(), "dataIsEmpty");
2165
2166 QVERIFY(reply->isFinished());
2167 QVERIFY(!reply->isRunning());
2168
2169 QFETCH(int, httpStatusCode);
2170 if (httpStatusCode != 0) {
2171 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), httpStatusCode);
2172 }
2173}
2174
2175static inline QByteArray md5sum(const QByteArray &data)
2176{
2177 return QCryptographicHash::hash(data, method: QCryptographicHash::Md5);
2178}
2179
2180void tst_QNetworkReply::putToFile_data()
2181{
2182 QTest::addColumn<QByteArray>(name: "data");
2183 QTest::addColumn<QByteArray>(name: "md5sum");
2184
2185 QByteArray data;
2186 data = "";
2187 QTest::newRow(dataTag: "empty") << data << md5sum(data);
2188
2189 data = "This is a normal message.";
2190 QTest::newRow(dataTag: "generic") << data << md5sum(data);
2191
2192 data = "This is a message to show that Qt rocks!\r\n\n";
2193 QTest::newRow(dataTag: "small") << data << md5sum(data);
2194
2195 data = QByteArray("abcd\0\1\2\abcd",12);
2196 QTest::newRow(dataTag: "with-nul") << data << md5sum(data);
2197
2198 data = QByteArray(4097, '\4');
2199 QTest::newRow(dataTag: "4k+1") << data << md5sum(data);
2200
2201 data = QByteArray(128*1024+1, '\177');
2202 QTest::newRow(dataTag: "128k+1") << data << md5sum(data);
2203
2204 data = QByteArray(2*1024*1024+1, '\177');
2205 QTest::newRow(dataTag: "2MB+1") << data << md5sum(data);
2206}
2207
2208void tst_QNetworkReply::putToFile()
2209{
2210 QFile file(testFileName);
2211
2212 QUrl url = QUrl::fromLocalFile(localfile: file.fileName());
2213 QNetworkRequest request(url);
2214 QNetworkReplyPtr reply;
2215
2216 QFETCH(QByteArray, data);
2217
2218 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data));
2219
2220 QCOMPARE(reply->url(), url);
2221 QCOMPARE(reply->error(), QNetworkReply::NoError);
2222 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
2223 QVERIFY(reply->readAll().isEmpty());
2224
2225 QVERIFY(file.open(QIODevice::ReadOnly));
2226 QCOMPARE(file.size(), qint64(data.size()));
2227 QByteArray contents = file.readAll();
2228 QCOMPARE(contents, data);
2229}
2230
2231void tst_QNetworkReply::putToFtp_data()
2232{
2233 putToFile_data();
2234}
2235
2236void tst_QNetworkReply::putToFtp()
2237{
2238 QUrl url("ftp://" + QtNetworkSettings::ftpServerName());
2239 url.setPath(path: QString("/qtest/upload/qnetworkaccess-putToFtp-%1-%2")
2240 .arg(a: QTest::currentDataTag())
2241 .arg(a: uniqueExtension));
2242
2243 QNetworkRequest request(url);
2244 QNetworkReplyPtr reply;
2245
2246 QFETCH(QByteArray, data);
2247
2248 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data));
2249
2250 QCOMPARE(reply->url(), url);
2251 QCOMPARE(reply->error(), QNetworkReply::NoError);
2252 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
2253 QVERIFY(reply->readAll().isEmpty());
2254
2255 // download the file again from FTP to make sure it was uploaded
2256 // correctly
2257 QNetworkAccessManager qnam;
2258 QNetworkRequest req(url);
2259 QNetworkReply *r = qnam.get(request: req);
2260
2261 QObject::connect(sender: r, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
2262 int count = 0;
2263 QSignalSpy spy(r, SIGNAL(downloadProgress(qint64,qint64)));
2264 while (!r->isFinished()) {
2265 QTestEventLoop::instance().enterLoop(secs: 10);
2266 if (count == spy.count() && !r->isFinished())
2267 break;
2268 count = spy.count();
2269 }
2270 QObject::disconnect(sender: r, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
2271
2272 QByteArray uploaded = r->readAll();
2273 QCOMPARE(uploaded.size(), data.size());
2274 QCOMPARE(uploaded, data);
2275
2276 r->close();
2277 QObject::connect(sender: r, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
2278 QTestEventLoop::instance().enterLoop(secs: 10);
2279 QObject::disconnect(sender: r, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
2280}
2281
2282void tst_QNetworkReply::putToFtpWithInvalidCredentials()
2283{
2284 QUrl url("ftp://" + QtNetworkSettings::ftpServerName());
2285 url.setPath(path: QString("/qtest/upload/qnetworkaccess-putToFtp-%1-%2")
2286 .arg(a: QTest::currentDataTag())
2287 .arg(a: uniqueExtension));
2288 url.setUserName(userName: "invalidUser");
2289 url.setPassword(password: "InvalidPassword");
2290 QNetworkRequest req(url);
2291 QNetworkReplyPtr r;
2292
2293 for (int i = 0; i < 2; i++)
2294 {
2295 runSimpleRequest(op: QNetworkAccessManager::PutOperation, request: req, reply&: r, data: QByteArray());
2296
2297 QVERIFY(r->isFinished());
2298 QCOMPARE(r->url(), url);
2299 QCOMPARE(r->error(), QNetworkReply::AuthenticationRequiredError);
2300 r->close();
2301 }
2302}
2303
2304void tst_QNetworkReply::putToHttp_data()
2305{
2306 putToFile_data();
2307}
2308
2309void tst_QNetworkReply::putToHttp()
2310{
2311 QUrl url("http://" + QtNetworkSettings::httpServerName());
2312 url.setPath(path: QString("/dav/qnetworkaccess-putToHttp-%1-%2")
2313 .arg(a: QTest::currentDataTag())
2314 .arg(a: uniqueExtension));
2315
2316 QNetworkRequest request(url);
2317 QNetworkReplyPtr reply;
2318
2319 QFETCH(QByteArray, data);
2320
2321 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data));
2322
2323 QCOMPARE(reply->url(), url);
2324 QCOMPARE(reply->error(), QNetworkReply::NoError);
2325
2326 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 201); // 201 Created
2327
2328 // download the file again from HTTP to make sure it was uploaded
2329 // correctly. HTTP/0.9 is enough
2330 QTcpSocket socket;
2331 socket.connectToHost(hostName: QtNetworkSettings::httpServerName(), port: 80);
2332 socket.write(data: "GET " + url.toEncoded(options: QUrl::RemoveScheme | QUrl::RemoveAuthority) + "\r\n");
2333 if (!socket.waitForDisconnected(msecs: 10000))
2334 QFAIL("Network timeout");
2335
2336 QByteArray uploadedData = socket.readAll();
2337 QCOMPARE(uploadedData, data);
2338}
2339
2340void tst_QNetworkReply::putToHttpSynchronous_data()
2341{
2342 uniqueExtension = createUniqueExtension();
2343 putToFile_data();
2344}
2345
2346void tst_QNetworkReply::putToHttpSynchronous()
2347{
2348 QUrl url("http://" + QtNetworkSettings::httpServerName());
2349 url.setPath(path: QString("/dav/qnetworkaccess-putToHttp-%1-%2")
2350 .arg(a: QTest::currentDataTag())
2351 .arg(a: uniqueExtension));
2352
2353 QNetworkRequest request(url);
2354 QNetworkReplyPtr reply;
2355
2356 QFETCH(QByteArray, data);
2357
2358 request.setAttribute(
2359 code: QNetworkRequest::SynchronousRequestAttribute,
2360 value: true);
2361
2362 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data));
2363
2364 QCOMPARE(reply->url(), url);
2365 QCOMPARE(reply->error(), QNetworkReply::NoError);
2366
2367 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 201); // 201 Created
2368
2369 // download the file again from HTTP to make sure it was uploaded
2370 // correctly. HTTP/0.9 is enough
2371 QTcpSocket socket;
2372 socket.connectToHost(hostName: QtNetworkSettings::httpServerName(), port: 80);
2373 socket.write(data: "GET " + url.toEncoded(options: QUrl::RemoveScheme | QUrl::RemoveAuthority) + "\r\n");
2374 if (!socket.waitForDisconnected(msecs: 10000))
2375 QFAIL("Network timeout");
2376
2377 QByteArray uploadedData = socket.readAll();
2378 QCOMPARE(uploadedData, data);
2379}
2380
2381void tst_QNetworkReply::postToHttp_data()
2382{
2383 putToFile_data();
2384}
2385
2386void tst_QNetworkReply::postToHttp()
2387{
2388 QUrl url("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/md5sum.cgi");
2389
2390 QNetworkRequest request(url);
2391 request.setRawHeader(headerName: "Content-Type", value: "application/octet-stream");
2392 QNetworkReplyPtr reply;
2393
2394 QFETCH(QByteArray, data);
2395
2396 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data));
2397
2398 QCOMPARE(reply->url(), url);
2399 QCOMPARE(reply->error(), QNetworkReply::NoError);
2400
2401 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
2402
2403 QFETCH(QByteArray, md5sum);
2404 QByteArray uploadedData = reply->readAll().trimmed();
2405 QCOMPARE(uploadedData, md5sum.toHex());
2406}
2407
2408void tst_QNetworkReply::postToHttpSynchronous_data()
2409{
2410 putToFile_data();
2411}
2412
2413void tst_QNetworkReply::postToHttpSynchronous()
2414{
2415 QUrl url("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/md5sum.cgi");
2416
2417 QNetworkRequest request(url);
2418 request.setRawHeader(headerName: "Content-Type", value: "application/octet-stream");
2419
2420 request.setAttribute(
2421 code: QNetworkRequest::SynchronousRequestAttribute,
2422 value: true);
2423
2424 QNetworkReplyPtr reply;
2425
2426 QFETCH(QByteArray, data);
2427
2428 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data));
2429
2430 QCOMPARE(reply->url(), url);
2431 QCOMPARE(reply->error(), QNetworkReply::NoError);
2432
2433 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
2434
2435 QFETCH(QByteArray, md5sum);
2436 QByteArray uploadedData = reply->readAll().trimmed();
2437 QCOMPARE(uploadedData, md5sum.toHex());
2438}
2439
2440void tst_QNetworkReply::postToHttpMultipart_data()
2441{
2442 QTest::addColumn<QUrl>(name: "url");
2443 QTest::addColumn<QHttpMultiPart *>(name: "multiPart");
2444 QTest::addColumn<QByteArray>(name: "expectedReplyData");
2445 QTest::addColumn<QByteArray>(name: "contentType");
2446
2447 QUrl url("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/multipart.cgi");
2448 QByteArray expectedData;
2449
2450
2451 // empty parts
2452
2453 QHttpMultiPart *emptyMultiPart = new QHttpMultiPart;
2454 QTest::newRow(dataTag: "empty") << url << emptyMultiPart << expectedData << QByteArray("mixed");
2455
2456 QHttpMultiPart *emptyRelatedMultiPart = new QHttpMultiPart;
2457 emptyRelatedMultiPart->setContentType(QHttpMultiPart::RelatedType);
2458 QTest::newRow(dataTag: "empty-related") << url << emptyRelatedMultiPart << expectedData << QByteArray("related");
2459
2460 QHttpMultiPart *emptyAlternativeMultiPart = new QHttpMultiPart;
2461 emptyAlternativeMultiPart->setContentType(QHttpMultiPart::AlternativeType);
2462 QTest::newRow(dataTag: "empty-alternative") << url << emptyAlternativeMultiPart << expectedData << QByteArray("alternative");
2463
2464
2465 // text-only parts
2466
2467 QHttpPart textPart;
2468 textPart.setHeader(header: QNetworkRequest::ContentTypeHeader, value: QVariant("text/plain"));
2469 textPart.setHeader(header: QNetworkRequest::ContentDispositionHeader, value: QVariant("form-data; name=\"text\""));
2470 textPart.setBody("7 bytes");
2471 QHttpMultiPart *multiPart1 = new QHttpMultiPart;
2472 multiPart1->setContentType(QHttpMultiPart::FormDataType);
2473 multiPart1->append(httpPart: textPart);
2474 expectedData = "key: text, value: 7 bytes\n";
2475 QTest::newRow(dataTag: "text") << url << multiPart1 << expectedData << QByteArray("form-data");
2476
2477 QHttpMultiPart *customMultiPart = new QHttpMultiPart;
2478 customMultiPart->append(httpPart: textPart);
2479 expectedData = "header: Content-Type, value: 'text/plain'\n"
2480 "header: Content-Disposition, value: 'form-data; name=\"text\"'\n"
2481 "content: 7 bytes\n"
2482 "\n";
2483 QTest::newRow(dataTag: "text-custom") << url << customMultiPart << expectedData << QByteArray("custom");
2484
2485 QHttpPart textPart2;
2486 textPart2.setHeader(header: QNetworkRequest::ContentTypeHeader, value: QVariant("text/plain"));
2487 textPart2.setRawHeader(headerName: "myRawHeader", headerValue: "myValue");
2488 textPart2.setHeader(header: QNetworkRequest::ContentDispositionHeader, value: QVariant("form-data; name=\"text2\""));
2489 textPart2.setBody("some more bytes");
2490 textPart2.setBodyDevice((QIODevice *) 1); // test whether setting and unsetting of the device works
2491 textPart2.setBodyDevice(0);
2492 QHttpMultiPart *multiPart2 = new QHttpMultiPart;
2493 multiPart2->setContentType(QHttpMultiPart::FormDataType);
2494 multiPart2->append(httpPart: textPart);
2495 multiPart2->append(httpPart: textPart2);
2496#ifdef QT_TEST_SERVER
2497 expectedData = "key: text, value: 7 bytes\n"
2498 "key: text2, value: some more bytes\n";
2499#else
2500 expectedData = "key: text2, value: some more bytes\n"
2501 "key: text, value: 7 bytes\n";
2502#endif
2503 QTest::newRow(dataTag: "text-text") << url << multiPart2 << expectedData << QByteArray("form-data");
2504
2505
2506 QHttpPart textPart3;
2507 textPart3.setHeader(header: QNetworkRequest::ContentTypeHeader, value: QVariant("text/plain"));
2508 textPart3.setHeader(header: QNetworkRequest::ContentDispositionHeader, value: QVariant("form-data; name=\"text3\""));
2509 textPart3.setRawHeader(headerName: "Content-Location", headerValue: "http://my.test.location.tld");
2510 textPart3.setBody("even more bytes");
2511 QHttpMultiPart *multiPart3 = new QHttpMultiPart;
2512 multiPart3->setContentType(QHttpMultiPart::AlternativeType);
2513 multiPart3->append(httpPart: textPart);
2514 multiPart3->append(httpPart: textPart2);
2515 multiPart3->append(httpPart: textPart3);
2516 expectedData = "header: Content-Type, value: 'text/plain'\n"
2517 "header: Content-Disposition, value: 'form-data; name=\"text\"'\n"
2518 "content: 7 bytes\n"
2519 "\n"
2520 "header: Content-Type, value: 'text/plain'\n"
2521 "header: myRawHeader, value: 'myValue'\n"
2522 "header: Content-Disposition, value: 'form-data; name=\"text2\"'\n"
2523 "content: some more bytes\n"
2524 "\n"
2525 "header: Content-Type, value: 'text/plain'\n"
2526 "header: Content-Disposition, value: 'form-data; name=\"text3\"'\n"
2527 "header: Content-Location, value: 'http://my.test.location.tld'\n"
2528 "content: even more bytes\n\n";
2529 QTest::newRow(dataTag: "text-text-text") << url << multiPart3 << expectedData << QByteArray("alternative");
2530
2531
2532
2533 // text and image parts
2534
2535 QHttpPart imagePart11;
2536 imagePart11.setHeader(header: QNetworkRequest::ContentTypeHeader, value: QVariant("image/jpeg"));
2537 imagePart11.setHeader(header: QNetworkRequest::ContentDispositionHeader, value: QVariant("form-data; name=\"testImage\""));
2538 imagePart11.setRawHeader(headerName: "Content-Location", headerValue: "http://my.test.location.tld");
2539 imagePart11.setRawHeader(headerName: "Content-ID", headerValue: "my@id.tld");
2540 QFile *file11 = new QFile(testDataDir + "/image1.jpg");
2541 file11->open(flags: QIODevice::ReadOnly);
2542 imagePart11.setBodyDevice(file11);
2543 QHttpMultiPart *imageMultiPart1 = new QHttpMultiPart(QHttpMultiPart::FormDataType);
2544 imageMultiPart1->append(httpPart: imagePart11);
2545 file11->setParent(imageMultiPart1);
2546 expectedData = "key: testImage, value: 87ef3bb319b004ba9e5e9c9fa713776e\n"; // md5 sum of file
2547 QTest::newRow(dataTag: "image") << url << imageMultiPart1 << expectedData << QByteArray("form-data");
2548
2549 QHttpPart imagePart21;
2550 imagePart21.setHeader(header: QNetworkRequest::ContentTypeHeader, value: QVariant("image/jpeg"));
2551 imagePart21.setHeader(header: QNetworkRequest::ContentDispositionHeader, value: QVariant("form-data; name=\"testImage1\""));
2552 imagePart21.setRawHeader(headerName: "Content-Location", headerValue: "http://my.test.location.tld");
2553 imagePart21.setRawHeader(headerName: "Content-ID", headerValue: "my@id.tld");
2554 QFile *file21 = new QFile(testDataDir + "/image1.jpg");
2555 file21->open(flags: QIODevice::ReadOnly);
2556 imagePart21.setBodyDevice(file21);
2557 QHttpMultiPart *imageMultiPart2 = new QHttpMultiPart();
2558 imageMultiPart2->setContentType(QHttpMultiPart::FormDataType);
2559 imageMultiPart2->append(httpPart: textPart);
2560 imageMultiPart2->append(httpPart: imagePart21);
2561 file21->setParent(imageMultiPart2);
2562 QHttpPart imagePart22;
2563 imagePart22.setHeader(header: QNetworkRequest::ContentTypeHeader, value: QVariant("image/jpeg"));
2564 imagePart22.setHeader(header: QNetworkRequest::ContentDispositionHeader, value: QVariant("form-data; name=\"testImage2\""));
2565 QFile *file22 = new QFile(testDataDir + "/image2.jpg");
2566 file22->open(flags: QIODevice::ReadOnly);
2567 imagePart22.setBodyDevice(file22);
2568 imageMultiPart2->append(httpPart: imagePart22);
2569 file22->setParent(imageMultiPart2);
2570#ifdef QT_TEST_SERVER
2571 expectedData = "key: text, value: 7 bytes\n"
2572 "key: testImage1, value: 87ef3bb319b004ba9e5e9c9fa713776e\n"
2573 "key: testImage2, value: 483761b893f7fb1bd2414344cd1f3dfb\n";
2574#else
2575 expectedData = "key: testImage1, value: 87ef3bb319b004ba9e5e9c9fa713776e\n"
2576 "key: text, value: 7 bytes\n"
2577 "key: testImage2, value: 483761b893f7fb1bd2414344cd1f3dfb\n";
2578#endif
2579 QTest::newRow(dataTag: "text-image-image") << url << imageMultiPart2 << expectedData << QByteArray("form-data");
2580
2581
2582 QHttpPart imagePart31;
2583 imagePart31.setHeader(header: QNetworkRequest::ContentTypeHeader, value: QVariant("image/jpeg"));
2584 imagePart31.setHeader(header: QNetworkRequest::ContentDispositionHeader, value: QVariant("form-data; name=\"testImage1\""));
2585 imagePart31.setRawHeader(headerName: "Content-Location", headerValue: "http://my.test.location.tld");
2586 imagePart31.setRawHeader(headerName: "Content-ID", headerValue: "my@id.tld");
2587 QFile *file31 = new QFile(testDataDir + "/image1.jpg");
2588 file31->open(flags: QIODevice::ReadOnly);
2589 imagePart31.setBodyDevice(file31);
2590 QHttpMultiPart *imageMultiPart3 = new QHttpMultiPart(QHttpMultiPart::FormDataType);
2591 imageMultiPart3->append(httpPart: imagePart31);
2592 file31->setParent(imageMultiPart3);
2593 QHttpPart imagePart32;
2594 imagePart32.setHeader(header: QNetworkRequest::ContentTypeHeader, value: QVariant("image/jpeg"));
2595 imagePart32.setHeader(header: QNetworkRequest::ContentDispositionHeader, value: QVariant("form-data; name=\"testImage2\""));
2596 QFile *file32 = new QFile(testDataDir + "/image2.jpg");
2597 file32->open(flags: QIODevice::ReadOnly);
2598 imagePart32.setBodyDevice(file31); // check that resetting works
2599 imagePart32.setBodyDevice(file32);
2600 imageMultiPart3->append(httpPart: imagePart32);
2601 file32->setParent(imageMultiPart3);
2602 QHttpPart imagePart33;
2603 imagePart33.setHeader(header: QNetworkRequest::ContentTypeHeader, value: QVariant("image/jpeg"));
2604 imagePart33.setHeader(header: QNetworkRequest::ContentDispositionHeader, value: QVariant("form-data; name=\"testImage3\""));
2605 QFile *file33 = new QFile(testDataDir + "/image3.jpg");
2606 file33->open(flags: QIODevice::ReadOnly);
2607 imagePart33.setBodyDevice(file33);
2608 imageMultiPart3->append(httpPart: imagePart33);
2609 file33->setParent(imageMultiPart3);
2610 expectedData = "key: testImage1, value: 87ef3bb319b004ba9e5e9c9fa713776e\n"
2611 "key: testImage2, value: 483761b893f7fb1bd2414344cd1f3dfb\n"
2612 "key: testImage3, value: ab0eb6fd4fcf8b4436254870b4513033\n";
2613 QTest::newRow(dataTag: "3-images") << url << imageMultiPart3 << expectedData << QByteArray("form-data");
2614
2615
2616 // note: nesting multiparts is not working currently; for that, the outputDevice would need to be public
2617
2618// QHttpPart imagePart41;
2619// imagePart41.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
2620// QFile *file41 = new QFile(testDataDir + "/image1.jpg");
2621// file41->open(QIODevice::ReadOnly);
2622// imagePart41.setBodyDevice(file41);
2623//
2624// QHttpMultiPart *innerMultiPart = new QHttpMultiPart();
2625// innerMultiPart->setContentType(QHttpMultiPart::FormDataType);
2626// textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant());
2627// innerMultiPart->append(textPart);
2628// innerMultiPart->append(imagePart41);
2629// textPart2.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant());
2630// innerMultiPart->append(textPart2);
2631//
2632// QHttpPart nestedPart;
2633// nestedPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"nestedMessage"));
2634// nestedPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("multipart/alternative; boundary=\"" + innerMultiPart->boundary() + "\""));
2635// innerMultiPart->outputDevice()->open(QIODevice::ReadOnly);
2636// nestedPart.setBodyDevice(innerMultiPart->outputDevice());
2637//
2638// QHttpMultiPart *outerMultiPart = new QHttpMultiPart;
2639// outerMultiPart->setContentType(QHttpMultiPart::FormDataType);
2640// outerMultiPart->append(textPart);
2641// outerMultiPart->append(nestedPart);
2642// outerMultiPart->append(textPart2);
2643// expectedData = "nothing"; // the CGI.pm module running on the test server does not understand nested multiparts
2644// openFiles.clear();
2645// openFiles << file41;
2646// QTest::newRow("nested") << url << outerMultiPart << expectedData << openFiles;
2647
2648
2649 // test setting large chunks of content with a byte array instead of a device (DISCOURAGED because of high memory consumption,
2650 // but we need to test that the behavior is correct)
2651 QHttpPart imagePart51;
2652 imagePart51.setHeader(header: QNetworkRequest::ContentTypeHeader, value: QVariant("image/jpeg"));
2653 imagePart51.setHeader(header: QNetworkRequest::ContentDispositionHeader, value: QVariant("form-data; name=\"testImage\""));
2654 QFile *file51 = new QFile(testDataDir + "/image1.jpg");
2655 file51->open(flags: QIODevice::ReadOnly);
2656 QByteArray imageData = file51->readAll();
2657 file51->close();
2658 delete file51;
2659 imagePart51.setBody("7 bytes"); // check that resetting works
2660 imagePart51.setBody(imageData);
2661 QHttpMultiPart *imageMultiPart5 = new QHttpMultiPart;
2662 imageMultiPart5->setContentType(QHttpMultiPart::FormDataType);
2663 imageMultiPart5->append(httpPart: imagePart51);
2664 expectedData = "key: testImage, value: 87ef3bb319b004ba9e5e9c9fa713776e\n"; // md5 sum of file
2665 QTest::newRow(dataTag: "image-as-content") << url << imageMultiPart5 << expectedData << QByteArray("form-data");
2666}
2667
2668void tst_QNetworkReply::postToHttpMultipart()
2669{
2670 QFETCH(QUrl, url);
2671
2672 static QSet<QByteArray> boundaries;
2673
2674 QNetworkRequest request(url);
2675 QNetworkReplyPtr reply;
2676
2677 QFETCH(QHttpMultiPart *, multiPart);
2678 QFETCH(QByteArray, expectedReplyData);
2679 QFETCH(QByteArray, contentType);
2680
2681 // hack for testing the setting of the content-type header by hand:
2682 if (contentType == "custom") {
2683 QByteArray contentType("multipart/custom; boundary=\"" + multiPart->boundary() + "\"");
2684 request.setHeader(header: QNetworkRequest::ContentTypeHeader, value: contentType);
2685 }
2686
2687 QVERIFY2(! boundaries.contains(multiPart->boundary()), "boundary '" + multiPart->boundary() + "' has been created twice");
2688 boundaries.insert(value: multiPart->boundary());
2689
2690 RUN_REQUEST(runMultipartRequest(request, reply, multiPart, "POST"));
2691 multiPart->deleteLater();
2692
2693 QCOMPARE(reply->url(), url);
2694 QCOMPARE(reply->error(), QNetworkReply::NoError);
2695
2696 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
2697
2698 QVERIFY(multiPart->boundary().count() > 20); // check that there is randomness after the "boundary_.oOo._" string
2699 QVERIFY(multiPart->boundary().count() < 70);
2700 QByteArray replyData = reply->readAll();
2701
2702 expectedReplyData.prepend(a: "content type: multipart/" + contentType + "; boundary=\"" + multiPart->boundary() + "\"\n");
2703// QEXPECT_FAIL("nested", "the server does not understand nested multipart messages", Continue); // see above
2704 QCOMPARE(replyData, expectedReplyData);
2705}
2706
2707void tst_QNetworkReply::multipartSkipIndices() // QTBUG-32534
2708{
2709 QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::MixedType);
2710 QUrl url("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/multipart.cgi");
2711 QNetworkRequest request(url);
2712 QList<QByteArray> parts;
2713 parts << QByteArray(56083, 'X') << QByteArray(468, 'X') << QByteArray(24952, 'X');
2714
2715 QHttpPart part1;
2716 part1.setHeader(header: QNetworkRequest::ContentDispositionHeader, value: "form-data; name=\"field1\"; filename=\"aaaa.bin\"");
2717 part1.setHeader(header: QNetworkRequest::ContentTypeHeader, value: "application/octet-stream");
2718 part1.setBody(parts.at(i: 0));
2719 multiPart->append(httpPart: part1);
2720
2721 QHttpPart part2;
2722 part2.setHeader(header: QNetworkRequest::ContentDispositionHeader, value: "form-data; name=\"field2\"; filename=\"bbbb.txt\"");
2723 part2.setHeader(header: QNetworkRequest::ContentTypeHeader, value: "text/plain");
2724 part2.setBody(parts.at(i: 1));
2725 multiPart->append(httpPart: part2);
2726
2727 QHttpPart part3;
2728 part3.setHeader(header: QNetworkRequest::ContentDispositionHeader, value: "form-data; name=\"text-3\"; filename=\"cccc.txt\"");
2729 part3.setHeader(header: QNetworkRequest::ContentTypeHeader, value: "text/plain");
2730 part3.setBody(parts.at(i: 2));
2731 multiPart->append(httpPart: part3);
2732
2733 QNetworkReplyPtr reply;
2734 RUN_REQUEST(runMultipartRequest(request, reply, multiPart, "POST"));
2735
2736 QCOMPARE(reply->error(), QNetworkReply::NoError);
2737
2738 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
2739 QByteArray line;
2740 int partIndex = 0;
2741 while ((line = reply->readLine()) != QByteArray("")) {
2742 if (line.startsWith(c: "content:")) {
2743 // before, the 3rd part would return garbled output at the end
2744 QCOMPARE("content: " + parts[partIndex++] + "\n", line);
2745 }
2746 }
2747 multiPart->deleteLater();
2748}
2749
2750void tst_QNetworkReply::putToHttpMultipart_data()
2751{
2752 postToHttpMultipart_data();
2753}
2754
2755void tst_QNetworkReply::putToHttpMultipart()
2756{
2757 QSKIP("test server script cannot handle PUT data yet");
2758 QFETCH(QUrl, url);
2759
2760 static QSet<QByteArray> boundaries;
2761
2762 QNetworkRequest request(url);
2763 QNetworkReplyPtr reply;
2764
2765 QFETCH(QHttpMultiPart *, multiPart);
2766 QFETCH(QByteArray, expectedReplyData);
2767 QFETCH(QByteArray, contentType);
2768
2769 // hack for testing the setting of the content-type header by hand:
2770 if (contentType == "custom") {
2771 QByteArray contentType("multipart/custom; boundary=\"" + multiPart->boundary() + "\"");
2772 request.setHeader(header: QNetworkRequest::ContentTypeHeader, value: contentType);
2773 }
2774
2775 QVERIFY2(! boundaries.contains(multiPart->boundary()), "boundary '" + multiPart->boundary() + "' has been created twice");
2776 boundaries.insert(value: multiPart->boundary());
2777
2778 RUN_REQUEST(runMultipartRequest(request, reply, multiPart, "PUT"));
2779 multiPart->deleteLater();
2780
2781 QCOMPARE(reply->url(), url);
2782 QCOMPARE(reply->error(), QNetworkReply::NoError);
2783
2784 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
2785
2786 QVERIFY(multiPart->boundary().count() > 20); // check that there is randomness after the "boundary_.oOo._" string
2787 QVERIFY(multiPart->boundary().count() < 70);
2788 QByteArray replyData = reply->readAll();
2789
2790 expectedReplyData.prepend(a: "content type: multipart/" + contentType + "; boundary=\"" + multiPart->boundary() + "\"\n");
2791// QEXPECT_FAIL("nested", "the server does not understand nested multipart messages", Continue); // see above
2792 QCOMPARE(replyData, expectedReplyData);
2793}
2794
2795#ifndef QT_NO_SSL
2796void tst_QNetworkReply::putToHttps_data()
2797{
2798 uniqueExtension = createUniqueExtension();
2799 putToFile_data();
2800}
2801
2802void tst_QNetworkReply::putToHttps()
2803{
2804 QUrl url("https://" + QtNetworkSettings::httpServerName());
2805 url.setPath(path: QString("/dav/qnetworkaccess-putToHttp-%1-%2")
2806 .arg(a: QTest::currentDataTag())
2807 .arg(a: uniqueExtension));
2808
2809 QNetworkRequest request(url);
2810 QList<QSslCertificate> certs = QSslCertificate::fromPath(path: testDataDir + certsFilePath);
2811 QSslConfiguration conf;
2812 conf.setCaCertificates(certs);
2813 request.setSslConfiguration(conf);
2814 QNetworkReplyPtr reply;
2815
2816 QFETCH(QByteArray, data);
2817
2818 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data));
2819
2820 QCOMPARE(reply->url(), url);
2821 QCOMPARE(reply->error(), QNetworkReply::NoError);
2822
2823 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 201); // 201 Created
2824
2825 // download the file again from HTTP to make sure it was uploaded
2826 // correctly. HTTP/0.9 is enough
2827 QTcpSocket socket;
2828 socket.connectToHost(hostName: QtNetworkSettings::httpServerName(), port: 80);
2829 socket.write(data: "GET " + url.toEncoded(options: QUrl::RemoveScheme | QUrl::RemoveAuthority) + "\r\n");
2830 if (!socket.waitForDisconnected(msecs: 10000))
2831 QFAIL("Network timeout");
2832
2833 QByteArray uploadedData = socket.readAll();
2834 QCOMPARE(uploadedData, data);
2835}
2836
2837void tst_QNetworkReply::putToHttpsSynchronous_data()
2838{
2839 uniqueExtension = createUniqueExtension();
2840 putToFile_data();
2841}
2842
2843void tst_QNetworkReply::putToHttpsSynchronous()
2844{
2845 QUrl url("https://" + QtNetworkSettings::httpServerName());
2846 url.setPath(path: QString("/dav/qnetworkaccess-putToHttp-%1-%2")
2847 .arg(a: QTest::currentDataTag())
2848 .arg(a: uniqueExtension));
2849
2850 QNetworkRequest request(url);
2851 QList<QSslCertificate> certs = QSslCertificate::fromPath(path: testDataDir + certsFilePath);
2852 QSslConfiguration conf;
2853 conf.setCaCertificates(certs);
2854 request.setSslConfiguration(conf);
2855 QNetworkReplyPtr reply;
2856
2857 QFETCH(QByteArray, data);
2858
2859 request.setAttribute(
2860 code: QNetworkRequest::SynchronousRequestAttribute,
2861 value: true);
2862
2863 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data));
2864
2865 QCOMPARE(reply->url(), url);
2866 QCOMPARE(reply->error(), QNetworkReply::NoError);
2867
2868 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 201); // 201 Created
2869
2870 // download the file again from HTTP to make sure it was uploaded
2871 // correctly. HTTP/0.9 is enough
2872 QTcpSocket socket;
2873 socket.connectToHost(hostName: QtNetworkSettings::httpServerName(), port: 80);
2874 socket.write(data: "GET " + url.toEncoded(options: QUrl::RemoveScheme | QUrl::RemoveAuthority) + "\r\n");
2875 if (!socket.waitForDisconnected(msecs: 10000))
2876 QFAIL("Network timeout");
2877
2878 QByteArray uploadedData = socket.readAll();
2879 QCOMPARE(uploadedData, data);
2880}
2881
2882void tst_QNetworkReply::postToHttps_data()
2883{
2884 putToFile_data();
2885}
2886
2887void tst_QNetworkReply::postToHttps()
2888{
2889 QUrl url("https://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/md5sum.cgi");
2890
2891 QNetworkRequest request(url);
2892 QList<QSslCertificate> certs = QSslCertificate::fromPath(path: testDataDir + certsFilePath);
2893 QSslConfiguration conf;
2894 conf.setCaCertificates(certs);
2895 request.setSslConfiguration(conf);
2896 request.setRawHeader(headerName: "Content-Type", value: "application/octet-stream");
2897 QNetworkReplyPtr reply;
2898
2899 QFETCH(QByteArray, data);
2900
2901 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data));
2902
2903 QCOMPARE(reply->url(), url);
2904 QCOMPARE(reply->error(), QNetworkReply::NoError);
2905
2906 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
2907
2908 QFETCH(QByteArray, md5sum);
2909 QByteArray uploadedData = reply->readAll().trimmed();
2910 QCOMPARE(uploadedData, md5sum.toHex());
2911}
2912
2913void tst_QNetworkReply::postToHttpsSynchronous_data()
2914{
2915 putToFile_data();
2916}
2917
2918void tst_QNetworkReply::postToHttpsSynchronous()
2919{
2920 QUrl url("https://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/md5sum.cgi");
2921
2922 QNetworkRequest request(url);
2923 QList<QSslCertificate> certs = QSslCertificate::fromPath(path: testDataDir + certsFilePath);
2924 QSslConfiguration conf;
2925 conf.setCaCertificates(certs);
2926 request.setSslConfiguration(conf);
2927 request.setRawHeader(headerName: "Content-Type", value: "application/octet-stream");
2928
2929 request.setAttribute(
2930 code: QNetworkRequest::SynchronousRequestAttribute,
2931 value: true);
2932
2933 QNetworkReplyPtr reply;
2934
2935 QFETCH(QByteArray, data);
2936
2937 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data));
2938
2939 QCOMPARE(reply->url(), url);
2940 QCOMPARE(reply->error(), QNetworkReply::NoError);
2941
2942 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
2943
2944 QFETCH(QByteArray, md5sum);
2945 QByteArray uploadedData = reply->readAll().trimmed();
2946 QCOMPARE(uploadedData, md5sum.toHex());
2947}
2948
2949void tst_QNetworkReply::postToHttpsMultipart_data()
2950{
2951 postToHttpMultipart_data();
2952}
2953
2954void tst_QNetworkReply::postToHttpsMultipart()
2955{
2956 QFETCH(QUrl, url);
2957 url.setScheme("https");
2958
2959 static QSet<QByteArray> boundaries;
2960
2961 QNetworkRequest request(url);
2962 QList<QSslCertificate> certs = QSslCertificate::fromPath(path: testDataDir + certsFilePath);
2963 QSslConfiguration conf;
2964 conf.setCaCertificates(certs);
2965 request.setSslConfiguration(conf);
2966 QNetworkReplyPtr reply;
2967
2968 QFETCH(QHttpMultiPart *, multiPart);
2969 QFETCH(QByteArray, expectedReplyData);
2970 QFETCH(QByteArray, contentType);
2971
2972 // hack for testing the setting of the content-type header by hand:
2973 if (contentType == "custom") {
2974 QByteArray contentType("multipart/custom; boundary=\"" + multiPart->boundary() + '"');
2975 request.setHeader(header: QNetworkRequest::ContentTypeHeader, value: contentType);
2976 }
2977
2978 QVERIFY2(! boundaries.contains(multiPart->boundary()), "boundary '" + multiPart->boundary() + "' has been created twice");
2979 boundaries.insert(value: multiPart->boundary());
2980
2981 RUN_REQUEST(runMultipartRequest(request, reply, multiPart, "POST"));
2982 multiPart->deleteLater();
2983
2984 QCOMPARE(reply->url(), url);
2985 QCOMPARE(reply->error(), QNetworkReply::NoError);
2986
2987 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
2988
2989 QVERIFY(multiPart->boundary().count() > 20); // check that there is randomness after the "boundary_.oOo._" string
2990 QVERIFY(multiPart->boundary().count() < 70);
2991 QByteArray replyData = reply->readAll();
2992
2993 expectedReplyData.prepend(a: "content type: multipart/" + contentType + "; boundary=\"" + multiPart->boundary() + "\"\n");
2994 QCOMPARE(replyData, expectedReplyData);
2995}
2996
2997#endif // QT_NO_SSL
2998
2999void tst_QNetworkReply::deleteFromHttp_data()
3000{
3001 QTest::addColumn<QUrl>(name: "url");
3002 QTest::addColumn<int>(name: "resultCode");
3003 QTest::addColumn<QNetworkReply::NetworkError>(name: "error");
3004
3005 // for status codes to expect, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
3006
3007 QString httpServer = QtNetworkSettings::httpServerName();
3008 QTest::newRow(dataTag: "405-method-not-allowed")
3009 << QUrl("http://" + httpServer + "/index.html")
3010 << 405 << QNetworkReply::ContentOperationNotPermittedError;
3011
3012 QTest::newRow(dataTag: "200-ok")
3013 << QUrl("http://" + httpServer + "/qtest/cgi-bin/http-delete.cgi?200-ok")
3014 << 200 << QNetworkReply::NoError;
3015
3016 QTest::newRow(dataTag: "202-accepted")
3017 << QUrl("http://" + httpServer + "/qtest/cgi-bin/http-delete.cgi?202-accepted")
3018 << 202 << QNetworkReply::NoError;
3019
3020 QTest::newRow(dataTag: "204-no-content")
3021 << QUrl("http://" + httpServer + "/qtest/cgi-bin/http-delete.cgi?204-no-content")
3022 << 204 << QNetworkReply::NoError;
3023
3024 QTest::newRow(dataTag: "404-not-found")
3025 << QUrl("http://" + httpServer + "/qtest/cgi-bin/http-delete.cgi?404-not-found")
3026 << 404 << QNetworkReply::ContentNotFoundError;
3027}
3028
3029void tst_QNetworkReply::deleteFromHttp()
3030{
3031 QFETCH(QUrl, url);
3032 QFETCH(int, resultCode);
3033 QFETCH(QNetworkReply::NetworkError, error);
3034 QNetworkRequest request(url);
3035 QNetworkReplyPtr reply;
3036 runSimpleRequest(op: QNetworkAccessManager::DeleteOperation, request, reply, data: 0);
3037 QCOMPARE(reply->url(), url);
3038 QCOMPARE(reply->error(), error);
3039 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), resultCode);
3040}
3041
3042void tst_QNetworkReply::putGetDeleteGetFromHttp_data()
3043{
3044 QTest::addColumn<QUrl>(name: "putUrl");
3045 QTest::addColumn<int>(name: "putResultCode");
3046 QTest::addColumn<QNetworkReply::NetworkError>(name: "putError");
3047 QTest::addColumn<QUrl>(name: "deleteUrl");
3048 QTest::addColumn<int>(name: "deleteResultCode");
3049 QTest::addColumn<QNetworkReply::NetworkError>(name: "deleteError");
3050 QTest::addColumn<QUrl>(name: "get2Url");
3051 QTest::addColumn<int>(name: "get2ResultCode");
3052 QTest::addColumn<QNetworkReply::NetworkError>(name: "get2Error");
3053
3054 QUrl url("http://" + QtNetworkSettings::httpServerName());
3055 url.setPath(path: QString("/dav/qnetworkaccess-putToHttp-%1-%2")
3056 .arg(a: QTest::currentDataTag())
3057 .arg(a: uniqueExtension));
3058
3059 // first use case: put, get (to check it is there), delete, get (to check it is not there anymore)
3060 QTest::newRow(dataTag: "success") << url << 201 << QNetworkReply::NoError << url << 204 << QNetworkReply::NoError << url << 404 << QNetworkReply::ContentNotFoundError;
3061
3062 QUrl wrongUrl("http://" + QtNetworkSettings::httpServerName());
3063 wrongUrl.setPath(path: QString("/dav/qnetworkaccess-thisURLisNotAvailable"));
3064
3065 // second use case: put, get (to check it is there), delete wrong URL, get (to check it is still there)
3066 QTest::newRow(dataTag: "delete-error") << url << 201 << QNetworkReply::NoError << wrongUrl << 404 << QNetworkReply::ContentNotFoundError << url << 200 << QNetworkReply::NoError;
3067
3068}
3069
3070void tst_QNetworkReply::putGetDeleteGetFromHttp()
3071{
3072 QFETCH(QUrl, putUrl);
3073 QFETCH(int, putResultCode);
3074 QFETCH(QNetworkReply::NetworkError, putError);
3075 QFETCH(QUrl, deleteUrl);
3076 QFETCH(int, deleteResultCode);
3077 QFETCH(QNetworkReply::NetworkError, deleteError);
3078 QFETCH(QUrl, get2Url);
3079 QFETCH(int, get2ResultCode);
3080 QFETCH(QNetworkReply::NetworkError, get2Error);
3081
3082 QNetworkRequest putRequest(putUrl);
3083 QNetworkRequest deleteRequest(deleteUrl);
3084 QNetworkRequest get2Request(get2Url);
3085 QNetworkReplyPtr reply;
3086
3087 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, putRequest, reply, 0));
3088 QCOMPARE(reply->error(), putError);
3089 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), putResultCode);
3090
3091 runSimpleRequest(op: QNetworkAccessManager::GetOperation, request: putRequest, reply, data: 0);
3092 QCOMPARE(reply->error(), QNetworkReply::NoError);
3093 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3094
3095 runSimpleRequest(op: QNetworkAccessManager::DeleteOperation, request: deleteRequest, reply, data: 0);
3096 QCOMPARE(reply->error(), deleteError);
3097 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), deleteResultCode);
3098
3099 runSimpleRequest(op: QNetworkAccessManager::GetOperation, request: get2Request, reply, data: 0);
3100 QCOMPARE(reply->error(), get2Error);
3101 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), get2ResultCode);
3102
3103}
3104
3105void tst_QNetworkReply::connectToIPv6Address_data()
3106{
3107 QTest::addColumn<QUrl>(name: "url");
3108 QTest::addColumn<QNetworkReply::NetworkError>(name: "error");
3109 QTest::addColumn<QByteArray>(name: "dataToSend");
3110 QTest::addColumn<QByteArray>(name: "hostfield");
3111 QTest::newRow(dataTag: "localhost") << QUrl(QByteArray("http://[::1]")) << QNetworkReply::NoError<< QByteArray("localhost") << QByteArray("[::1]");
3112 //QTest::newRow("ipv4localhost") << QUrl(QByteArray("http://127.0.0.1")) << QNetworkReply::NoError<< QByteArray("ipv4localhost") << QByteArray("127.0.0.1");
3113 //to add more test data here
3114}
3115
3116void tst_QNetworkReply::connectToIPv6Address()
3117{
3118 QFETCH(QUrl, url);
3119 QFETCH(QNetworkReply::NetworkError, error);
3120 QFETCH(QByteArray, dataToSend);
3121 QFETCH(QByteArray, hostfield);
3122
3123 if (!QtNetworkSettings::hasIPv6())
3124 QSKIP("system doesn't support ipv6!");
3125
3126 QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: ");
3127 httpResponse += QByteArray::number(dataToSend.size());
3128 httpResponse += "\r\n\r\n";
3129 httpResponse += dataToSend;
3130
3131 MiniHttpServer server(httpResponse, false, NULL/*thread*/, true/*useipv6*/);
3132 server.doClose = true;
3133
3134 url.setPort(server.serverPort());
3135 QNetworkRequest request(url);
3136
3137 QNetworkReplyPtr reply(manager.get(request));
3138 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
3139 QByteArray content = reply->readAll();
3140 //qDebug() << server.receivedData;
3141 QByteArray hostinfo = "\r\nHost: " + hostfield + ':' + QByteArray::number(server.serverPort()) + "\r\n";
3142 QVERIFY(server.receivedData.contains(hostinfo));
3143 QCOMPARE(content, dataToSend);
3144 QCOMPARE(reply->url(), request.url());
3145 QCOMPARE(reply->error(), error);
3146}
3147
3148void tst_QNetworkReply::sendCustomRequestToHttp_data()
3149{
3150 QTest::addColumn<QUrl>(name: "url");
3151 QTest::addColumn<QByteArray>(name: "verb");
3152 QTest::addColumn<QBuffer *>(name: "device");
3153 QTest::addColumn<int>(name: "resultCode");
3154 QTest::addColumn<QNetworkReply::NetworkError>(name: "error");
3155 QTest::addColumn<QByteArray>(name: "expectedContent");
3156
3157 QTest::newRow(dataTag: "options") << QUrl("http://" + QtNetworkSettings::httpServerName()) <<
3158 QByteArray("OPTIONS") << (QBuffer *) 0 << 200 << QNetworkReply::NoError << QByteArray();
3159 QTest::newRow(dataTag: "trace") << QUrl("http://" + QtNetworkSettings::httpServerName()) <<
3160 QByteArray("TRACE") << (QBuffer *) 0 << 200 << QNetworkReply::NoError << QByteArray();
3161 QTest::newRow(dataTag: "connect") << QUrl("http://" + QtNetworkSettings::httpServerName()) <<
3162 QByteArray("CONNECT") << (QBuffer *) 0 << 400 << QNetworkReply::ProtocolInvalidOperationError << QByteArray(); // 400 = Bad Request
3163 QTest::newRow(dataTag: "nonsense") << QUrl("http://" + QtNetworkSettings::httpServerName()) <<
3164 QByteArray("NONSENSE") << (QBuffer *) 0 << 501 << QNetworkReply::OperationNotImplementedError << QByteArray(); // 501 = Method Not Implemented
3165
3166 QByteArray ba("test");
3167 QBuffer *buffer = new QBuffer;
3168 buffer->setData(ba);
3169 buffer->open(openMode: QIODevice::ReadOnly);
3170 QTest::newRow(dataTag: "post") << QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/md5sum.cgi") << QByteArray("POST")
3171 << buffer << 200 << QNetworkReply::NoError << QByteArray("098f6bcd4621d373cade4e832627b4f6\n");
3172
3173 QByteArray ba2("test");
3174 QBuffer *buffer2 = new QBuffer;
3175 buffer2->setData(ba2);
3176 buffer2->open(openMode: QIODevice::ReadOnly);
3177 QTest::newRow(dataTag: "put") << QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/md5sum.cgi") << QByteArray("PUT")
3178 << buffer2 << 200 << QNetworkReply::NoError << QByteArray("098f6bcd4621d373cade4e832627b4f6\n");
3179}
3180
3181void tst_QNetworkReply::sendCustomRequestToHttp()
3182{
3183 QFETCH(QUrl, url);
3184 QNetworkRequest request(url);
3185 QNetworkReplyPtr reply;
3186 QFETCH(QByteArray, verb);
3187 QFETCH(QBuffer *, device);
3188 runCustomRequest(request, reply, verb, data: device);
3189 QCOMPARE(reply->url(), url);
3190 QFETCH(QNetworkReply::NetworkError, error);
3191 QCOMPARE(reply->error(), error);
3192 QFETCH(int, resultCode);
3193 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), resultCode);
3194 QFETCH(QByteArray, expectedContent);
3195 if (! expectedContent.isEmpty())
3196 QCOMPARE(reply->readAll(), expectedContent);
3197}
3198
3199void tst_QNetworkReply::ioGetFromData_data()
3200{
3201 QTest::addColumn<QString>(name: "urlStr");
3202 QTest::addColumn<QByteArray>(name: "data");
3203
3204 QTest::newRow(dataTag: "data-empty") << "data:," << QByteArray();
3205 QTest::newRow(dataTag: "data-literal") << "data:,foo" << QByteArray("foo");
3206 QTest::newRow(dataTag: "data-pct") << "data:,%3Cbody%20contentEditable%3Dtrue%3E%0D%0A"
3207 << QByteArray("<body contentEditable=true>\r\n");
3208 QTest::newRow(dataTag: "data-base64") << "data:;base64,UXQgaXMgZ3JlYXQh" << QByteArray("Qt is great!");
3209}
3210
3211void tst_QNetworkReply::ioGetFromData()
3212{
3213 QFETCH(QString, urlStr);
3214
3215 QUrl url = QUrl::fromEncoded(url: urlStr.toLatin1());
3216 QNetworkRequest request(url);
3217
3218 QNetworkReplyPtr reply(manager.get(request));
3219 DataReader reader(reply);
3220
3221 connect(ptr: reply, SIGNAL(finished()),
3222 receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
3223 QTestEventLoop::instance().enterLoop(secs: 10);
3224 QVERIFY(!QTestEventLoop::instance().timeout());
3225
3226 QCOMPARE(reply->url(), request.url());
3227 QCOMPARE(reply->error(), QNetworkReply::NoError);
3228
3229 QFETCH(QByteArray, data);
3230 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toInt(), data.size());
3231 QCOMPARE(reader.data.size(), data.size());
3232 QCOMPARE(reader.data, data);
3233}
3234
3235void tst_QNetworkReply::ioGetFromFileSpecial_data()
3236{
3237 getFromFileSpecial_data();
3238}
3239
3240void tst_QNetworkReply::ioGetFromFileSpecial()
3241{
3242#if defined(QT_TEST_SERVER) && defined(Q_OS_WIN)
3243 if (qstrcmp(QTest::currentDataTag(), "smb-path") == 0)
3244 QSKIP("Docker-based test server doesn't support smb protocol yet");
3245#endif
3246
3247 QFETCH(QString, fileName);
3248 QFETCH(QString, url);
3249
3250 QFile resource(fileName);
3251 QVERIFY(resource.open(QIODevice::ReadOnly));
3252
3253 QNetworkRequest request;
3254 request.setUrl(url);
3255 QNetworkReplyPtr reply(manager.get(request));
3256 DataReader reader(reply);
3257
3258 connect(ptr: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
3259 QTestEventLoop::instance().enterLoop(secs: 10);
3260 QVERIFY(!QTestEventLoop::instance().timeout());
3261
3262 QCOMPARE(reply->url(), request.url());
3263 QCOMPARE(reply->error(), QNetworkReply::NoError);
3264
3265 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), resource.size());
3266 QCOMPARE(qint64(reader.data.size()), resource.size());
3267 QCOMPARE(reader.data, resource.readAll());
3268}
3269
3270void tst_QNetworkReply::ioGetFromFile_data()
3271{
3272 putToFile_data();
3273}
3274
3275void tst_QNetworkReply::ioGetFromFile()
3276{
3277 QTemporaryFile file(QDir::currentPath() + "/temp-XXXXXX");
3278 file.setAutoRemove(true);
3279 QVERIFY2(file.open(), qPrintable(file.errorString()));
3280
3281 QFETCH(QByteArray, data);
3282 QCOMPARE(file.write(data), data.size());
3283 file.flush();
3284 QCOMPARE(file.size(), qint64(data.size()));
3285
3286 QNetworkRequest request(QUrl::fromLocalFile(localfile: file.fileName()));
3287 QNetworkReplyPtr reply(manager.get(request));
3288 QVERIFY(reply->isFinished()); // a file should immediately be done
3289 DataReader reader(reply);
3290
3291 connect(ptr: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
3292 QTestEventLoop::instance().enterLoop(secs: 10);
3293 QVERIFY(!QTestEventLoop::instance().timeout());
3294
3295 QCOMPARE(reply->url(), request.url());
3296 QCOMPARE(reply->error(), QNetworkReply::NoError);
3297
3298 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), file.size());
3299 QCOMPARE(qint64(reader.data.size()), file.size());
3300 QCOMPARE(reader.data, data);
3301}
3302
3303void tst_QNetworkReply::ioGetFromFtp_data()
3304{
3305 QTest::addColumn<QString>(name: "fileName");
3306 QTest::addColumn<qint64>(name: "expectedSize");
3307
3308 QTest::newRow(dataTag: "bigfile") << "bigfile" << Q_INT64_C(519240);
3309
3310 QFile file(testDataDir + "/rfc3252.txt");
3311 QTest::newRow(dataTag: "rfc3252.txt") << "rfc3252.txt" << file.size();
3312}
3313
3314void tst_QNetworkReply::ioGetFromFtp()
3315{
3316 QFETCH(QString, fileName);
3317 QFile reference(fileName);
3318 reference.open(flags: QIODevice::ReadOnly); // will fail for bigfile
3319
3320 QNetworkRequest request("ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/" + fileName);
3321 QNetworkReplyPtr reply(manager.get(request));
3322 DataReader reader(reply);
3323
3324 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
3325
3326 QCOMPARE(reply->url(), request.url());
3327 QCOMPARE(reply->error(), QNetworkReply::NoError);
3328
3329 QFETCH(qint64, expectedSize);
3330 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), expectedSize);
3331 QCOMPARE(qint64(reader.data.size()), expectedSize);
3332
3333 if (reference.isOpen())
3334 QCOMPARE(reader.data, reference.readAll());
3335}
3336
3337void tst_QNetworkReply::ioGetFromFtpWithReuse()
3338{
3339 QString fileName = testDataDir + "/rfc3252.txt";
3340 QFile reference(fileName);
3341 reference.open(flags: QIODevice::ReadOnly);
3342
3343 QNetworkRequest request(QUrl("ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/rfc3252.txt"));
3344
3345 // two concurrent (actually, consecutive) gets:
3346 QNetworkReplyPtr reply1(manager.get(request));
3347 DataReader reader1(reply1);
3348 QNetworkReplyPtr reply2(manager.get(request));
3349 DataReader reader2(reply2);
3350 QSignalSpy spy(reply1.data(), SIGNAL(finished()));
3351
3352 QCOMPARE(waitForFinish(reply1), int(Success));
3353 QCOMPARE(waitForFinish(reply2), int(Success));
3354
3355 QCOMPARE(reply1->url(), request.url());
3356 QCOMPARE(reply2->url(), request.url());
3357 QCOMPARE(reply1->error(), QNetworkReply::NoError);
3358 QCOMPARE(reply2->error(), QNetworkReply::NoError);
3359
3360 QCOMPARE(qint64(reader1.data.size()), reference.size());
3361 QCOMPARE(qint64(reader2.data.size()), reference.size());
3362 QCOMPARE(reply1->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
3363 QCOMPARE(reply2->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
3364
3365 QByteArray referenceData = reference.readAll();
3366 QCOMPARE(reader1.data, referenceData);
3367 QCOMPARE(reader2.data, referenceData);
3368}
3369
3370void tst_QNetworkReply::ioGetFromHttp()
3371{
3372 QFile reference(testDataDir + "/rfc3252.txt");
3373 QVERIFY(reference.open(QIODevice::ReadOnly));
3374
3375 QNetworkRequest request(QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"));
3376 QNetworkReplyPtr reply(manager.get(request));
3377 DataReader reader(reply);
3378
3379 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
3380
3381 QCOMPARE(reply->url(), request.url());
3382 QCOMPARE(reply->error(), QNetworkReply::NoError);
3383 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3384
3385 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
3386 QCOMPARE(qint64(reader.data.size()), reference.size());
3387
3388 QCOMPARE(reader.data, reference.readAll());
3389}
3390
3391void tst_QNetworkReply::ioGetFromHttpWithReuseParallel()
3392{
3393 QFile reference(testDataDir + "/rfc3252.txt");
3394 QVERIFY(reference.open(QIODevice::ReadOnly));
3395
3396 QNetworkRequest request(QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"));
3397 QNetworkReplyPtr reply1(manager.get(request));
3398 QNetworkReplyPtr reply2(manager.get(request));
3399 DataReader reader1(reply1);
3400 DataReader reader2(reply2);
3401 QSignalSpy spy(reply1.data(), SIGNAL(finished()));
3402
3403 QCOMPARE(waitForFinish(reply2), int(Success));
3404 QCOMPARE(waitForFinish(reply1), int(Success));
3405
3406 QCOMPARE(reply1->url(), request.url());
3407 QCOMPARE(reply2->url(), request.url());
3408 QCOMPARE(reply1->error(), QNetworkReply::NoError);
3409 QCOMPARE(reply2->error(), QNetworkReply::NoError);
3410 QCOMPARE(reply1->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3411 QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3412
3413 QCOMPARE(reply1->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
3414 QCOMPARE(reply2->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
3415 QCOMPARE(qint64(reader1.data.size()), reference.size());
3416 QCOMPARE(qint64(reader2.data.size()), reference.size());
3417
3418 QByteArray referenceData = reference.readAll();
3419 QCOMPARE(reader1.data, referenceData);
3420 QCOMPARE(reader2.data, referenceData);
3421}
3422
3423void tst_QNetworkReply::ioGetFromHttpWithReuseSequential()
3424{
3425 QFile reference(testDataDir + "/rfc3252.txt");
3426 QVERIFY(reference.open(QIODevice::ReadOnly));
3427
3428 QNetworkRequest request(QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"));
3429 {
3430 QNetworkReplyPtr reply(manager.get(request));
3431 DataReader reader(reply);
3432
3433 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
3434
3435 QCOMPARE(reply->url(), request.url());
3436 QCOMPARE(reply->error(), QNetworkReply::NoError);
3437 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3438
3439 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
3440 QCOMPARE(qint64(reader.data.size()), reference.size());
3441
3442 QCOMPARE(reader.data, reference.readAll());
3443 }
3444
3445 reference.seek(offset: 0);
3446 // rinse and repeat:
3447 {
3448 QNetworkReplyPtr reply(manager.get(request));
3449 DataReader reader(reply);
3450
3451 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
3452
3453 QCOMPARE(reply->url(), request.url());
3454 QCOMPARE(reply->error(), QNetworkReply::NoError);
3455 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3456
3457 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
3458 QCOMPARE(qint64(reader.data.size()), reference.size());
3459
3460 QCOMPARE(reader.data, reference.readAll());
3461 }
3462}
3463
3464void tst_QNetworkReply::ioGetFromHttpWithAuth_data()
3465{
3466 QTest::addColumn<QUrl>(name: "url");
3467 QTest::addColumn<QByteArray>(name: "expectedData");
3468 QTest::addColumn<int>(name: "expectedAuth");
3469
3470 QFile reference(testDataDir + "/rfc3252.txt");
3471 reference.open(flags: QIODevice::ReadOnly);
3472 QByteArray referenceData = reference.readAll();
3473 QString httpServer = QtNetworkSettings::httpServerName();
3474 QTest::newRow(dataTag: "basic")
3475 << QUrl("http://" + httpServer + "/qtest/rfcs-auth/rfc3252.txt")
3476 << referenceData << 1;
3477
3478 QTest::newRow(dataTag: "digest")
3479 << QUrl("http://" + httpServer + "/qtest/auth-digest/")
3480 << QByteArray("digest authentication successful\n") << 1;
3481
3482 //if url contains username & password, then it should be used
3483 QTest::newRow(dataTag: "basic-in-url")
3484 << QUrl("http://httptest:httptest@" + httpServer + "/qtest/rfcs-auth/rfc3252.txt")
3485 << referenceData << 0;
3486
3487 QTest::newRow(dataTag: "digest-in-url")
3488 << QUrl("http://httptest:httptest@" + httpServer + "/qtest/auth-digest/")
3489 << QByteArray("digest authentication successful\n") << 0;
3490
3491 // if url contains incorrect credentials, expect QNAM to ask for good ones (even if cached - matches behaviour of browsers)
3492 QTest::newRow(dataTag: "basic-bad-user-in-url")
3493 << QUrl("http://baduser:httptest@" + httpServer + "/qtest/rfcs-auth/rfc3252.txt")
3494 << referenceData << 3;
3495
3496 QTest::newRow(dataTag: "basic-bad-password-in-url")
3497 << QUrl("http://httptest:wrong@" + httpServer + "/qtest/rfcs-auth/rfc3252.txt")
3498 << referenceData << 3;
3499
3500 QTest::newRow(dataTag: "digest-bad-user-in-url")
3501 << QUrl("http://baduser:httptest@" + httpServer + "/qtest/auth-digest/")
3502 << QByteArray("digest authentication successful\n") << 3;
3503
3504 QTest::newRow(dataTag: "digest-bad-password-in-url")
3505 << QUrl("http://httptest:wrong@" + httpServer + "/qtest/auth-digest/")
3506 << QByteArray("digest authentication successful\n") << 3;
3507}
3508
3509void tst_QNetworkReply::ioGetFromHttpWithAuth()
3510{
3511 // This test sends three requests
3512 // The first two in parallel
3513 // The third after the first two finished
3514
3515 QFETCH(QUrl, url);
3516 QFETCH(QByteArray, expectedData);
3517 QFETCH(int, expectedAuth);
3518 QNetworkRequest request(url);
3519 {
3520 QNetworkReplyPtr reply1(manager.get(request));
3521 QUrl copy = url;
3522 copy.setUserName(userName: QString());
3523 copy.setPassword(password: QString());
3524 QNetworkRequest request2(copy);
3525 QNetworkReplyPtr reply2(manager.get(request: request2));
3526 DataReader reader1(reply1);
3527 DataReader reader2(reply2);
3528 QSignalSpy finishedspy(reply1.data(), SIGNAL(finished()));
3529
3530 QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
3531 connect(asender: &manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
3532 SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
3533
3534 QCOMPARE(waitForFinish(reply2), int(Success));
3535 QCOMPARE(waitForFinish(reply1), int(Success));
3536
3537 manager.disconnect(SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
3538 receiver: this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
3539
3540 QCOMPARE(reply1->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3541 QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3542 QCOMPARE(reader1.data, expectedData);
3543 QCOMPARE(reader2.data, expectedData);
3544
3545 QCOMPARE(authspy.count(), (expectedAuth ? 1 : 0));
3546 expectedAuth = qMax(a: 0, b: expectedAuth - 1);
3547 }
3548
3549 // rinse and repeat:
3550 {
3551 QNetworkReplyPtr reply(manager.get(request));
3552 DataReader reader(reply);
3553
3554 QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
3555 connect(asender: &manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
3556 SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
3557
3558 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
3559
3560 manager.disconnect(SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
3561 receiver: this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
3562
3563 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3564 QCOMPARE(reader.data, expectedData);
3565
3566 QCOMPARE(authspy.count(), (expectedAuth ? 1 : 0));
3567 expectedAuth = qMax(a: 0, b: expectedAuth - 1);
3568 }
3569
3570 // now check with synchronous calls:
3571 {
3572 request.setAttribute(
3573 code: QNetworkRequest::SynchronousRequestAttribute,
3574 value: true);
3575
3576 QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
3577 QNetworkReplyPtr replySync(manager.get(request));
3578 QVERIFY(replySync->isFinished()); // synchronous
3579 if (expectedAuth) {
3580 // bad credentials in a synchronous request should just fail
3581 QCOMPARE(replySync->error(), QNetworkReply::AuthenticationRequiredError);
3582 } else {
3583 QCOMPARE(authspy.count(), 0);
3584
3585 // we cannot use a data reader here, since that connects to the readyRead signal,
3586 // just use readAll()
3587
3588 // the only thing we check here is that the auth cache was used when using synchronous requests
3589 QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3590 QCOMPARE(replySync->readAll(), expectedData);
3591 }
3592 }
3593
3594 // check that credentials are used from cache if the same url is requested without credentials
3595 {
3596 url.setUserInfo(userInfo: QString());
3597 request.setUrl(url);
3598 request.setAttribute(
3599 code: QNetworkRequest::SynchronousRequestAttribute,
3600 value: true);
3601
3602 QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
3603 QNetworkReplyPtr replySync(manager.get(request));
3604 QVERIFY(replySync->isFinished()); // synchronous
3605 if (expectedAuth) {
3606 // bad credentials in a synchronous request should just fail
3607 QCOMPARE(replySync->error(), QNetworkReply::AuthenticationRequiredError);
3608 } else {
3609 QCOMPARE(authspy.count(), 0);
3610
3611 // we cannot use a data reader here, since that connects to the readyRead signal,
3612 // just use readAll()
3613
3614 // the only thing we check here is that the auth cache was used when using synchronous requests
3615 QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3616 QCOMPARE(replySync->readAll(), expectedData);
3617 }
3618 }
3619}
3620
3621void tst_QNetworkReply::ioGetFromHttpWithAuthSynchronous()
3622{
3623 // verify that we do not enter an endless loop with synchronous calls and wrong credentials
3624 // the case when we succeed with the login is tested in ioGetFromHttpWithAuth()
3625
3626 QNetworkRequest request(QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfcs-auth/rfc3252.txt"));
3627 request.setAttribute(
3628 code: QNetworkRequest::SynchronousRequestAttribute,
3629 value: true);
3630
3631 QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
3632 QNetworkReplyPtr replySync(manager.get(request));
3633 QVERIFY(replySync->isFinished()); // synchronous
3634 QCOMPARE(replySync->error(), QNetworkReply::AuthenticationRequiredError);
3635 QCOMPARE(authspy.count(), 0);
3636 QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 401);
3637}
3638
3639#ifndef QT_NO_NETWORKPROXY
3640void tst_QNetworkReply::ioGetFromHttpWithProxyAuth()
3641{
3642 // This test sends three requests
3643 // The first two in parallel
3644 // The third after the first two finished
3645 QFile reference(testDataDir + "/rfc3252.txt");
3646 QVERIFY(reference.open(QIODevice::ReadOnly));
3647
3648 QNetworkProxy proxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129);
3649 QNetworkRequest request(QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"));
3650 {
3651 manager.setProxy(proxy);
3652 QNetworkReplyPtr reply1(manager.get(request));
3653 QNetworkReplyPtr reply2(manager.get(request));
3654 manager.setProxy(QNetworkProxy());
3655
3656 DataReader reader1(reply1);
3657 DataReader reader2(reply2);
3658 QSignalSpy finishedspy(reply1.data(), SIGNAL(finished()));
3659
3660 QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3661 connect(asender: &manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
3662 SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3663
3664 QCOMPARE(waitForFinish(reply2), int(Success));
3665 QCOMPARE(waitForFinish(reply1), int(Success));
3666
3667 manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
3668 receiver: this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3669
3670 QCOMPARE(reply1->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3671 QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3672 QByteArray referenceData = reference.readAll();
3673 QCOMPARE(reader1.data, referenceData);
3674 QCOMPARE(reader2.data, referenceData);
3675
3676 QCOMPARE(authspy.count(), 1);
3677 }
3678
3679 reference.seek(offset: 0);
3680 // rinse and repeat:
3681 {
3682 manager.setProxy(proxy);
3683 QNetworkReplyPtr reply(manager.get(request));
3684 DataReader reader(reply);
3685 manager.setProxy(QNetworkProxy());
3686
3687 QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3688 connect(asender: &manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
3689 SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3690
3691 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
3692
3693 manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
3694 receiver: this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3695
3696 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3697 QCOMPARE(reader.data, reference.readAll());
3698
3699 QCOMPARE(authspy.count(), 0);
3700 }
3701
3702 // now check with synchronous calls:
3703 reference.seek(offset: 0);
3704 {
3705 request.setAttribute(
3706 code: QNetworkRequest::SynchronousRequestAttribute,
3707 value: true);
3708
3709 QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3710 QNetworkReplyPtr replySync(manager.get(request));
3711 QVERIFY(replySync->isFinished()); // synchronous
3712 QCOMPARE(authspy.count(), 0);
3713
3714 // we cannot use a data reader here, since that connects to the readyRead signal,
3715 // just use readAll()
3716
3717 // the only thing we check here is that the proxy auth cache was used when using synchronous requests
3718 QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3719 QCOMPARE(replySync->readAll(), reference.readAll());
3720 }
3721}
3722
3723void tst_QNetworkReply::ioGetFromHttpWithProxyAuthSynchronous()
3724{
3725 // verify that we do not enter an endless loop with synchronous calls and wrong credentials
3726 // the case when we succeed with the login is tested in ioGetFromHttpWithAuth()
3727
3728 QNetworkProxy proxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129);
3729 QNetworkRequest request(QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"));
3730 manager.setProxy(proxy);
3731 request.setAttribute(
3732 code: QNetworkRequest::SynchronousRequestAttribute,
3733 value: true);
3734
3735 QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3736 QNetworkReplyPtr replySync(manager.get(request));
3737 manager.setProxy(QNetworkProxy()); // reset
3738 QVERIFY(replySync->isFinished()); // synchronous
3739 QCOMPARE(replySync->error(), QNetworkReply::ProxyAuthenticationRequiredError);
3740 QCOMPARE(authspy.count(), 0);
3741 QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 407);
3742}
3743
3744void tst_QNetworkReply::ioGetFromHttpWithSocksProxy()
3745{
3746 // HTTP caching proxies are tested by the above function
3747 // test SOCKSv5 proxies too
3748
3749 QFile reference(testDataDir + "/rfc3252.txt");
3750 QVERIFY(reference.open(QIODevice::ReadOnly));
3751
3752 QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1080);
3753 QNetworkRequest request(QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"));
3754 {
3755 manager.setProxy(proxy);
3756 QNetworkReplyPtr reply(manager.get(request));
3757 DataReader reader(reply);
3758 manager.setProxy(QNetworkProxy());
3759
3760 QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3761 connect(asender: &manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
3762 SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3763
3764 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
3765
3766 manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
3767 receiver: this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3768
3769 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3770 QCOMPARE(reader.data, reference.readAll());
3771
3772 QCOMPARE(authspy.count(), 0);
3773 }
3774
3775 // set an invalid proxy just to make sure that we can't load
3776 proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1079);
3777 {
3778 manager.setProxy(proxy);
3779 QNetworkReplyPtr reply(manager.get(request));
3780 DataReader reader(reply);
3781 manager.setProxy(QNetworkProxy());
3782
3783 QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3784 connect(asender: &manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
3785 SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3786
3787 QCOMPARE(waitForFinish(reply), int(Failure));
3788
3789 manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
3790 receiver: this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
3791
3792 QVERIFY(!reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).isValid());
3793 QVERIFY(reader.data.isEmpty());
3794
3795 QVERIFY(int(reply->error()) > 0);
3796 QEXPECT_FAIL("", "QTcpSocket doesn't return enough information yet", Continue);
3797 QCOMPARE(int(reply->error()), int(QNetworkReply::ProxyConnectionRefusedError));
3798
3799 QCOMPARE(authspy.count(), 0);
3800 }
3801}
3802#endif // !QT_NO_NETWORKPROXY
3803
3804#ifndef QT_NO_SSL
3805void tst_QNetworkReply::ioGetFromHttpsWithSslErrors()
3806{
3807 QFile reference(testDataDir + "/rfc3252.txt");
3808 QVERIFY(reference.open(QIODevice::ReadOnly));
3809
3810 QNetworkRequest request(QUrl("https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"));
3811 QNetworkReplyPtr reply(manager.get(request));
3812 DataReader reader(reply);
3813
3814 QSignalSpy sslspy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
3815 connect(asender: &manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
3816 SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
3817 connect(ptr: reply, SIGNAL(metaDataChanged()), SLOT(storeSslConfiguration()));
3818
3819 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
3820
3821 manager.disconnect(SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
3822 receiver: this, SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
3823
3824 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3825 QCOMPARE(reader.data, reference.readAll());
3826
3827 QCOMPARE(sslspy.count(), 1);
3828
3829 QVERIFY(!storedSslConfiguration.isNull());
3830 QVERIFY(!reply->sslConfiguration().isNull());
3831}
3832
3833void tst_QNetworkReply::ioGetFromHttpsWithIgnoreSslErrors()
3834{
3835 // same as above, except that we call ignoreSslErrors and don't connect
3836 // to the sslErrors() signal (which is *still* emitted)
3837
3838 QFile reference(testDataDir + "/rfc3252.txt");
3839 QVERIFY(reference.open(QIODevice::ReadOnly));
3840
3841 QNetworkRequest request(QUrl("https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"));
3842
3843 QNetworkReplyPtr reply(manager.get(request));
3844 reply->ignoreSslErrors();
3845 DataReader reader(reply);
3846
3847 QSignalSpy sslspy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
3848 connect(ptr: reply, SIGNAL(metaDataChanged()), SLOT(storeSslConfiguration()));
3849
3850 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
3851
3852 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3853 QCOMPARE(reader.data, reference.readAll());
3854
3855 QCOMPARE(sslspy.count(), 1);
3856
3857 QVERIFY(!storedSslConfiguration.isNull());
3858 QVERIFY(!reply->sslConfiguration().isNull());
3859}
3860
3861void tst_QNetworkReply::ioGetFromHttpsWithSslHandshakeError()
3862{
3863 QFile reference(testDataDir + "/rfc3252.txt");
3864 QVERIFY(reference.open(QIODevice::ReadOnly));
3865
3866 QNetworkRequest request(QUrl("https://" + QtNetworkSettings::httpServerName() + ":80"));
3867
3868 QNetworkReplyPtr reply(manager.get(request));
3869 reply->ignoreSslErrors();
3870 DataReader reader(reply);
3871
3872 QSignalSpy sslspy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
3873 connect(ptr: reply, SIGNAL(metaDataChanged()), SLOT(storeSslConfiguration()));
3874
3875 QCOMPARE(waitForFinish(reply), int(Failure));
3876
3877 QCOMPARE(reply->error(), QNetworkReply::SslHandshakeFailedError);
3878 QCOMPARE(sslspy.count(), 0);
3879}
3880#endif
3881
3882void tst_QNetworkReply::ioGetFromHttpBrokenServer_data()
3883{
3884 QTest::addColumn<QByteArray>(name: "dataToSend");
3885 QTest::addColumn<bool>(name: "doDisconnect");
3886
3887 QTest::newRow(dataTag: "no-newline") << QByteArray("Hello World") << false;
3888
3889 // these are OK now, we just eat the lonely newlines
3890 //QTest::newRow("just-newline") << QByteArray("\r\n") << false;
3891 //QTest::newRow("just-2newline") << QByteArray("\r\n\r\n") << false;
3892
3893 QTest::newRow(dataTag: "with-newlines") << QByteArray("Long first line\r\nLong second line") << false;
3894 QTest::newRow(dataTag: "with-newlines2") << QByteArray("\r\nSecond line") << false;
3895 QTest::newRow(dataTag: "with-newlines3") << QByteArray("ICY\r\nSecond line") << false;
3896 QTest::newRow(dataTag: "invalid-version") << QByteArray("HTTP/123 200 \r\n") << false;
3897 QTest::newRow(dataTag: "invalid-version2") << QByteArray("HTTP/a.\033 200 \r\n") << false;
3898 QTest::newRow(dataTag: "invalid-reply-code") << QByteArray("HTTP/1.0 fuu \r\n") << false;
3899
3900 QTest::newRow(dataTag: "empty+disconnect") << QByteArray() << true;
3901
3902 QTest::newRow(dataTag: "no-newline+disconnect") << QByteArray("Hello World") << true;
3903 QTest::newRow(dataTag: "just-newline+disconnect") << QByteArray("\r\n") << true;
3904 QTest::newRow(dataTag: "just-2newline+disconnect") << QByteArray("\r\n\r\n") << true;
3905 QTest::newRow(dataTag: "with-newlines+disconnect") << QByteArray("Long first line\r\nLong second line") << true;
3906 QTest::newRow(dataTag: "with-newlines2+disconnect") << QByteArray("\r\nSecond line") << true;
3907 QTest::newRow(dataTag: "with-newlines3+disconnect") << QByteArray("ICY\r\nSecond line") << true;
3908
3909 QTest::newRow(dataTag: "invalid-version+disconnect") << QByteArray("HTTP/123 200 ") << true;
3910 QTest::newRow(dataTag: "invalid-version2+disconnect") << QByteArray("HTTP/a.\033 200 ") << true;
3911 QTest::newRow(dataTag: "invalid-reply-code+disconnect") << QByteArray("HTTP/1.0 fuu ") << true;
3912
3913 QTest::newRow(dataTag: "immediate disconnect") << QByteArray("") << true;
3914 QTest::newRow(dataTag: "justHalfStatus+disconnect") << QByteArray("HTTP/1.1") << true;
3915 QTest::newRow(dataTag: "justStatus+disconnect") << QByteArray("HTTP/1.1 200 OK\r\n") << true;
3916 QTest::newRow(dataTag: "justStatusAndHalfHeaders+disconnect") << QByteArray("HTTP/1.1 200 OK\r\nContent-L") << true;
3917
3918 QTest::newRow(dataTag: "halfContent+disconnect") << QByteArray("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nAB") << true;
3919
3920}
3921
3922void tst_QNetworkReply::ioGetFromHttpBrokenServer()
3923{
3924 QFETCH(QByteArray, dataToSend);
3925 QFETCH(bool, doDisconnect);
3926 MiniHttpServer server(dataToSend);
3927 server.doClose = doDisconnect;
3928
3929 QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
3930 QNetworkReplyPtr reply(manager.get(request));
3931 QSignalSpy spy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
3932
3933 QCOMPARE(waitForFinish(reply), int(Failure));
3934
3935 QCOMPARE(reply->url(), request.url());
3936 QCOMPARE(spy.count(), 1);
3937 QVERIFY(reply->error() != QNetworkReply::NoError);
3938}
3939
3940void tst_QNetworkReply::ioGetFromHttpStatus100_data()
3941{
3942 QTest::addColumn<QByteArray>(name: "dataToSend");
3943 QTest::addColumn<int>(name: "statusCode");
3944 QTest::newRow(dataTag: "normal") << QByteArray("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200;
3945 QTest::newRow(dataTag: "minimal") << QByteArray("HTTP/1.1 100 Continue\n\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200;
3946 QTest::newRow(dataTag: "minimal2") << QByteArray("HTTP/1.1 100 Continue\n\nHTTP/1.0 200 OK\r\n\r\n") << 200;
3947 QTest::newRow(dataTag: "minimal3") << QByteArray("HTTP/1.1 100 Continue\n\nHTTP/1.0 200 OK\n\n") << 200;
3948 QTest::newRow(dataTag: "minimal+404") << QByteArray("HTTP/1.1 100 Continue\n\nHTTP/1.0 204 No Content\r\n\r\n") << 204;
3949 QTest::newRow(dataTag: "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;
3950 QTest::newRow(dataTag: "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;
3951}
3952
3953void tst_QNetworkReply::ioGetFromHttpStatus100()
3954{
3955 QFETCH(QByteArray, dataToSend);
3956 QFETCH(int, statusCode);
3957 MiniHttpServer server(dataToSend);
3958 server.doClose = true;
3959
3960 QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
3961 QNetworkReplyPtr reply(manager.get(request));
3962
3963 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
3964
3965 QCOMPARE(reply->url(), request.url());
3966 QCOMPARE(reply->error(), QNetworkReply::NoError);
3967 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), statusCode);
3968 QVERIFY(reply->rawHeader("bla").isNull());
3969}
3970
3971void tst_QNetworkReply::ioGetFromHttpNoHeaders_data()
3972{
3973 QTest::addColumn<QByteArray>(name: "dataToSend");
3974 QTest::newRow(dataTag: "justStatus+noheaders+disconnect") << QByteArray("HTTP/1.0 200 OK\r\n\r\n");
3975}
3976
3977void tst_QNetworkReply::ioGetFromHttpNoHeaders()
3978{
3979 QFETCH(QByteArray, dataToSend);
3980 MiniHttpServer server(dataToSend);
3981 server.doClose = true;
3982
3983 QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
3984 QNetworkReplyPtr reply(manager.get(request));
3985
3986 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
3987
3988 QCOMPARE(reply->url(), request.url());
3989 QCOMPARE(reply->error(), QNetworkReply::NoError);
3990 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
3991}
3992
3993void tst_QNetworkReply::ioGetFromHttpWithCache_data()
3994{
3995 qRegisterMetaType<MyMemoryCache::CachedContent>();
3996 QTest::addColumn<QByteArray>(name: "dataToSend");
3997 QTest::addColumn<QString>(name: "body");
3998 QTest::addColumn<MyMemoryCache::CachedContent>(name: "cachedReply");
3999 QTest::addColumn<int>(name: "cacheMode");
4000 QTest::addColumn<QStringList>(name: "extraHttpHeaders");
4001 QTest::addColumn<bool>(name: "loadedFromCache");
4002 QTest::addColumn<bool>(name: "networkUsed");
4003
4004 QByteArray reply200 =
4005 "HTTP/1.0 200\r\n"
4006 "Connection: keep-alive\r\n"
4007 "Content-Type: text/plain\r\n"
4008 "Cache-control: no-store\r\n"
4009 "Content-length: 8\r\n"
4010 "\r\n"
4011 "Reloaded";
4012 QByteArray reply304 =
4013 "HTTP/1.0 304 Use Cache\r\n"
4014 "Connection: keep-alive\r\n"
4015 "\r\n";
4016
4017 QTest::newRow(dataTag: "not-cached,always-network")
4018 << reply200 << "Reloaded" << MyMemoryCache::CachedContent() << int(QNetworkRequest::AlwaysNetwork) << QStringList() << false << true;
4019 QTest::newRow(dataTag: "not-cached,prefer-network")
4020 << reply200 << "Reloaded" << MyMemoryCache::CachedContent() << int(QNetworkRequest::PreferNetwork) << QStringList() << false << true;
4021 QTest::newRow(dataTag: "not-cached,prefer-cache")
4022 << reply200 << "Reloaded" << MyMemoryCache::CachedContent() << int(QNetworkRequest::PreferCache) << QStringList() << false << true;
4023
4024 QDateTime present = QDateTime::currentDateTime().toUTC();
4025 QDateTime past = present.addSecs(secs: -3600);
4026 QDateTime future = present.addSecs(secs: 3600);
4027 static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'";
4028
4029 QNetworkCacheMetaData::RawHeaderList rawHeaders;
4030 MyMemoryCache::CachedContent content;
4031 content.second = "Not-reloaded";
4032 content.first.setLastModified(past);
4033
4034 // "no-cache"
4035 rawHeaders.clear();
4036 rawHeaders << QNetworkCacheMetaData::RawHeader("Date", QLocale::c().toString(dateTime: past, format: dateFormat).toLatin1())
4037 << QNetworkCacheMetaData::RawHeader("Cache-control", "no-cache");
4038 content.first.setRawHeaders(rawHeaders);
4039 content.first.setLastModified(past);
4040 content.first.setExpirationDate(future);
4041
4042 // "no-cache" does not mean "no cache", just that we must consult remote first
4043 QTest::newRow(dataTag: "no-cache,200,always-network")
4044 << reply200 << "Reloaded" << content << int(QNetworkRequest::AlwaysNetwork) << QStringList() << false << true;
4045 QTest::newRow(dataTag: "no-cache,200,prefer-network")
4046 << reply200 << "Reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << false << true;
4047 QTest::newRow(dataTag: "no-cache,200,prefer-cache")
4048 << reply200 << "Reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << false << true;
4049 // We're not allowed by the spec to deliver cached data without checking if it is still
4050 // up-to-date.
4051 QTest::newRow(dataTag: "no-cache,200,always-cache")
4052 << reply200 << QString() << content << int(QNetworkRequest::AlwaysCache) << QStringList() << false << false;
4053
4054 QTest::newRow(dataTag: "no-cache,304,prefer-network")
4055 << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << true << true;
4056 QTest::newRow(dataTag: "no-cache,304,prefer-cache")
4057 << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << true;
4058 QTest::newRow(dataTag: "no-cache,304,always-cache")
4059 << reply304 << QString() << content << int(QNetworkRequest::AlwaysCache) << QStringList() << false << false;
4060
4061 //
4062 // Set to expired
4063 //
4064 rawHeaders.clear();
4065 rawHeaders << QNetworkCacheMetaData::RawHeader("Date", QLocale::c().toString(dateTime: past, format: dateFormat).toLatin1())
4066 << QNetworkCacheMetaData::RawHeader("Cache-control", "max-age=0"); // isn't used in cache loading
4067 content.first.setRawHeaders(rawHeaders);
4068 content.first.setLastModified(past);
4069 content.first.setExpirationDate(past);
4070
4071 QTest::newRow(dataTag: "expired,200,prefer-network")
4072 << reply200 << "Reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << false << true;
4073 QTest::newRow(dataTag: "expired,200,prefer-cache")
4074 << reply200 << "Reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << false << true;
4075
4076 QTest::newRow(dataTag: "expired,304,prefer-network")
4077 << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << true << true;
4078 QTest::newRow(dataTag: "expired,304,prefer-cache")
4079 << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << true;
4080
4081 //
4082 // Set to not-expired
4083 //
4084 rawHeaders.clear();
4085 rawHeaders << QNetworkCacheMetaData::RawHeader("Date", QLocale::c().toString(dateTime: past, format: dateFormat).toLatin1())
4086 << QNetworkCacheMetaData::RawHeader("Cache-control", "max-age=7200"); // isn't used in cache loading
4087 content.first.setRawHeaders(rawHeaders);
4088 content.first.setExpirationDate(future);
4089
4090 QTest::newRow(dataTag: "not-expired,200,always-network")
4091 << reply200 << "Reloaded" << content << int(QNetworkRequest::AlwaysNetwork) << QStringList() << false << true;
4092 QTest::newRow(dataTag: "not-expired,200,prefer-network")
4093 << reply200 << "Not-reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << true << false;
4094 QTest::newRow(dataTag: "not-expired,200,prefer-cache")
4095 << reply200 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << false;
4096 QTest::newRow(dataTag: "not-expired,200,always-cache")
4097 << reply200 << "Not-reloaded" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << true << false;
4098
4099 QTest::newRow(dataTag: "not-expired,304,prefer-network")
4100 << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << true << false;
4101 QTest::newRow(dataTag: "not-expired,304,prefer-cache")
4102 << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << false;
4103 QTest::newRow(dataTag: "not-expired,304,always-cache")
4104 << reply304 << "Not-reloaded" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << true << false;
4105
4106 //
4107 // Set must-revalidate now
4108 //
4109 rawHeaders.clear();
4110 rawHeaders << QNetworkCacheMetaData::RawHeader("Date", QLocale::c().toString(dateTime: past, format: dateFormat).toLatin1())
4111 << QNetworkCacheMetaData::RawHeader("Cache-control", "max-age=7200, must-revalidate"); // must-revalidate is used
4112 content.first.setRawHeaders(rawHeaders);
4113
4114 QTest::newRow(dataTag: "must-revalidate,200,always-network")
4115 << reply200 << "Reloaded" << content << int(QNetworkRequest::AlwaysNetwork) << QStringList() << false << true;
4116 QTest::newRow(dataTag: "must-revalidate,200,prefer-network")
4117 << reply200 << "Reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << false << true;
4118 QTest::newRow(dataTag: "must-revalidate,200,prefer-cache")
4119 << reply200 << "Reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << false << true;
4120 QTest::newRow(dataTag: "must-revalidate,200,always-cache")
4121 << reply200 << "" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << false << false;
4122
4123 QTest::newRow(dataTag: "must-revalidate,304,prefer-network")
4124 << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << true << true;
4125 QTest::newRow(dataTag: "must-revalidate,304,prefer-cache")
4126 << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << true;
4127 QTest::newRow(dataTag: "must-revalidate,304,always-cache")
4128 << reply304 << "" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << false << false;
4129
4130 //
4131 // Partial content
4132 //
4133 rawHeaders.clear();
4134 rawHeaders << QNetworkCacheMetaData::RawHeader("Date", QLocale::c().toString(dateTime: past, format: dateFormat).toLatin1())
4135 << QNetworkCacheMetaData::RawHeader("Cache-control", "max-age=7200"); // isn't used in cache loading
4136 content.first.setRawHeaders(rawHeaders);
4137 content.first.setExpirationDate(future);
4138
4139 QByteArray reply206 =
4140 "HTTP/1.0 206\r\n"
4141 "Connection: keep-alive\r\n"
4142 "Content-Type: text/plain\r\n"
4143 "Cache-control: no-cache\r\n"
4144 "Content-Range: bytes 2-6/8\r\n"
4145 "Content-length: 4\r\n"
4146 "\r\n"
4147 "load";
4148
4149 QTest::newRow(dataTag: "partial,dontuse-cache")
4150 << reply206 << "load" << content << int(QNetworkRequest::PreferCache) << (QStringList() << "Range" << "bytes=2-6") << false << true;
4151}
4152
4153void tst_QNetworkReply::ioGetFromHttpWithCache()
4154{
4155 QFETCH(QByteArray, dataToSend);
4156 MiniHttpServer server(dataToSend);
4157 server.doClose = false;
4158
4159 MyMemoryCache *memoryCache = new MyMemoryCache(&manager);
4160 manager.setCache(memoryCache);
4161
4162 QFETCH(MyMemoryCache::CachedContent, cachedReply);
4163 QUrl url = "http://localhost:" + QString::number(server.serverPort());
4164 cachedReply.first.setUrl(url);
4165 if (!cachedReply.second.isNull())
4166 memoryCache->cache.insert(key: url.toEncoded(), value: cachedReply);
4167
4168 QFETCH(int, cacheMode);
4169 QNetworkRequest request(url);
4170 request.setAttribute(code: QNetworkRequest::CacheLoadControlAttribute, value: cacheMode);
4171 request.setAttribute(code: QNetworkRequest::CacheSaveControlAttribute, value: false);
4172
4173 QFETCH(QStringList, extraHttpHeaders);
4174 QVERIFY(extraHttpHeaders.size() % 2 == 0);
4175 for (auto it = extraHttpHeaders.cbegin(), end = extraHttpHeaders.cend(); it != end; /*double-stepping*/) {
4176 QString header = *it++;
4177 QString value = *it++;
4178 request.setRawHeader(headerName: header.toLatin1(), value: value.toLatin1()); // To latin1? Deal with it!
4179 }
4180
4181 QNetworkReplyPtr reply(manager.get(request));
4182
4183 QVERIFY(waitForFinish(reply) != Timeout);
4184
4185 QTEST(reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(), "loadedFromCache");
4186 QTEST(server.totalConnections > 0, "networkUsed");
4187 QFETCH(QString, body);
4188 QCOMPARE(reply->readAll().constData(), qPrintable(body));
4189}
4190
4191#ifndef QT_NO_NETWORKPROXY
4192void tst_QNetworkReply::ioGetWithManyProxies_data()
4193{
4194 QTest::addColumn<QList<QNetworkProxy> >(name: "proxyList");
4195 QTest::addColumn<QNetworkProxy>(name: "proxyUsed");
4196 QTest::addColumn<QString>(name: "url");
4197 QTest::addColumn<QNetworkReply::NetworkError>(name: "expectedError");
4198
4199 QList<QNetworkProxy> proxyList;
4200
4201 // All of the other functions test DefaultProxy
4202 // So let's test something else
4203
4204 // Simple tests that work:
4205
4206 // HTTP request with HTTP caching proxy
4207 proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129);
4208 QTest::newRow(dataTag: "http-on-http")
4209 << proxyList << proxyList.at(i: 0)
4210 << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
4211 << QNetworkReply::NoError;
4212
4213 // HTTP request with HTTP transparent proxy
4214 proxyList.clear();
4215 proxyList << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::httpProxyServerName(), 3129);
4216 QTest::newRow(dataTag: "http-on-http2")
4217 << proxyList << proxyList.at(i: 0)
4218 << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
4219 << QNetworkReply::NoError;
4220
4221 // HTTP request with SOCKS transparent proxy
4222 proxyList.clear();
4223 proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1081);
4224 QTest::newRow(dataTag: "http-on-socks")
4225 << proxyList << proxyList.at(i: 0)
4226 << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
4227 << QNetworkReply::NoError;
4228
4229 // FTP request with FTP caching proxy
4230 proxyList.clear();
4231 proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121);
4232 QTest::newRow(dataTag: "ftp-on-ftp")
4233 << proxyList << proxyList.at(i: 0)
4234 << "ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/rfc3252.txt"
4235 << QNetworkReply::NoError;
4236
4237 // The following test doesn't work because QFtp is too limited
4238 // It can only talk to its own kind of proxies
4239
4240 // FTP request with SOCKSv5 transparent proxy
4241 proxyList.clear();
4242 proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1081);
4243 QTest::newRow(dataTag: "ftp-on-socks")
4244 << proxyList << proxyList.at(i: 0)
4245 << "ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/rfc3252.txt"
4246 << QNetworkReply::NoError;
4247
4248#ifndef QT_NO_SSL
4249 // HTTPS with HTTP transparent proxy
4250 proxyList.clear();
4251 proxyList << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::httpProxyServerName(), 3129);
4252 QTest::newRow(dataTag: "https-on-http")
4253 << proxyList << proxyList.at(i: 0)
4254 << "https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
4255 << QNetworkReply::NoError;
4256
4257 // HTTPS request with SOCKS transparent proxy
4258 proxyList.clear();
4259 proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1081);
4260 QTest::newRow(dataTag: "https-on-socks")
4261 << proxyList << proxyList.at(i: 0)
4262 << "https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
4263 << QNetworkReply::NoError;
4264#endif
4265
4266 // Tests that fail:
4267
4268 // HTTP request with FTP caching proxy
4269 proxyList.clear();
4270 proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121);
4271 QTest::newRow(dataTag: "http-on-ftp")
4272 << proxyList << QNetworkProxy()
4273 << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
4274 << QNetworkReply::ProxyNotFoundError;
4275
4276 // FTP request with HTTP caching proxy
4277 proxyList.clear();
4278 proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129);
4279 QTest::newRow(dataTag: "ftp-on-http")
4280 << proxyList << QNetworkProxy()
4281 << "ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/rfc3252.txt"
4282 << QNetworkReply::ProxyNotFoundError;
4283
4284 // FTP request with HTTP caching proxies
4285 proxyList.clear();
4286 proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129)
4287 << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3130);
4288 QTest::newRow(dataTag: "ftp-on-multiple-http")
4289 << proxyList << QNetworkProxy()
4290 << "ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/rfc3252.txt"
4291 << QNetworkReply::ProxyNotFoundError;
4292
4293#ifndef QT_NO_SSL
4294 // HTTPS with HTTP caching proxy
4295 proxyList.clear();
4296 proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129);
4297 QTest::newRow(dataTag: "https-on-httptransparent")
4298 << proxyList << QNetworkProxy()
4299 << "https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
4300 << QNetworkReply::ProxyNotFoundError;
4301
4302 // HTTPS with FTP caching proxy
4303 proxyList.clear();
4304 proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121);
4305 QTest::newRow(dataTag: "https-on-ftp")
4306 << proxyList << QNetworkProxy()
4307 << "https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
4308 << QNetworkReply::ProxyNotFoundError;
4309#endif
4310
4311 // Complex requests:
4312
4313 // HTTP request with more than one HTTP proxy
4314 proxyList.clear();
4315 proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129)
4316 << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3130);
4317 QTest::newRow(dataTag: "http-on-multiple-http")
4318 << proxyList << proxyList.at(i: 0)
4319 << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
4320 << QNetworkReply::NoError;
4321
4322 // HTTP request with HTTP + SOCKS
4323 proxyList.clear();
4324 proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129)
4325 << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1081);
4326 QTest::newRow(dataTag: "http-on-http+socks")
4327 << proxyList << proxyList.at(i: 0)
4328 << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
4329 << QNetworkReply::NoError;
4330
4331 // HTTP request with FTP + HTTP + SOCKS
4332 proxyList.clear();
4333 proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121)
4334 << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129)
4335 << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1081);
4336 QTest::newRow(dataTag: "http-on-ftp+http+socks")
4337 << proxyList << proxyList.at(i: 1) // second proxy should be used
4338 << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
4339 << QNetworkReply::NoError;
4340
4341 // HTTP request with NoProxy + HTTP
4342 proxyList.clear();
4343 proxyList << QNetworkProxy(QNetworkProxy::NoProxy)
4344 << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129);
4345 QTest::newRow(dataTag: "http-on-noproxy+http")
4346 << proxyList << proxyList.at(i: 0)
4347 << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
4348 << QNetworkReply::NoError;
4349
4350 // HTTP request with FTP + NoProxy
4351 proxyList.clear();
4352 proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121)
4353 << QNetworkProxy(QNetworkProxy::NoProxy);
4354 QTest::newRow(dataTag: "http-on-ftp+noproxy")
4355 << proxyList << proxyList.at(i: 1) // second proxy should be used
4356 << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
4357 << QNetworkReply::NoError;
4358
4359 // FTP request with HTTP Caching + FTP
4360 proxyList.clear();
4361 proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129)
4362 << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121);
4363 QTest::newRow(dataTag: "ftp-on-http+ftp")
4364 << proxyList << proxyList.at(i: 1) // second proxy should be used
4365 << "ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/rfc3252.txt"
4366 << QNetworkReply::NoError;
4367
4368#ifndef QT_NO_SSL
4369 // HTTPS request with HTTP Caching + HTTP transparent
4370 proxyList.clear();
4371 proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129)
4372 << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::httpProxyServerName(), 3129);
4373 QTest::newRow(dataTag: "https-on-httpcaching+http")
4374 << proxyList << proxyList.at(i: 1) // second proxy should be used
4375 << "https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
4376 << QNetworkReply::NoError;
4377
4378 // HTTPS request with FTP + HTTP C + HTTP T
4379 proxyList.clear();
4380 proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121)
4381 << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129)
4382 << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::httpProxyServerName(), 3129);
4383 QTest::newRow(dataTag: "https-on-ftp+httpcaching+http")
4384 << proxyList << proxyList.at(i: 2) // skip the first two
4385 << "https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
4386 << QNetworkReply::NoError;
4387#endif
4388}
4389
4390void tst_QNetworkReply::ioGetWithManyProxies()
4391{
4392 // Test proxy factories
4393
4394 QFile reference(testDataDir + "/rfc3252.txt");
4395 QVERIFY(reference.open(QIODevice::ReadOnly));
4396
4397 // set the proxy factory:
4398 QFETCH(QList<QNetworkProxy>, proxyList);
4399 MyProxyFactory *proxyFactory = new MyProxyFactory;
4400 proxyFactory->toReturn = proxyList;
4401 manager.setProxyFactory(proxyFactory);
4402
4403 QFETCH(QString, url);
4404 QUrl theUrl(url);
4405 QNetworkRequest request(theUrl);
4406 QNetworkReplyPtr reply(manager.get(request));
4407 DataReader reader(reply);
4408
4409 QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
4410 connect(asender: &manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
4411 SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
4412#ifndef QT_NO_SSL
4413 connect(asender: &manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
4414 SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
4415#endif
4416
4417 QVERIFY(waitForFinish(reply) != Timeout);
4418
4419 manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
4420 receiver: this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
4421#ifndef QT_NO_SSL
4422 manager.disconnect(SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
4423 receiver: this, SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
4424#endif
4425
4426 QFETCH(QNetworkReply::NetworkError, expectedError);
4427 QEXPECT_FAIL("ftp-on-socks", "QFtp is too limited and won't accept non-FTP proxies", Abort);
4428 QCOMPARE(reply->error(), expectedError);
4429
4430 // Verify that the factory was called properly
4431 QCOMPARE(proxyFactory->callCount, 1);
4432 QCOMPARE(proxyFactory->lastQuery, QNetworkProxyQuery(theUrl));
4433
4434 if (expectedError == QNetworkReply::NoError) {
4435 // request succeeded
4436 QCOMPARE(reader.data, reference.readAll());
4437
4438 // now verify that the proxies worked:
4439 QFETCH(QNetworkProxy, proxyUsed);
4440 if (proxyUsed.type() == QNetworkProxy::NoProxy) {
4441 QCOMPARE(authspy.count(), 0);
4442 } else {
4443 if (QByteArray(QTest::currentDataTag()).startsWith(c: "ftp-"))
4444 return; // No authentication with current FTP or with FTP proxies
4445 QCOMPARE(authspy.count(), 1);
4446 QCOMPARE(qvariant_cast<QNetworkProxy>(authspy.at(0).at(0)), proxyUsed);
4447 }
4448 } else {
4449 // request failed
4450 QCOMPARE(authspy.count(), 0);
4451 }
4452}
4453#endif // !QT_NO_NETWORKPROXY
4454
4455void tst_QNetworkReply::ioPutToFileFromFile_data()
4456{
4457 QTest::addColumn<QString>(name: "fileName");
4458
4459 QTest::newRow(dataTag: "empty") << (testDataDir + "/empty");
4460 QTest::newRow(dataTag: "real-file") << (testDataDir + "/rfc3252.txt");
4461 QTest::newRow(dataTag: "resource") << ":/resource";
4462 QTest::newRow(dataTag: "search-path") << "testdata:/rfc3252.txt";
4463}
4464
4465void tst_QNetworkReply::ioPutToFileFromFile()
4466{
4467 QFETCH(QString, fileName);
4468 QFile sourceFile(fileName);
4469 QFile targetFile(testFileName);
4470
4471 QVERIFY(sourceFile.open(QIODevice::ReadOnly));
4472
4473 QUrl url = QUrl::fromLocalFile(localfile: targetFile.fileName());
4474 QNetworkRequest request(url);
4475 QNetworkReplyPtr reply(manager.put(request, data: &sourceFile));
4476
4477 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
4478
4479 QCOMPARE(reply->url(), url);
4480 QCOMPARE(reply->error(), QNetworkReply::NoError);
4481 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
4482 QVERIFY(reply->readAll().isEmpty());
4483
4484 QVERIFY(sourceFile.atEnd());
4485 sourceFile.seek(offset: 0); // reset it to the beginning
4486
4487 QVERIFY(targetFile.open(QIODevice::ReadOnly));
4488 QCOMPARE(targetFile.size(), sourceFile.size());
4489 QCOMPARE(targetFile.readAll(), sourceFile.readAll());
4490}
4491
4492void tst_QNetworkReply::ioPutToFileFromSocket_data()
4493{
4494 putToFile_data();
4495}
4496
4497void tst_QNetworkReply::ioPutToFileFromSocket()
4498{
4499 QFile file(testFileName);
4500
4501 QUrl url = QUrl::fromLocalFile(localfile: file.fileName());
4502 QNetworkRequest request(url);
4503
4504 QFETCH(QByteArray, data);
4505 SocketPair socketpair;
4506 QTRY_VERIFY(socketpair.create()); //QTRY_VERIFY as a workaround for QTBUG-24451
4507
4508 socketpair.endPoints[0]->write(data);
4509 QNetworkReplyPtr reply(manager.put(request: QNetworkRequest(url), data: socketpair.endPoints[1]));
4510 socketpair.endPoints[0]->close();
4511
4512 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
4513 QCOMPARE(reply->error(), QNetworkReply::NoError);
4514
4515 QCOMPARE(reply->url(), url);
4516 QCOMPARE(reply->error(), QNetworkReply::NoError);
4517 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
4518 QVERIFY(reply->readAll().isEmpty());
4519
4520 QVERIFY(file.open(QIODevice::ReadOnly));
4521 QCOMPARE(file.size(), qint64(data.size()));
4522 QByteArray contents = file.readAll();
4523 QCOMPARE(contents, data);
4524}
4525
4526void tst_QNetworkReply::ioPutToFileFromLocalSocket_data()
4527{
4528 putToFile_data();
4529}
4530
4531void tst_QNetworkReply::ioPutToFileFromLocalSocket()
4532{
4533 QString socketname = "networkreplytest";
4534 QLocalServer server;
4535 if (!server.listen(name: socketname)) {
4536 QLocalServer::removeServer(name: socketname);
4537 QVERIFY(server.listen(socketname));
4538 }
4539 QLocalSocket active;
4540 active.connectToServer(name: socketname);
4541 QVERIFY2(server.waitForNewConnection(10), server.errorString().toLatin1().constData());
4542 QVERIFY2(active.waitForConnected(10), active.errorString().toLatin1().constData());
4543 QVERIFY2(server.hasPendingConnections(), server.errorString().toLatin1().constData());
4544 QLocalSocket *passive = server.nextPendingConnection();
4545
4546 QFile file(testFileName);
4547 QUrl url = QUrl::fromLocalFile(localfile: file.fileName());
4548 QNetworkRequest request(url);
4549
4550 QFETCH(QByteArray, data);
4551 active.write(data);
4552 active.close();
4553 QNetworkReplyPtr reply(manager.put(request: QNetworkRequest(url), data: passive));
4554 passive->setParent(reply.data());
4555
4556#ifdef Q_OS_WIN
4557 if (!data.isEmpty())
4558 QEXPECT_FAIL("", "QTBUG-18385", Abort);
4559#endif
4560 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
4561 QCOMPARE(reply->error(), QNetworkReply::NoError);
4562
4563 QCOMPARE(reply->url(), url);
4564 QCOMPARE(reply->error(), QNetworkReply::NoError);
4565 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
4566 QVERIFY(reply->readAll().isEmpty());
4567
4568 QVERIFY(file.open(QIODevice::ReadOnly));
4569 QCOMPARE(file.size(), qint64(data.size()));
4570 QByteArray contents = file.readAll();
4571 QCOMPARE(contents, data);
4572}
4573
4574// Currently no stdin/out supported for Windows CE.
4575void tst_QNetworkReply::ioPutToFileFromProcess_data()
4576{
4577#if QT_CONFIG(process)
4578 putToFile_data();
4579#endif
4580}
4581
4582void tst_QNetworkReply::ioPutToFileFromProcess()
4583{
4584#if !QT_CONFIG(process)
4585 QSKIP("No qprocess support", SkipAll);
4586#else
4587
4588#ifdef Q_OS_WIN
4589 if (qstrcmp(QTest::currentDataTag(), "small") == 0)
4590 QSKIP("When passing a CR-LF-LF sequence through Windows stdio, it gets converted, "
4591 "so this test fails. Disabled on Windows");
4592#endif
4593
4594 QFile file(testFileName);
4595
4596 QUrl url = QUrl::fromLocalFile(localfile: file.fileName());
4597 QNetworkRequest request(url);
4598
4599 QFETCH(QByteArray, data);
4600 QProcess process;
4601 QString echoExe = echoProcessDir + "/echo";
4602 process.start(program: echoExe, arguments: QStringList("all"));
4603 QVERIFY2(process.waitForStarted(), qPrintable(
4604 QString::fromLatin1("Could not start %1: %2").arg(echoExe, process.errorString())));
4605 process.write(data);
4606 process.closeWriteChannel();
4607
4608 QNetworkReplyPtr reply(manager.put(request: QNetworkRequest(url), data: &process));
4609
4610 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
4611
4612 QCOMPARE(reply->url(), url);
4613 QCOMPARE(reply->error(), QNetworkReply::NoError);
4614 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
4615 QVERIFY(reply->readAll().isEmpty());
4616
4617 QVERIFY(file.open(QIODevice::ReadOnly));
4618 QCOMPARE(file.size(), qint64(data.size()));
4619 QByteArray contents = file.readAll();
4620 QCOMPARE(contents, data);
4621
4622#endif // QT_CONFIG(process)
4623}
4624
4625void tst_QNetworkReply::ioPutToFtpFromFile_data()
4626{
4627 ioPutToFileFromFile_data();
4628}
4629
4630void tst_QNetworkReply::ioPutToFtpFromFile()
4631{
4632 QFETCH(QString, fileName);
4633 QFile sourceFile(fileName);
4634 QVERIFY(sourceFile.open(QIODevice::ReadOnly));
4635
4636 QUrl url("ftp://" + QtNetworkSettings::ftpServerName());
4637 url.setPath(path: QString("/qtest/upload/qnetworkaccess-ioPutToFtpFromFile-%1-%2")
4638 .arg(a: QTest::currentDataTag())
4639 .arg(a: uniqueExtension));
4640
4641 QNetworkRequest request(url);
4642 QNetworkReplyPtr reply(manager.put(request, data: &sourceFile));
4643
4644 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
4645
4646 QCOMPARE(reply->url(), url);
4647 QCOMPARE(reply->error(), QNetworkReply::NoError);
4648 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
4649 QVERIFY(reply->readAll().isEmpty());
4650
4651 QVERIFY(sourceFile.atEnd());
4652 sourceFile.seek(offset: 0); // reset it to the beginning
4653
4654 // download the file again from FTP to make sure it was uploaded
4655 // correctly
4656 QNetworkAccessManager qnam;
4657 QNetworkRequest req(url);
4658 QNetworkReply *r = qnam.get(request: req);
4659
4660 QObject::connect(sender: r, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
4661 QTestEventLoop::instance().enterLoop(secs: 3);
4662 QObject::disconnect(sender: r, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
4663
4664 QByteArray uploaded = r->readAll();
4665 QCOMPARE(qint64(uploaded.size()), sourceFile.size());
4666 QCOMPARE(uploaded, sourceFile.readAll());
4667
4668 r->close();
4669 QObject::connect(sender: r, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
4670 QTestEventLoop::instance().enterLoop(secs: 10);
4671 QObject::disconnect(sender: r, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
4672}
4673
4674void tst_QNetworkReply::ioPutToHttpFromFile_data()
4675{
4676 ioPutToFileFromFile_data();
4677}
4678
4679void tst_QNetworkReply::ioPutToHttpFromFile()
4680{
4681 QFETCH(QString, fileName);
4682 QFile sourceFile(fileName);
4683 QVERIFY(sourceFile.open(QIODevice::ReadOnly));
4684
4685 QUrl url("http://" + QtNetworkSettings::httpServerName());
4686 url.setPath(path: QString("/dav/qnetworkaccess-ioPutToHttpFromFile-%1-%2")
4687 .arg(a: QTest::currentDataTag())
4688 .arg(a: uniqueExtension));
4689
4690 QNetworkRequest request(url);
4691 QNetworkReplyPtr reply(manager.put(request, data: &sourceFile));
4692
4693 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
4694
4695 QCOMPARE(reply->url(), url);
4696 QCOMPARE(reply->error(), QNetworkReply::NoError);
4697
4698 // verify that the HTTP status code is 201 Created
4699 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 201);
4700
4701 QVERIFY(sourceFile.atEnd());
4702 sourceFile.seek(offset: 0); // reset it to the beginning
4703
4704 // download the file again from HTTP to make sure it was uploaded
4705 // correctly
4706 reply.reset(t: manager.get(request));
4707
4708 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
4709
4710 QCOMPARE(reply->url(), url);
4711 QCOMPARE(reply->error(), QNetworkReply::NoError);
4712 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
4713
4714 QCOMPARE(reply->readAll(), sourceFile.readAll());
4715}
4716
4717void tst_QNetworkReply::ioPostToHttpFromFile_data()
4718{
4719 ioPutToFileFromFile_data();
4720}
4721
4722void tst_QNetworkReply::ioPostToHttpFromFile()
4723{
4724 QFETCH(QString, fileName);
4725 QFile sourceFile(fileName);
4726 QVERIFY(sourceFile.open(QIODevice::ReadOnly));
4727
4728 QUrl url("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/md5sum.cgi");
4729 QNetworkRequest request(url);
4730 request.setRawHeader(headerName: "Content-Type", value: "application/octet-stream");
4731
4732 QNetworkReplyPtr reply(manager.post(request, data: &sourceFile));
4733
4734 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
4735
4736 QCOMPARE(reply->url(), url);
4737 QCOMPARE(reply->error(), QNetworkReply::NoError);
4738
4739 // verify that the HTTP status code is 200 Ok
4740 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
4741
4742 QVERIFY(sourceFile.atEnd());
4743 sourceFile.seek(offset: 0); // reset it to the beginning
4744
4745 QCOMPARE(reply->readAll().trimmed(), md5sum(sourceFile.readAll()).toHex());
4746}
4747
4748#ifndef QT_NO_NETWORKPROXY
4749void tst_QNetworkReply::ioPostToHttpFromSocket_data()
4750{
4751 QTest::addColumn<QByteArray>(name: "data");
4752 QTest::addColumn<QByteArray>(name: "md5sum");
4753 QTest::addColumn<QUrl>(name: "url");
4754 QTest::addColumn<QNetworkProxy>(name: "proxy");
4755 QTest::addColumn<int>(name: "authenticationRequiredCount");
4756 QTest::addColumn<int>(name: "proxyAuthenticationRequiredCount");
4757
4758 for (int i = 0; i < proxies.count(); ++i)
4759 for (int auth = 0; auth < 2; ++auth) {
4760 QUrl url;
4761 if (auth)
4762 url = "http://" + QtNetworkSettings::httpServerName() + "/qtest/protected/cgi-bin/md5sum.cgi";
4763 else
4764 url = "http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/md5sum.cgi";
4765
4766 QNetworkProxy proxy = proxies.at(i).proxy;
4767 QByteArray testsuffix = QByteArray(auth ? "+auth" : "") + proxies.at(i).tag;
4768 int proxyauthcount = proxies.at(i).requiresAuthentication;
4769
4770 QByteArray data;
4771 data = "";
4772 QTest::newRow(dataTag: "empty" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount;
4773
4774 data = "This is a normal message.";
4775 QTest::newRow(dataTag: "generic" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount;
4776
4777 data = "This is a message to show that Qt rocks!\r\n\n";
4778 QTest::newRow(dataTag: "small" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount;
4779
4780 data = QByteArray("abcd\0\1\2\abcd",12);
4781 QTest::newRow(dataTag: "with-nul" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount;
4782
4783 data = QByteArray(4097, '\4');
4784 QTest::newRow(dataTag: "4k+1" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount;
4785
4786 data = QByteArray(128*1024+1, '\177');
4787 QTest::newRow(dataTag: "128k+1" + testsuffix) << data << md5sum(data) << url << proxy << auth << proxyauthcount;
4788 }
4789}
4790
4791void tst_QNetworkReply::ioPostToHttpFromSocket()
4792{
4793 if (QTest::currentDataTag() == QByteArray("128k+1+proxyauth")
4794 || QTest::currentDataTag() == QByteArray("128k+1+auth+proxyauth"))
4795 QSKIP("Squid cannot handle authentication with POST data >= 64K (QTBUG-33180)");
4796
4797 QFETCH(QByteArray, data);
4798 QFETCH(QUrl, url);
4799 QFETCH(QNetworkProxy, proxy);
4800
4801 SocketPair socketpair;
4802 QTRY_VERIFY(socketpair.create()); //QTRY_VERIFY as a workaround for QTBUG-24451
4803
4804 socketpair.endPoints[0]->write(data);
4805
4806 QNetworkRequest request(url);
4807 request.setRawHeader(headerName: "Content-Type", value: "application/octet-stream");
4808
4809 manager.setProxy(proxy);
4810 QNetworkReplyPtr reply(manager.post(request, data: socketpair.endPoints[1]));
4811 socketpair.endPoints[0]->close();
4812
4813 connect(asender: &manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
4814 SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
4815 connect(asender: &manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
4816 SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4817
4818 QSignalSpy authenticationRequiredSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4819 QSignalSpy proxyAuthenticationRequiredSpy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
4820
4821 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
4822
4823 disconnect(sender: &manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
4824 receiver: this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
4825 disconnect(sender: &manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
4826 receiver: this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4827 QCOMPARE(reply->error(), QNetworkReply::NoError);
4828
4829 QCOMPARE(reply->url(), url);
4830 QCOMPARE(reply->error(), QNetworkReply::NoError);
4831 // verify that the HTTP status code is 200 Ok
4832 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
4833
4834 QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
4835
4836 QTEST(authenticationRequiredSpy.count(), "authenticationRequiredCount");
4837 QTEST(proxyAuthenticationRequiredSpy.count(), "proxyAuthenticationRequiredCount");
4838}
4839
4840void tst_QNetworkReply::ioPostToHttpFromSocketSynchronous_data()
4841{
4842 QTest::addColumn<QByteArray>(name: "data");
4843 QTest::addColumn<QByteArray>(name: "md5sum");
4844
4845 QByteArray data;
4846 data = "";
4847 QTest::newRow(dataTag: "empty") << data << md5sum(data);
4848
4849 data = "This is a normal message.";
4850 QTest::newRow(dataTag: "generic") << data << md5sum(data);
4851
4852 data = "This is a message to show that Qt rocks!\r\n\n";
4853 QTest::newRow(dataTag: "small") << data << md5sum(data);
4854
4855 data = QByteArray("abcd\0\1\2\abcd",12);
4856 QTest::newRow(dataTag: "with-nul") << data << md5sum(data);
4857
4858 data = QByteArray(4097, '\4');
4859 QTest::newRow(dataTag: "4k+1") << data << md5sum(data);
4860
4861 data = QByteArray(128*1024+1, '\177');
4862 QTest::newRow(dataTag: "128k+1") << data << md5sum(data);
4863
4864 data = QByteArray(2*1024*1024+1, '\177');
4865 QTest::newRow(dataTag: "2MB+1") << data << md5sum(data);
4866}
4867
4868void tst_QNetworkReply::ioPostToHttpFromSocketSynchronous()
4869{
4870 QFETCH(QByteArray, data);
4871
4872 SocketPair socketpair;
4873 QTRY_VERIFY(socketpair.create()); //QTRY_VERIFY as a workaround for QTBUG-24451
4874 socketpair.endPoints[0]->write(data);
4875 socketpair.endPoints[0]->waitForBytesWritten(msecs: 5000);
4876 // ### for 4.8: make the socket pair unbuffered, to not read everything in one go in QNetworkReplyImplPrivate::setup()
4877 QTestEventLoop::instance().enterLoop(secs: 3);
4878
4879 QUrl url("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/md5sum.cgi");
4880 QNetworkRequest request(url);
4881 request.setRawHeader(headerName: "Content-Type", value: "application/octet-stream");
4882 request.setAttribute(
4883 code: QNetworkRequest::SynchronousRequestAttribute,
4884 value: true);
4885
4886 QNetworkReplyPtr reply(manager.post(request, data: socketpair.endPoints[1]));
4887 QVERIFY(reply->isFinished());
4888 socketpair.endPoints[0]->close();
4889
4890 QCOMPARE(reply->error(), QNetworkReply::NoError);
4891
4892 QCOMPARE(reply->url(), url);
4893 QCOMPARE(reply->error(), QNetworkReply::NoError);
4894 // verify that the HTTP status code is 200 Ok
4895 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
4896
4897 QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
4898}
4899#endif // !QT_NO_NETWORKPROXY
4900
4901// this tests checks if rewinding the POST-data to some place in the middle
4902// worked.
4903void tst_QNetworkReply::ioPostToHttpFromMiddleOfFileToEnd()
4904{
4905 QFile sourceFile(testDataDir + "/rfc3252.txt");
4906 QVERIFY(sourceFile.open(QIODevice::ReadOnly));
4907 // seeking to the middle
4908 sourceFile.seek(offset: sourceFile.size() / 2);
4909
4910 QUrl url = "http://" + QtNetworkSettings::httpServerName() + "/qtest/protected/cgi-bin/md5sum.cgi";
4911 QNetworkRequest request(url);
4912 request.setRawHeader(headerName: "Content-Type", value: "application/octet-stream");
4913 QNetworkReplyPtr reply(manager.post(request, data: &sourceFile));
4914
4915 connect(asender: &manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
4916 SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4917
4918 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
4919
4920 disconnect(sender: &manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
4921 receiver: this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4922
4923 // compare half data
4924 sourceFile.seek(offset: sourceFile.size() / 2);
4925 QByteArray data = sourceFile.readAll();
4926 QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
4927}
4928
4929void tst_QNetworkReply::ioPostToHttpFromMiddleOfFileFiveBytes()
4930{
4931 QFile sourceFile(testDataDir + "/rfc3252.txt");
4932 QVERIFY(sourceFile.open(QIODevice::ReadOnly));
4933 // seeking to the middle
4934 sourceFile.seek(offset: sourceFile.size() / 2);
4935
4936 QUrl url = "http://" + QtNetworkSettings::httpServerName() + "/qtest/protected/cgi-bin/md5sum.cgi";
4937 QNetworkRequest request(url);
4938 request.setRawHeader(headerName: "Content-Type", value: "application/octet-stream");
4939 // only send 5 bytes
4940 request.setHeader(header: QNetworkRequest::ContentLengthHeader, value: 5);
4941 QVERIFY(request.header(QNetworkRequest::ContentLengthHeader).isValid());
4942 QNetworkReplyPtr reply(manager.post(request, data: &sourceFile));
4943
4944 connect(asender: &manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
4945 SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4946
4947 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
4948
4949 disconnect(sender: &manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
4950 receiver: this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4951
4952 // compare half data
4953 sourceFile.seek(offset: sourceFile.size() / 2);
4954 QByteArray data = sourceFile.read(maxlen: 5);
4955 QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
4956}
4957
4958void tst_QNetworkReply::ioPostToHttpFromMiddleOfQBufferFiveBytes()
4959{
4960 // test needed since a QBuffer goes with a different codepath than the QFile
4961 // tested in ioPostToHttpFromMiddleOfFileFiveBytes
4962 QBuffer uploadBuffer;
4963 uploadBuffer.open(openMode: QIODevice::ReadWrite);
4964 uploadBuffer.write(data: "1234567890");
4965 uploadBuffer.seek(off: 5);
4966
4967 QUrl url = "http://" + QtNetworkSettings::httpServerName() + "/qtest/protected/cgi-bin/md5sum.cgi";
4968 QNetworkRequest request(url);
4969 request.setRawHeader(headerName: "Content-Type", value: "application/octet-stream");
4970 QNetworkReplyPtr reply(manager.post(request, data: &uploadBuffer));
4971
4972 connect(asender: &manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
4973 SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4974
4975 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
4976
4977 disconnect(sender: &manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
4978 receiver: this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
4979
4980 // compare half data
4981 uploadBuffer.seek(off: 5);
4982 QByteArray data = uploadBuffer.read(maxlen: 5);
4983 QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
4984}
4985
4986
4987void tst_QNetworkReply::ioPostToHttpNoBufferFlag()
4988{
4989 QByteArray data = QByteArray("daaaaaaataaaaaaa");
4990 // create a sequential QIODevice by feeding the data into a local TCP server
4991 SocketPair socketpair;
4992 QTRY_VERIFY(socketpair.create()); //QTRY_VERIFY as a workaround for QTBUG-24451
4993 socketpair.endPoints[0]->write(data);
4994
4995 QUrl url = "http://" + QtNetworkSettings::httpServerName() + "/qtest/protected/cgi-bin/md5sum.cgi";
4996 QNetworkRequest request(url);
4997 request.setRawHeader(headerName: "Content-Type", value: "application/octet-stream");
4998 // disallow buffering
4999 request.setAttribute(code: QNetworkRequest::DoNotBufferUploadDataAttribute, value: true);
5000 request.setHeader(header: QNetworkRequest::ContentLengthHeader, value: data.size());
5001 QNetworkReplyPtr reply(manager.post(request, data: socketpair.endPoints[1]));
5002 socketpair.endPoints[0]->close();
5003
5004 connect(asender: &manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
5005 SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
5006
5007 QCOMPARE(waitForFinish(reply), int(Failure));
5008
5009 disconnect(sender: &manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
5010 receiver: this, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
5011
5012 // verify: error code is QNetworkReply::ContentReSendError
5013 QCOMPARE(reply->error(), QNetworkReply::ContentReSendError);
5014}
5015
5016#ifndef QT_NO_SSL
5017class SslServer : public QTcpServer
5018{
5019 Q_OBJECT
5020public:
5021 SslServer() : socket(0), m_ssl(true) {}
5022 void incomingConnection(qintptr socketDescriptor)
5023 {
5024 QSslSocket *serverSocket = new QSslSocket;
5025 serverSocket->setParent(this);
5026
5027 if (serverSocket->setSocketDescriptor(socketDescriptor)) {
5028 connect(sender: serverSocket, SIGNAL(readyRead()), receiver: this, SLOT(readyReadSlot()));
5029 if (!m_ssl) {
5030 emit newPlainConnection(s: serverSocket);
5031 return;
5032 }
5033 connect(sender: serverSocket, SIGNAL(encrypted()), receiver: this, SLOT(encryptedSlot()));
5034 connect(sender: serverSocket, SIGNAL(sslErrors(QList<QSslError>)), receiver: serverSocket, SLOT(ignoreSslErrors()));
5035 setupSslServer(serverSocket);
5036 } else {
5037 delete serverSocket;
5038 }
5039 }
5040signals:
5041 void newEncryptedConnection(QSslSocket *s);
5042 void newPlainConnection(QSslSocket *s);
5043public slots:
5044 void encryptedSlot()
5045 {
5046 socket = (QSslSocket*) sender();
5047 emit newEncryptedConnection(s: socket);
5048 }
5049 void readyReadSlot()
5050 {
5051 // for the incoming sockets, not the server socket
5052 //qDebug() << static_cast<QSslSocket*>(sender())->bytesAvailable() << static_cast<QSslSocket*>(sender())->encryptedBytesAvailable();
5053 }
5054
5055public:
5056 QSslSocket *socket;
5057 bool m_ssl;
5058};
5059
5060// very similar to ioPostToHttpUploadProgress but for SSL
5061void tst_QNetworkReply::ioPostToHttpsUploadProgress()
5062{
5063 //QFile sourceFile(testDataDir + "/bigfile");
5064 //QVERIFY(sourceFile.open(QIODevice::ReadOnly));
5065 qint64 wantedSize = 2*1024*1024; // 2 MB
5066 QByteArray sourceFile;
5067 // And in the case of SSL, the compression can fool us and let the
5068 // server send the data much faster than expected.
5069 // So better provide random data that cannot be compressed.
5070 for (int i = 0; i < wantedSize; ++i)
5071 sourceFile += (char)QRandomGenerator::global()->generate();
5072
5073 // emulate a minimal https server
5074 SslServer server;
5075 server.listen(address: QHostAddress(QHostAddress::LocalHost), port: 0);
5076
5077 // create the request
5078 QUrl url = QUrl(QLatin1String("https://127.0.0.1:") + QString::number(server.serverPort()) + QLatin1Char('/'));
5079 QNetworkRequest request(url);
5080
5081 request.setRawHeader(headerName: "Content-Type", value: "application/octet-stream");
5082 QNetworkReplyPtr reply(manager.post(request, data: sourceFile));
5083
5084 QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64)));
5085 connect(sender: &server, SIGNAL(newEncryptedConnection(QSslSocket*)), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
5086 connect(ptr: reply, SIGNAL(sslErrors(QList<QSslError>)), receiver: reply.data(), SLOT(ignoreSslErrors()));
5087
5088 // get the request started and the incoming socket connected
5089 QTestEventLoop::instance().enterLoop(secs: 10);
5090 QVERIFY(!QTestEventLoop::instance().timeout());
5091 QTcpSocket *incomingSocket = server.socket;
5092 QVERIFY(incomingSocket);
5093 disconnect(sender: &server, SIGNAL(newEncryptedConnection(QSslSocket*)), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
5094
5095
5096 incomingSocket->setReadBufferSize(1024);
5097 // some progress should have been made
5098 QTRY_VERIFY(!spy.isEmpty());
5099 QList<QVariant> args = spy.last();
5100 QVERIFY(args.at(0).toLongLong() > 0);
5101 // but not everything!
5102 QVERIFY(args.at(0).toLongLong() != sourceFile.size());
5103
5104 // set the read buffer to unlimited
5105 incomingSocket->setReadBufferSize(0);
5106 QTestEventLoop::instance().enterLoop(secs: 10);
5107 // progress should be finished
5108 QVERIFY(!spy.isEmpty());
5109 QList<QVariant> args3 = spy.last();
5110 QCOMPARE(args3.at(0).toLongLong(), args3.at(1).toLongLong());
5111 QCOMPARE(args3.at(0).toLongLong(), qint64(sourceFile.size()));
5112
5113 // after sending this, the QNAM should emit finished()
5114 incomingSocket->write(data: "HTTP/1.0 200 OK\r\n");
5115 incomingSocket->write(data: "Content-Length: 0\r\n");
5116 incomingSocket->write(data: "\r\n");
5117
5118 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
5119
5120 incomingSocket->close();
5121 server.close();
5122}
5123#endif
5124
5125void tst_QNetworkReply::ioGetFromBuiltinHttp_data()
5126{
5127 QTest::addColumn<bool>(name: "https");
5128 QTest::addColumn<int>(name: "bufferSize");
5129 QTest::newRow(dataTag: "http+unlimited") << false << 0;
5130 QTest::newRow(dataTag: "http+limited") << false << 4096;
5131#ifndef QT_NO_SSL
5132 QTest::newRow(dataTag: "https+unlimited") << true << 0;
5133 QTest::newRow(dataTag: "https+limited") << true << 4096;
5134#endif
5135}
5136
5137void tst_QNetworkReply::ioGetFromBuiltinHttp()
5138{
5139 QFETCH(bool, https);
5140 QFETCH(int, bufferSize);
5141
5142 QByteArray testData;
5143 // Make the data big enough so that it can fill the kernel buffer
5144 // (which seems to hold 202 KB here)
5145 const int wantedSize = 1200 * 1000;
5146 testData.reserve(asize: wantedSize);
5147 // And in the case of SSL, the compression can fool us and let the
5148 // server send the data much faster than expected.
5149 // So better provide random data that cannot be compressed.
5150 for (int i = 0; i < wantedSize; ++i)
5151 testData += (char)QRandomGenerator::global()->generate();
5152
5153 QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: ");
5154 httpResponse += QByteArray::number(testData.size());
5155 httpResponse += "\r\n\r\n";
5156 httpResponse += testData;
5157
5158 qDebug() << "Server will send" << (httpResponse.size()-testData.size()) << "bytes of header and"
5159 << testData.size() << "bytes of data";
5160
5161 const bool fillKernelBuffer = bufferSize > 0;
5162 notEnoughDataForFastSender = false;
5163 FastSender server(httpResponse, https, fillKernelBuffer, this);
5164
5165 QUrl url(QString("%1://127.0.0.1:%2/qtest/rfc3252.txt")
5166 .arg(a: https?"https":"http")
5167 .arg(a: server.serverPort()));
5168 QNetworkRequest request(url);
5169 QNetworkReplyPtr reply(manager.get(request));
5170 reply->setReadBufferSize(bufferSize);
5171 reply->ignoreSslErrors();
5172 const int rate = 200; // in kB per sec
5173 RateControlledReader reader(server, reply.data(), rate, bufferSize);
5174
5175 QElapsedTimer loopTimer;
5176 loopTimer.start();
5177
5178 const int result = waitForFinish(reply);
5179 if (notEnoughDataForFastSender) {
5180 server.wait();
5181 QSKIP("kernel socket buffers are too big for this test to work");
5182 }
5183
5184 QVERIFY2(result == Success, msgWaitForFinished(reply));
5185
5186 const int elapsedTime = loopTimer.elapsed();
5187 server.wait();
5188 reader.wrapUp();
5189
5190 qDebug() << "send rate:" << server.transferRate << "B/s";
5191 qDebug() << "receive rate:" << reader.totalBytesRead * 1000 / elapsedTime
5192 << "(it received" << reader.totalBytesRead << "bytes in" << elapsedTime << "ms)";
5193
5194 QCOMPARE(reply->url(), request.url());
5195 QCOMPARE(reply->error(), QNetworkReply::NoError);
5196 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
5197
5198 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), (qint64)testData.size());
5199 if (reader.data.size() < testData.size()) { // oops?
5200 QCOMPARE(reader.data, testData.left(reader.data.size()));
5201 qDebug() << "The data is incomplete, the last" << testData.size() - reader.data.size() << "bytes are missing";
5202 }
5203 QCOMPARE(reader.data.size(), testData.size());
5204 QCOMPARE(reader.data, testData);
5205
5206 // OK we got the file alright, but did setReadBufferSize work?
5207 QVERIFY(server.transferRate != -1);
5208 if (bufferSize > 0) {
5209 const int allowedDeviation = 16; // TODO find out why the send rate is 13% faster currently
5210 const int minRate = rate * 1024 * (100-allowedDeviation) / 100;
5211 const int maxRate = rate * 1024 * (100+allowedDeviation) / 100;
5212 qDebug() << minRate << "<="<< server.transferRate << "<=" << maxRate << '?';
5213 // The test takes too long to run if sending enough data to overwhelm the
5214 // receiver's kernel buffers.
5215 //QEXPECT_FAIL("http+limited", "Limiting is broken right now, check QTBUG-15065", Continue);
5216 //QEXPECT_FAIL("https+limited", "Limiting is broken right now, check QTBUG-15065", Continue);
5217 //QVERIFY(server.transferRate >= minRate && server.transferRate <= maxRate);
5218 }
5219}
5220
5221void tst_QNetworkReply::ioPostToHttpUploadProgress()
5222{
5223 //test file must be larger than OS socket buffers (~830kB on MacOS 10.6)
5224 QFile sourceFile(testDataDir + "/image1.jpg");
5225 QVERIFY(sourceFile.open(QIODevice::ReadOnly));
5226 const qint64 sourceFileSize = sourceFile.size();
5227
5228 // emulate a minimal http server
5229 QTcpServer server;
5230 server.listen(address: QHostAddress(QHostAddress::LocalHost), port: 0);
5231
5232 // create the request
5233 QUrl url = QUrl(QString("http://127.0.0.1:%1/").arg(a: server.serverPort()));
5234 QNetworkRequest request(url);
5235 request.setRawHeader(headerName: "Content-Type", value: "application/octet-stream");
5236 QNetworkReplyPtr reply(manager.post(request, data: &sourceFile));
5237 QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64)));
5238 connect(sender: &server, SIGNAL(newConnection()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
5239
5240 // get the request started and the incoming socket connected
5241 QTestEventLoop::instance().enterLoop(secs: 10);
5242 QVERIFY(!QTestEventLoop::instance().timeout());
5243 QTcpSocket *incomingSocket = server.nextPendingConnection();
5244 QVERIFY(incomingSocket);
5245 disconnect(sender: &server, SIGNAL(newConnection()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
5246
5247 incomingSocket->setReadBufferSize(1024);
5248 QTestEventLoop::instance().enterLoop(secs: 5);
5249 // some progress should have been made
5250 QVERIFY(!spy.isEmpty());
5251 const QList<QVariant> args = spy.last();
5252 QVERIFY(!args.isEmpty());
5253 const qint64 bufferedUploadProgress = args.at(i: 0).toLongLong();
5254 QVERIFY(bufferedUploadProgress > 0);
5255 // but not everything? - Note however, that under CI virtualization,
5256 // particularly on Windows, it frequently happens that the whole file
5257 // is uploaded in one chunk.
5258 if (bufferedUploadProgress == sourceFileSize) {
5259 qWarning() << QDir::toNativeSeparators(pathName: sourceFile.fileName())
5260 << "of" << sourceFileSize << "bytes was uploaded in one go.";
5261 }
5262
5263 // set the read buffer to unlimited
5264 incomingSocket->setReadBufferSize(0);
5265 QTestEventLoop::instance().enterLoop(secs: 10);
5266 // progress should be finished
5267 QVERIFY(!spy.isEmpty());
5268 const QList<QVariant> args3 = spy.last();
5269 QVERIFY(!args3.isEmpty());
5270 // More progress than before
5271 const qint64 unbufferedUploadProgress = args3.at(i: 0).toLongLong();
5272 if (bufferedUploadProgress < sourceFileSize)
5273 QVERIFY(unbufferedUploadProgress > bufferedUploadProgress);
5274 QCOMPARE(unbufferedUploadProgress, args3.at(1).toLongLong());
5275 // And actually finished..
5276 QCOMPARE(unbufferedUploadProgress, sourceFileSize);
5277
5278 // after sending this, the QNAM should emit finished()
5279 connect(ptr: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
5280 incomingSocket->write(data: "HTTP/1.0 200 OK\r\n");
5281 incomingSocket->write(data: "Content-Length: 0\r\n");
5282 incomingSocket->write(data: "\r\n");
5283 QTestEventLoop::instance().enterLoop(secs: 10);
5284 // not timeouted -> finished() was emitted
5285 QVERIFY(!QTestEventLoop::instance().timeout());
5286
5287 incomingSocket->close();
5288 server.close();
5289}
5290
5291void tst_QNetworkReply::emitAllUploadProgressSignals()
5292{
5293 QFile sourceFile(testDataDir + "/image1.jpg");
5294 QVERIFY(sourceFile.open(QIODevice::ReadOnly));
5295
5296 // emulate a minimal http server
5297 QTcpServer server;
5298 server.listen(address: QHostAddress(QHostAddress::LocalHost), port: 0);
5299 connect(sender: &server, SIGNAL(newConnection()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
5300
5301 QUrl url = QUrl(QLatin1String("http://127.0.0.1:") + QString::number(server.serverPort()) + QLatin1Char('/'));
5302 QNetworkRequest normalRequest(url);
5303 normalRequest.setRawHeader(headerName: "Content-Type", value: "application/octet-stream");
5304
5305 QNetworkRequest catchAllSignalsRequest(normalRequest);
5306 catchAllSignalsRequest.setAttribute(code: QNetworkRequest::EmitAllUploadProgressSignalsAttribute, value: true);
5307
5308 QList<QNetworkRequest> requests;
5309 requests << normalRequest << catchAllSignalsRequest;
5310
5311 QList<int> signalCount;
5312
5313 foreach (const QNetworkRequest &request, requests) {
5314
5315 sourceFile.seek(offset: 0);
5316 QNetworkReplyPtr reply(manager.post(request, data: &sourceFile));
5317 QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64)));
5318
5319 // get the request started and the incoming socket connected
5320 QTestEventLoop::instance().enterLoop(secs: 10);
5321 QVERIFY(!QTestEventLoop::instance().timeout());
5322 QTcpSocket *incomingSocket = server.nextPendingConnection();
5323 QVERIFY(incomingSocket);
5324 QTestEventLoop::instance().enterLoop(secs: 10);
5325
5326 connect(ptr: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
5327 incomingSocket->write(data: "HTTP/1.0 200 OK\r\n");
5328 incomingSocket->write(data: "Content-Length: 0\r\n");
5329 incomingSocket->write(data: "\r\n");
5330 QTestEventLoop::instance().enterLoop(secs: 10);
5331 // not timeouted -> finished() was emitted
5332 QVERIFY(!QTestEventLoop::instance().timeout());
5333
5334 incomingSocket->close();
5335 signalCount.append(t: spy.count());
5336 reply->deleteLater();
5337 }
5338 server.close();
5339
5340 // verify that the normal request emitted less signals than the one emitting all signals
5341 QVERIFY2(signalCount.at(0) < signalCount.at(1), "no upload signal was suppressed");
5342}
5343
5344void tst_QNetworkReply::ioPostToHttpEmptyUploadProgress()
5345{
5346 QByteArray ba;
5347 ba.resize(size: 0);
5348 QBuffer buffer(&ba,0);
5349 QVERIFY(buffer.open(QIODevice::ReadOnly));
5350
5351 // emulate a minimal http server
5352 QTcpServer server;
5353 server.listen(address: QHostAddress(QHostAddress::LocalHost), port: 0);
5354
5355 // create the request
5356 QUrl url = QUrl(QLatin1String("http://127.0.0.1:") + QString::number(server.serverPort()) + QLatin1Char('/'));
5357 QNetworkRequest request(url);
5358 request.setRawHeader(headerName: "Content-Type", value: "application/octet-stream");
5359 QNetworkReplyPtr reply(manager.post(request, data: &buffer));
5360 QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64)));
5361 connect(sender: &server, SIGNAL(newConnection()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
5362
5363
5364 // get the request started and the incoming socket connected
5365 QTestEventLoop::instance().enterLoop(secs: 10);
5366 QVERIFY(!QTestEventLoop::instance().timeout());
5367 QTcpSocket *incomingSocket = server.nextPendingConnection();
5368 QVERIFY(incomingSocket);
5369
5370 // after sending this, the QNAM should emit finished()
5371 connect(ptr: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
5372 incomingSocket->write(data: "HTTP/1.0 200 OK\r\n");
5373 incomingSocket->write(data: "Content-Length: 0\r\n");
5374 incomingSocket->write(data: "\r\n");
5375 incomingSocket->flush();
5376 QTestEventLoop::instance().enterLoop(secs: 10);
5377 // not timeouted -> finished() was emitted
5378 QVERIFY(!QTestEventLoop::instance().timeout());
5379
5380 // final check: only 1 uploadProgress has been emitted
5381 QCOMPARE(spy.length(), 1);
5382 QList<QVariant> args = spy.last();
5383 QVERIFY(!args.isEmpty());
5384 QCOMPARE(args.at(0).toLongLong(), buffer.size());
5385 QCOMPARE(args.at(0).toLongLong(), buffer.size());
5386
5387 incomingSocket->close();
5388 server.close();
5389}
5390
5391void tst_QNetworkReply::lastModifiedHeaderForFile()
5392{
5393 QFileInfo fileInfo(testDataDir + "/bigfile");
5394 QVERIFY(fileInfo.exists());
5395
5396 QUrl url = QUrl::fromLocalFile(localfile: fileInfo.filePath());
5397
5398 QNetworkRequest request(url);
5399 QNetworkReplyPtr reply(manager.head(request));
5400
5401 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
5402
5403 QDateTime header = reply->header(header: QNetworkRequest::LastModifiedHeader).toDateTime();
5404 QCOMPARE(header, fileInfo.lastModified());
5405}
5406
5407void tst_QNetworkReply::lastModifiedHeaderForHttp()
5408{
5409 // Tue, 22 May 2007 12:04:57 GMT according to webserver
5410 QUrl url = "http://" + QtNetworkSettings::httpServerName() + "/qtest/fluke.gif";
5411
5412 QNetworkRequest request(url);
5413 QNetworkReplyPtr reply(manager.head(request));
5414
5415 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
5416
5417 QDateTime header = reply->header(header: QNetworkRequest::LastModifiedHeader).toDateTime();
5418 QDateTime realDate = QDateTime::fromString(s: "2007-05-22T12:04:57", f: Qt::ISODate);
5419 realDate.setTimeSpec(Qt::UTC);
5420
5421 QCOMPARE(header, realDate);
5422}
5423
5424void tst_QNetworkReply::httpCanReadLine()
5425{
5426 QNetworkRequest request(QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"));
5427 QNetworkReplyPtr reply(manager.get(request));
5428
5429 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
5430
5431 QCOMPARE(reply->error(), QNetworkReply::NoError);
5432
5433 QVERIFY(reply->canReadLine());
5434 QVERIFY(!reply->readAll().isEmpty());
5435 QVERIFY(!reply->canReadLine());
5436}
5437
5438#ifdef QT_BUILD_INTERNAL
5439void tst_QNetworkReply::rateControl_data()
5440{
5441 QTest::addColumn<int>(name: "rate");
5442
5443 QTest::newRow(dataTag: "15") << 15;
5444 QTest::newRow(dataTag: "40") << 40;
5445 QTest::newRow(dataTag: "73") << 73;
5446 QTest::newRow(dataTag: "80") << 80;
5447 QTest::newRow(dataTag: "125") << 125;
5448 QTest::newRow(dataTag: "250") << 250;
5449 QTest::newRow(dataTag: "1024") << 1024;
5450}
5451#endif
5452
5453#ifdef QT_BUILD_INTERNAL
5454void tst_QNetworkReply::rateControl()
5455{
5456 QSKIP("Test disabled -- only for manual purposes");
5457 // this function tests that we aren't reading from the network
5458 // faster than the data is being consumed.
5459 QFETCH(int, rate);
5460
5461 // ask for 20 seconds worth of data
5462 FastSender sender(20 * rate * 1024);
5463
5464 QNetworkRequest request("debugpipe://localhost:" + QString::number(sender.serverPort()));
5465 QNetworkReplyPtr reply(manager.get(request));
5466 reply->setReadBufferSize(32768);
5467 QSignalSpy errorSpy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
5468
5469 RateControlledReader reader(sender, reply.data(), rate, 20);
5470
5471 // this test is designed to run for 25 seconds at most
5472 QElapsedTimer loopTimer;
5473 loopTimer.start();
5474
5475 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
5476
5477 int elapsedTime = loopTimer.elapsed();
5478
5479 if (!errorSpy.isEmpty()) {
5480 qDebug() << "ERROR!" << errorSpy[0][0] << reply->errorString();
5481 }
5482
5483 qDebug() << "tst_QNetworkReply::rateControl" << "send rate:" << sender.transferRate;
5484 qDebug() << "tst_QNetworkReply::rateControl" << "receive rate:" << reader.totalBytesRead * 1000 / elapsedTime
5485 << "(it received" << reader.totalBytesRead << "bytes in" << elapsedTime << "ms)";
5486
5487 sender.wait();
5488
5489 QCOMPARE(reply->url(), request.url());
5490 QCOMPARE(reply->error(), QNetworkReply::NoError);
5491
5492 QVERIFY(sender.transferRate != -1);
5493 int minRate = rate * 1024 * 9 / 10;
5494 int maxRate = rate * 1024 * 11 / 10;
5495 QVERIFY(sender.transferRate >= minRate);
5496 QVERIFY(sender.transferRate <= maxRate);
5497}
5498#endif
5499
5500void tst_QNetworkReply::downloadProgress_data()
5501{
5502 QTest::addColumn<QUrl>(name: "url");
5503 QTest::addColumn<int>(name: "expectedSize");
5504
5505 QTest::newRow(dataTag: "empty") << QUrl::fromLocalFile(QFINDTESTDATA("empty")) << 0;
5506 QTest::newRow(dataTag: "http:small") << QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt") << 25962;
5507 QTest::newRow(dataTag: "http:big") << QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/bigfile") << 519240;
5508 QTest::newRow(dataTag: "http:no-length") << QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/deflate/rfc2616.html") << -1;
5509 QTest::newRow(dataTag: "ftp:small") << QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt") << 25962;
5510 QTest::newRow(dataTag: "ftp:big") << QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/bigfile") << 519240;
5511}
5512
5513class SlowReader : public QObject
5514{
5515 Q_OBJECT
5516public:
5517 SlowReader(QIODevice *dev)
5518 : device(dev)
5519 {
5520 connect(sender: device, SIGNAL(readyRead()), receiver: this, SLOT(deviceReady()));
5521 }
5522private slots:
5523 void deviceReady()
5524 {
5525 QTimer::singleShot(msec: 100, receiver: this, SLOT(doRead()));
5526 }
5527
5528 void doRead()
5529 {
5530 device->readAll();
5531 }
5532private:
5533 QIODevice *device;
5534};
5535
5536void tst_QNetworkReply::downloadProgress()
5537{
5538 QFETCH(QUrl, url);
5539 QFETCH(int, expectedSize);
5540
5541 QNetworkRequest request(url);
5542 QNetworkReplyPtr reply(manager.get(request));
5543 //artificially slow down the test, otherwise only the final signal is emitted
5544 reply->setReadBufferSize(qMax(a: 20000, b: expectedSize / 4));
5545 SlowReader reader(reply.data());
5546 QSignalSpy spy(reply.data(), SIGNAL(downloadProgress(qint64,qint64)));
5547 connect(ptr: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
5548 QTestEventLoop::instance().enterLoop(secs: 30);
5549 QVERIFY(!QTestEventLoop::instance().timeout());
5550 QVERIFY(reply->isFinished());
5551
5552 QVERIFY(spy.count() > 0);
5553
5554 //final progress should have equal current & total
5555 QList<QVariant> args = spy.takeLast();
5556 QCOMPARE(args.at(0).toInt(), args.at(1).toInt());
5557
5558 qint64 current = 0;
5559 //intermediate progress ascending and has expected total
5560 while (!spy.isEmpty()) {
5561 args = spy.takeFirst();
5562 QVERIFY(args.at(0).toInt() >= current);
5563 if (expectedSize >=0)
5564 QCOMPARE(args.at(1).toInt(), expectedSize);
5565 else
5566 QVERIFY(args.at(1).toInt() == expectedSize || args.at(1).toInt() == args.at(0).toInt());
5567 current = args.at(i: 0).toInt();
5568 }
5569}
5570
5571#ifdef QT_BUILD_INTERNAL
5572void tst_QNetworkReply::uploadProgress_data()
5573{
5574 putToFile_data();
5575}
5576#endif
5577
5578#ifdef QT_BUILD_INTERNAL
5579void tst_QNetworkReply::uploadProgress()
5580{
5581 QFETCH(QByteArray, data);
5582 QTcpServer server;
5583 QVERIFY(server.listen());
5584
5585 QNetworkRequest request("debugpipe://127.0.0.1:" + QString::number(server.serverPort()) + "/?bare=1");
5586 QNetworkReplyPtr reply(manager.put(request, data));
5587 QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64)));
5588 QSignalSpy finished(reply.data(), SIGNAL(finished()));
5589 QVERIFY(spy.isValid());
5590 QVERIFY(finished.isValid());
5591
5592 QCoreApplication::instance()->processEvents();
5593 if (!server.hasPendingConnections())
5594 server.waitForNewConnection(msec: 1000);
5595 QVERIFY(server.hasPendingConnections());
5596
5597 QTcpSocket *receiver = server.nextPendingConnection();
5598 if (finished.count() == 0) {
5599 // it's not finished yet, so wait for it to be
5600 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
5601 }
5602 delete receiver;
5603
5604 QVERIFY(finished.count() > 0);
5605 QVERIFY(spy.count() > 0);
5606
5607 QList<QVariant> args = spy.last();
5608 QCOMPARE(args.at(0).toInt(), data.size());
5609 QCOMPARE(args.at(1).toInt(), data.size());
5610}
5611#endif
5612
5613void tst_QNetworkReply::chaining_data()
5614{
5615 putToFile_data();
5616}
5617
5618void tst_QNetworkReply::chaining()
5619{
5620 QTemporaryFile sourceFile(QDir::currentPath() + "/temp-XXXXXX");
5621 sourceFile.setAutoRemove(true);
5622 QVERIFY2(sourceFile.open(), qPrintable(sourceFile.errorString()));
5623
5624 QFETCH(QByteArray, data);
5625 QCOMPARE(sourceFile.write(data), data.size());
5626 sourceFile.flush();
5627 QCOMPARE(sourceFile.size(), qint64(data.size()));
5628
5629 QNetworkRequest request(QUrl::fromLocalFile(localfile: sourceFile.fileName()));
5630 QNetworkReplyPtr getReply(manager.get(request));
5631
5632 QFile targetFile(testFileName);
5633 QUrl url = QUrl::fromLocalFile(localfile: targetFile.fileName());
5634 request.setUrl(url);
5635 QNetworkReplyPtr putReply(manager.put(request, data: getReply.data()));
5636
5637 QCOMPARE(waitForFinish(putReply), int(Success));
5638
5639 QCOMPARE(getReply->url(), QUrl::fromLocalFile(sourceFile.fileName()));
5640 QCOMPARE(getReply->error(), QNetworkReply::NoError);
5641 QCOMPARE(getReply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), sourceFile.size());
5642
5643 QCOMPARE(putReply->url(), url);
5644 QCOMPARE(putReply->error(), QNetworkReply::NoError);
5645 QCOMPARE(putReply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), Q_INT64_C(0));
5646 QVERIFY(putReply->readAll().isEmpty());
5647
5648 QVERIFY(sourceFile.atEnd());
5649 sourceFile.seek(offset: 0); // reset it to the beginning
5650
5651 QVERIFY(targetFile.open(QIODevice::ReadOnly));
5652 QCOMPARE(targetFile.size(), sourceFile.size());
5653 QCOMPARE(targetFile.readAll(), sourceFile.readAll());
5654}
5655
5656void tst_QNetworkReply::receiveCookiesFromHttp_data()
5657{
5658 QTest::addColumn<QString>(name: "cookieString");
5659 QTest::addColumn<QList<QNetworkCookie> >(name: "expectedCookiesFromHttp");
5660 QTest::addColumn<QList<QNetworkCookie> >(name: "expectedCookiesInJar");
5661
5662 QTest::newRow(dataTag: "empty") << "" << QList<QNetworkCookie>() << QList<QNetworkCookie>();
5663
5664 QList<QNetworkCookie> header, jar;
5665 QNetworkCookie cookie("a", "b");
5666 header << cookie;
5667 cookie.setDomain(QtNetworkSettings::httpServerName());
5668 cookie.setPath("/qtest/cgi-bin/");
5669 jar << cookie;
5670 QTest::newRow(dataTag: "simple-cookie") << "a=b" << header << jar;
5671
5672 header << QNetworkCookie("c", "d");
5673 cookie.setName("c");
5674 cookie.setValue("d");
5675 jar << cookie;
5676 QTest::newRow(dataTag: "two-cookies") << "a=b\nc=d" << header << jar;
5677
5678 header.clear();
5679 jar.clear();
5680 header << QNetworkCookie("a", "b, c=d");
5681 cookie.setName("a");
5682 cookie.setValue("b, c=d");
5683 jar << cookie;
5684 QTest::newRow(dataTag: "invalid-two-cookies") << "a=b, c=d" << header << jar;
5685
5686 header.clear();
5687 jar.clear();
5688 cookie = QNetworkCookie("a", "b");
5689 cookie.setPath("/not/part-of-path");
5690 header << cookie;
5691 cookie.setDomain(QtNetworkSettings::httpServerName());
5692 jar << cookie;
5693 QTest::newRow(dataTag: "invalid-cookie-path") << "a=b; path=/not/part-of-path" << header << jar;
5694
5695 jar.clear();
5696 cookie = QNetworkCookie("a", "b");
5697 cookie.setDomain(".example.com");
5698 header.clear();
5699 header << cookie;
5700 QTest::newRow(dataTag: "invalid-cookie-domain") << "a=b; domain=.example.com" << header << jar;
5701}
5702
5703void tst_QNetworkReply::receiveCookiesFromHttp()
5704{
5705 QFETCH(QString, cookieString);
5706
5707 QByteArray data = cookieString.toLatin1() + '\n';
5708 QUrl url("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/set-cookie.cgi");
5709 QNetworkRequest request(url);
5710 request.setRawHeader(headerName: "Content-Type", value: "application/octet-stream");
5711 QNetworkReplyPtr reply;
5712 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data));
5713
5714 QCOMPARE(reply->url(), url);
5715 QCOMPARE(reply->error(), QNetworkReply::NoError);
5716
5717 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
5718
5719 QList<QNetworkCookie> setCookies =
5720 qvariant_cast<QList<QNetworkCookie> >(v: reply->header(header: QNetworkRequest::SetCookieHeader));
5721 QTEST(setCookies, "expectedCookiesFromHttp");
5722 QTEST(cookieJar->allCookies(), "expectedCookiesInJar");
5723}
5724
5725void tst_QNetworkReply::receiveCookiesFromHttpSynchronous_data()
5726{
5727 tst_QNetworkReply::receiveCookiesFromHttp_data();
5728}
5729
5730void tst_QNetworkReply::receiveCookiesFromHttpSynchronous()
5731{
5732 QFETCH(QString, cookieString);
5733
5734 QByteArray data = cookieString.toLatin1() + '\n';
5735 QUrl url("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/set-cookie.cgi");
5736
5737 QNetworkRequest request(url);
5738 request.setRawHeader(headerName: "Content-Type", value: "application/octet-stream");
5739 request.setAttribute(
5740 code: QNetworkRequest::SynchronousRequestAttribute,
5741 value: true);
5742
5743 QNetworkReplyPtr reply;
5744 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data));
5745
5746 QCOMPARE(reply->url(), url);
5747 QCOMPARE(reply->error(), QNetworkReply::NoError);
5748
5749 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
5750
5751 QList<QNetworkCookie> setCookies =
5752 qvariant_cast<QList<QNetworkCookie> >(v: reply->header(header: QNetworkRequest::SetCookieHeader));
5753 QTEST(setCookies, "expectedCookiesFromHttp");
5754 QTEST(cookieJar->allCookies(), "expectedCookiesInJar");
5755}
5756
5757void tst_QNetworkReply::sendCookies_data()
5758{
5759 QTest::addColumn<QList<QNetworkCookie> >(name: "cookiesToSet");
5760 QTest::addColumn<QString>(name: "expectedCookieString");
5761
5762 QList<QNetworkCookie> list;
5763 QTest::newRow(dataTag: "empty") << list << "";
5764
5765 QNetworkCookie cookie("a", "b");
5766 cookie.setPath("/");
5767 cookie.setDomain("example.com");
5768 list << cookie;
5769 QTest::newRow(dataTag: "no-match-domain") << list << "";
5770
5771 cookie.setDomain(QtNetworkSettings::httpServerName());
5772 cookie.setPath("/something/else");
5773 list << cookie;
5774 QTest::newRow(dataTag: "no-match-path") << list << "";
5775
5776 cookie.setPath("/");
5777 list << cookie;
5778 QTest::newRow(dataTag: "simple-cookie") << list << "a=b";
5779
5780 cookie.setPath("/qtest");
5781 cookie.setValue("longer");
5782 list << cookie;
5783 QTest::newRow(dataTag: "two-cookies") << list << "a=longer; a=b";
5784
5785 list.clear();
5786 cookie = QNetworkCookie("a", "b");
5787 cookie.setPath("/");
5788 cookie.setDomain(QLatin1Char('.') + QtNetworkSettings::serverDomainName());
5789 list << cookie;
5790 QTest::newRow(dataTag: "domain-match") << list << "a=b";
5791
5792 // but it shouldn't match this:
5793 cookie.setDomain(QtNetworkSettings::serverDomainName());
5794 list << cookie;
5795 QTest::newRow(dataTag: "domain-match-2") << list << "a=b";
5796}
5797
5798void tst_QNetworkReply::sendCookies()
5799{
5800 QFETCH(QString, expectedCookieString);
5801 QFETCH(QList<QNetworkCookie>, cookiesToSet);
5802 cookieJar->setAllCookies(cookiesToSet);
5803
5804 QUrl url("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/get-cookie.cgi");
5805 QNetworkRequest request(url);
5806 QNetworkReplyPtr reply;
5807 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
5808
5809 QCOMPARE(reply->url(), url);
5810 QCOMPARE(reply->error(), QNetworkReply::NoError);
5811
5812 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
5813
5814 QCOMPARE(QString::fromLatin1(reply->readAll()).trimmed(), expectedCookieString);
5815}
5816
5817void tst_QNetworkReply::sendCookiesSynchronous_data()
5818{
5819 tst_QNetworkReply::sendCookies_data();
5820}
5821
5822void tst_QNetworkReply::sendCookiesSynchronous()
5823{
5824 QFETCH(QString, expectedCookieString);
5825 QFETCH(QList<QNetworkCookie>, cookiesToSet);
5826 cookieJar->setAllCookies(cookiesToSet);
5827
5828 QUrl url("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/get-cookie.cgi");
5829 QNetworkRequest request(url);
5830
5831 request.setAttribute(
5832 code: QNetworkRequest::SynchronousRequestAttribute,
5833 value: true);
5834
5835 QNetworkReplyPtr reply;
5836 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
5837
5838 QCOMPARE(reply->url(), url);
5839 QCOMPARE(reply->error(), QNetworkReply::NoError);
5840
5841 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
5842
5843 QCOMPARE(QString::fromLatin1(reply->readAll()).trimmed(), expectedCookieString);
5844}
5845
5846void tst_QNetworkReply::nestedEventLoops_slot()
5847{
5848 QEventLoop subloop;
5849
5850 // 16 seconds: fluke times out in 15 seconds, which triggers a QTcpSocket error
5851 QTimer::singleShot(msec: 16000, receiver: &subloop, SLOT(quit()));
5852 subloop.exec();
5853
5854 QTestEventLoop::instance().exitLoop();
5855}
5856
5857void tst_QNetworkReply::notEnoughData()
5858{
5859 notEnoughDataForFastSender = true;
5860}
5861
5862void tst_QNetworkReply::nestedEventLoops()
5863{
5864 // Slightly fragile test, it may not be testing anything
5865 // This is certifying that we're not running into the same issue
5866 // that QHttp had (task 200432): the QTcpSocket connection is
5867 // closed by the remote end because of the kept-alive HTTP
5868 // connection timed out.
5869 //
5870 // The exact time required for this to happen is not exactly
5871 // defined. Our server (Apache httpd) times out after 15
5872 // seconds. (see above)
5873
5874 qDebug(msg: "Takes 16 seconds to run, please wait");
5875
5876 QUrl url("http://" + QtNetworkSettings::httpServerName());
5877 QNetworkRequest request(url);
5878 QNetworkReplyPtr reply(manager.get(request));
5879
5880 QSignalSpy finishedspy(reply.data(), SIGNAL(finished()));
5881 QSignalSpy errorspy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
5882
5883 connect(ptr: reply, SIGNAL(finished()), SLOT(nestedEventLoops_slot()));
5884 QTestEventLoop::instance().enterLoop(secs: 20);
5885 QVERIFY2(!QTestEventLoop::instance().timeout(), "Network timeout");
5886
5887 QCOMPARE(finishedspy.count(), 1);
5888 QCOMPARE(errorspy.count(), 0);
5889}
5890
5891#ifndef QT_NO_NETWORKPROXY
5892void tst_QNetworkReply::httpProxyCommands_data()
5893{
5894 QTest::addColumn<QUrl>(name: "url");
5895 QTest::addColumn<QByteArray>(name: "responseToSend");
5896 QTest::addColumn<QString>(name: "expectedCommand");
5897
5898 QTest::newRow(dataTag: "http")
5899 << QUrl("http://0.0.0.0:4443/http-request")
5900 << QByteArray("HTTP/1.0 200 OK\r\nProxy-Connection: close\r\nContent-Length: 1\r\n\r\n1")
5901 << "GET http://0.0.0.0:4443/http-request HTTP/1.";
5902#ifndef QT_NO_SSL
5903 QTest::newRow(dataTag: "https")
5904 << QUrl("https://0.0.0.0:4443/https-request")
5905 << QByteArray("HTTP/1.0 200 Connection Established\r\n\r\n")
5906 << "CONNECT 0.0.0.0:4443 HTTP/1.";
5907#endif
5908}
5909
5910void tst_QNetworkReply::httpProxyCommands()
5911{
5912 QFETCH(QUrl, url);
5913 QFETCH(QByteArray, responseToSend);
5914 QFETCH(QString, expectedCommand);
5915
5916 MiniHttpServer proxyServer(responseToSend);
5917 QNetworkProxy proxy(QNetworkProxy::HttpProxy, "127.0.0.1", proxyServer.serverPort());
5918
5919 manager.setProxy(proxy);
5920 QNetworkRequest request(url);
5921 request.setRawHeader(headerName: "User-Agent", value: "QNetworkReplyAutoTest/1.0");
5922 QNetworkReplyPtr reply(manager.get(request));
5923 //clearing the proxy here causes the test to fail.
5924 //the proxy isn't used until after the bearer has been started
5925 //which is correct in general, because system proxy isn't known until that time.
5926 //removing this line is safe, as the proxy is also reset by the cleanup() function
5927 //manager.setProxy(QNetworkProxy());
5928
5929 // wait for the finished signal
5930 QVERIFY(waitForFinish(reply) != Timeout);
5931
5932 //qDebug() << reply->error() << reply->errorString();
5933 //qDebug() << proxyServer.receivedData;
5934
5935 // we don't really care if the request succeeded
5936 // especially since it won't succeed in the HTTPS case
5937 // so just check that the command was correct
5938
5939 QString receivedHeader = proxyServer.receivedData.left(len: expectedCommand.length());
5940 QCOMPARE(receivedHeader, expectedCommand);
5941
5942 //QTBUG-17223 - make sure the user agent from the request is sent to proxy server even for CONNECT
5943 int uapos = proxyServer.receivedData.indexOf(c: "User-Agent");
5944 int uaend = proxyServer.receivedData.indexOf(c: "\r\n", from: uapos);
5945 QByteArray uaheader = proxyServer.receivedData.mid(index: uapos, len: uaend - uapos);
5946 QCOMPARE(uaheader, QByteArray("User-Agent: QNetworkReplyAutoTest/1.0"));
5947}
5948
5949class ProxyChangeHelper : public QObject
5950{
5951 Q_OBJECT
5952public:
5953 ProxyChangeHelper() : QObject(), signalCount(0) {};
5954public slots:
5955 void finishedSlot()
5956 {
5957 signalCount++;
5958 if (signalCount == 2)
5959 QMetaObject::invokeMethod(obj: &QTestEventLoop::instance(), member: "exitLoop", type: Qt::QueuedConnection);
5960 }
5961private:
5962 int signalCount;
5963};
5964
5965void tst_QNetworkReply::httpProxyCommandsSynchronous_data()
5966{
5967 httpProxyCommands_data();
5968}
5969#endif // !QT_NO_NETWORKPROXY
5970
5971struct QThreadCleanup
5972{
5973 static inline void cleanup(QThread *thread)
5974 {
5975 thread->quit();
5976 if (thread->wait(time: 3000))
5977 delete thread;
5978 else
5979 qWarning(msg: "thread hung, leaking memory so test can finish");
5980 }
5981};
5982
5983struct QDeleteLaterCleanup
5984{
5985 static inline void cleanup(QObject *o)
5986 {
5987 o->deleteLater();
5988 }
5989};
5990
5991#ifndef QT_NO_NETWORKPROXY
5992void tst_QNetworkReply::httpProxyCommandsSynchronous()
5993{
5994 QFETCH(QUrl, url);
5995 QFETCH(QByteArray, responseToSend);
5996 QFETCH(QString, expectedCommand);
5997
5998 // when using synchronous commands, we need a different event loop for
5999 // the server thread, because the client is never returning to the
6000 // event loop
6001 QScopedPointer<QThread, QThreadCleanup> serverThread(new QThread);
6002 QScopedPointer<MiniHttpServer, QDeleteLaterCleanup> proxyServer(new MiniHttpServer(responseToSend, false, serverThread.data()));
6003 QNetworkProxy proxy(QNetworkProxy::HttpProxy, "127.0.0.1", proxyServer->serverPort());
6004
6005 manager.setProxy(proxy);
6006 QNetworkRequest request(url);
6007
6008 // send synchronous request
6009 request.setAttribute(
6010 code: QNetworkRequest::SynchronousRequestAttribute,
6011 value: true);
6012
6013 QNetworkReplyPtr reply(manager.get(request));
6014 QVERIFY(reply->isFinished()); // synchronous
6015 manager.setProxy(QNetworkProxy());
6016
6017 //qDebug() << reply->error() << reply->errorString();
6018
6019 // we don't really care if the request succeeded
6020 // especially since it won't succeed in the HTTPS case
6021 // so just check that the command was correct
6022
6023 QString receivedHeader = proxyServer->receivedData.left(len: expectedCommand.length());
6024 QCOMPARE(receivedHeader, expectedCommand);
6025}
6026
6027void tst_QNetworkReply::proxyChange()
6028{
6029 ProxyChangeHelper helper;
6030 MiniHttpServer proxyServer(
6031 "HTTP/1.0 200 OK\r\nProxy-Connection: keep-alive\r\n"
6032 "Content-Length: 1\r\n\r\n1");
6033 QNetworkProxy dummyProxy(QNetworkProxy::HttpProxy, "127.0.0.1", proxyServer.serverPort());
6034 QNetworkRequest req(QUrl("http://" + QtNetworkSettings::httpServerName()));
6035 proxyServer.doClose = false;
6036
6037 {
6038 // Needed to initialize a network session in QNAM. Without an initialized session the GET
6039 // will be deferred until later, and the proxy will be unset first. This caused the test to
6040 // fail in standalone runs (it passed in CI because the same QNAM instance is used for the
6041 // entire test).
6042 QNetworkReplyPtr temporary(manager.get(request: req));
6043 waitForFinish(reply&: temporary);
6044 }
6045
6046 manager.setProxy(dummyProxy);
6047 QNetworkReplyPtr reply1(manager.get(request: req));
6048 connect(ptr: reply1, SIGNAL(finished()), receiver: &helper, SLOT(finishedSlot()));
6049
6050 manager.setProxy(QNetworkProxy());
6051 QNetworkReplyPtr reply2(manager.get(request: req));
6052 connect(ptr: reply2, SIGNAL(finished()), receiver: &helper, SLOT(finishedSlot()));
6053
6054 QTestEventLoop::instance().enterLoop(secs: 20);
6055 QVERIFY(!QTestEventLoop::instance().timeout());
6056
6057 // verify that the replies succeeded
6058 QCOMPARE(reply1->error(), QNetworkReply::NoError);
6059 QCOMPARE(reply1->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
6060 QCOMPARE(reply1->size(), 1);
6061
6062 QCOMPARE(reply2->error(), QNetworkReply::NoError);
6063 QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
6064 QVERIFY(reply2->size() > 1);
6065
6066 // now try again and get an error
6067 // this verifies that we reuse the already-open connection
6068
6069 proxyServer.doClose = true;
6070 proxyServer.dataToTransmit =
6071 "HTTP/1.0 403 Forbidden\r\nProxy-Connection: close\r\n"
6072 "Content-Length: 1\r\n\r\n1";
6073
6074 manager.setProxy(dummyProxy);
6075 QNetworkReplyPtr reply3(manager.get(request: req));
6076
6077 QCOMPARE(waitForFinish(reply3), int(Failure));
6078
6079 QVERIFY(int(reply3->error()) > 0);
6080}
6081#endif // !QT_NO_NETWORKPROXY
6082
6083void tst_QNetworkReply::authorizationError_data()
6084{
6085
6086 QTest::addColumn<QString>(name: "url");
6087 QTest::addColumn<int>(name: "errorSignalCount");
6088 QTest::addColumn<int>(name: "finishedSignalCount");
6089 QTest::addColumn<int>(name: "error");
6090 QTest::addColumn<int>(name: "httpStatusCode");
6091 QTest::addColumn<QString>(name: "httpBody");
6092
6093 QTest::newRow(dataTag: "unknown-authorization-method")
6094 << "http://" + QtNetworkSettings::httpServerName()
6095 + "/qtest/cgi-bin/http-unknown-authentication-method.cgi?401-authorization-required"
6096 << 1 << 1 << int(QNetworkReply::AuthenticationRequiredError)
6097 << 401 << "authorization required";
6098
6099 QTest::newRow(dataTag: "unknown-proxy-authorization-method")
6100 << "http://" + QtNetworkSettings::httpServerName()
6101 + "/qtest/cgi-bin/http-unknown-authentication-method.cgi?407-proxy-authorization-required"
6102 << 1 << 1 << int(QNetworkReply::ProxyAuthenticationRequiredError)
6103 << 407 << "authorization required";
6104}
6105
6106void tst_QNetworkReply::authorizationError()
6107{
6108 QFETCH(QString, url);
6109 QNetworkRequest request(url);
6110 QNetworkReplyPtr reply(manager.get(request));
6111
6112 QCOMPARE(reply->error(), QNetworkReply::NoError);
6113
6114 QSignalSpy errorSpy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
6115 QSignalSpy finishedSpy(reply.data(), SIGNAL(finished()));
6116 // now run the request:
6117 QCOMPARE(waitForFinish(reply), int(Failure));
6118
6119 QFETCH(int, errorSignalCount);
6120 QCOMPARE(errorSpy.count(), errorSignalCount);
6121 QFETCH(int, finishedSignalCount);
6122 QCOMPARE(finishedSpy.count(), finishedSignalCount);
6123 QFETCH(int, error);
6124 QCOMPARE(reply->error(), QNetworkReply::NetworkError(error));
6125
6126 QFETCH(int, httpStatusCode);
6127 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), httpStatusCode);
6128
6129 QFETCH(QString, httpBody);
6130 QCOMPARE(qint64(reply->size()), qint64(httpBody.size()));
6131 QCOMPARE(QString(reply->readAll()), httpBody);
6132}
6133
6134void tst_QNetworkReply::httpConnectionCount()
6135{
6136 QTcpServer server;
6137 QVERIFY(server.listen());
6138 QCoreApplication::instance()->processEvents();
6139
6140 for (int i = 0; i < 10; i++) {
6141 QNetworkRequest request (QUrl("http://127.0.0.1:" + QString::number(server.serverPort()) + QLatin1Char('/') + QString::number(i)));
6142 QNetworkReply* reply = manager.get(request);
6143 reply->setParent(&server);
6144 }
6145
6146 int pendingConnectionCount = 0;
6147 QElapsedTimer timer;
6148 timer.start();
6149
6150 while(pendingConnectionCount <= 20) {
6151 QTestEventLoop::instance().enterLoop(secs: 1);
6152 QTcpSocket *socket = server.nextPendingConnection();
6153 while (socket != 0) {
6154 pendingConnectionCount++;
6155 socket->setParent(&server);
6156 socket = server.nextPendingConnection();
6157 }
6158
6159 // at max. wait 10 sec
6160 if (timer.elapsed() > 10000)
6161 break;
6162 }
6163
6164 QCOMPARE(pendingConnectionCount, 6);
6165}
6166
6167void tst_QNetworkReply::httpReUsingConnectionSequential_data()
6168{
6169 QTest::addColumn<bool>(name: "doDeleteLater");
6170 QTest::newRow(dataTag: "deleteLater") << true;
6171 QTest::newRow(dataTag: "noDeleteLater") << false;
6172}
6173
6174void tst_QNetworkReply::httpReUsingConnectionSequential()
6175{
6176 QFETCH(bool, doDeleteLater);
6177
6178 QByteArray response("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
6179 MiniHttpServer server(response);
6180 server.multiple = true;
6181 server.doClose = false;
6182
6183 QUrl url;
6184 url.setScheme("http");
6185 url.setPort(server.serverPort());
6186 url.setHost(host: "127.0.0.1");
6187 // first request
6188 QNetworkReply* reply1 = manager.get(request: QNetworkRequest(url));
6189 connect(sender: reply1, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
6190 QTestEventLoop::instance().enterLoop(secs: 2);
6191 QVERIFY(!QTestEventLoop::instance().timeout());
6192 QVERIFY(!reply1->error());
6193 int reply1port = server.client->peerPort();
6194
6195 if (doDeleteLater)
6196 reply1->deleteLater();
6197
6198 // finished received, send the next one
6199 QNetworkReply*reply2 = manager.get(request: QNetworkRequest(url));
6200 connect(sender: reply2, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
6201 QTestEventLoop::instance().enterLoop(secs: 2);
6202 QVERIFY(!QTestEventLoop::instance().timeout());
6203 QVERIFY(!reply2->error());
6204 int reply2port = server.client->peerPort(); // should still be the same object
6205
6206 QVERIFY(reply1port > 0);
6207 QCOMPARE(server.totalConnections, 1);
6208 QCOMPARE(reply2port, reply1port);
6209
6210 if (!doDeleteLater)
6211 reply1->deleteLater(); // only do it if it was not done earlier
6212 reply2->deleteLater();
6213}
6214
6215class HttpReUsingConnectionFromFinishedSlot : public QObject
6216{
6217 Q_OBJECT
6218public:
6219 QNetworkReply* reply1;
6220 QNetworkReply* reply2;
6221 QUrl url;
6222 QNetworkAccessManager manager;
6223public slots:
6224 void finishedSlot()
6225 {
6226 QVERIFY(!reply1->error());
6227
6228 QFETCH(bool, doDeleteLater);
6229 if (doDeleteLater) {
6230 reply1->deleteLater();
6231 reply1 = 0;
6232 }
6233
6234 // kick off 2nd request and exit the loop when it is done
6235 reply2 = manager.get(request: QNetworkRequest(url));
6236 reply2->setParent(this);
6237 connect(sender: reply2, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
6238 }
6239};
6240
6241void tst_QNetworkReply::httpReUsingConnectionFromFinishedSlot_data()
6242{
6243 httpReUsingConnectionSequential_data();
6244}
6245
6246void tst_QNetworkReply::httpReUsingConnectionFromFinishedSlot()
6247{
6248 QByteArray response("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
6249 MiniHttpServer server(response);
6250 server.multiple = true;
6251 server.doClose = false;
6252
6253 HttpReUsingConnectionFromFinishedSlot helper;
6254 helper.reply1 = 0;
6255 helper.reply2 = 0;
6256 helper.url.setScheme("http");
6257 helper.url.setPort(server.serverPort());
6258 helper.url.setHost(host: "127.0.0.1");
6259
6260 // first request
6261 helper.reply1 = helper.manager.get(request: QNetworkRequest(helper.url));
6262 helper.reply1->setParent(&helper);
6263 connect(sender: helper.reply1, SIGNAL(finished()), receiver: &helper, SLOT(finishedSlot()));
6264 QTestEventLoop::instance().enterLoop(secs: 4);
6265 QVERIFY(!QTestEventLoop::instance().timeout());
6266
6267 QVERIFY(helper.reply2);
6268 QVERIFY(!helper.reply2->error());
6269
6270 QCOMPARE(server.totalConnections, 1);
6271}
6272
6273class HttpRecursiveCreationHelper : public QObject
6274{
6275 Q_OBJECT
6276public:
6277
6278 HttpRecursiveCreationHelper():
6279 QObject(0),
6280 requestsStartedCount_finished(0),
6281 requestsStartedCount_readyRead(0),
6282 requestsFinishedCount(0)
6283 {
6284 }
6285 QNetworkAccessManager manager;
6286 int requestsStartedCount_finished;
6287 int requestsStartedCount_readyRead;
6288 int requestsFinishedCount;
6289public slots:
6290 void finishedSlot()
6291 {
6292 requestsFinishedCount++;
6293
6294 QNetworkReply *reply = qobject_cast<QNetworkReply*>(object: sender());
6295 QVERIFY(!reply->error());
6296 QCOMPARE(reply->bytesAvailable(), 27906);
6297
6298 if (requestsFinishedCount == 60) {
6299 QTestEventLoop::instance().exitLoop();
6300 return;
6301 }
6302
6303 if (requestsStartedCount_finished < 30) {
6304 startOne();
6305 requestsStartedCount_finished++;
6306 }
6307
6308 reply->deleteLater();
6309 }
6310 void readyReadSlot()
6311 {
6312 QNetworkReply *reply = qobject_cast<QNetworkReply*>(object: sender());
6313 QVERIFY(!reply->error());
6314
6315 if (requestsStartedCount_readyRead < 30 && reply->bytesAvailable() > 27906/2) {
6316 startOne();
6317 requestsStartedCount_readyRead++;
6318 }
6319 }
6320 void startOne()
6321 {
6322 QUrl url = "http://" + QtNetworkSettings::httpServerName() + "/qtest/fluke.gif";
6323 QNetworkRequest request(url);
6324 QNetworkReply *reply = manager.get(request);
6325 reply->setParent(this);
6326 connect(sender: reply, SIGNAL(finished()), receiver: this, SLOT(finishedSlot()));
6327 connect(sender: reply, SIGNAL(readyRead()), receiver: this, SLOT(readyReadSlot()));
6328 }
6329};
6330
6331void tst_QNetworkReply::httpRecursiveCreation()
6332{
6333 // this test checks if creation of new requests to the same host properly works
6334 // from readyRead() and finished() signals
6335 HttpRecursiveCreationHelper helper;
6336 helper.startOne();
6337 QTestEventLoop::instance().enterLoop(secs: 30);
6338 QVERIFY(!QTestEventLoop::instance().timeout());
6339}
6340
6341#ifndef QT_NO_SSL
6342void tst_QNetworkReply::ignoreSslErrorsList_data()
6343{
6344 QTest::addColumn<QList<QSslError> >(name: "expectedSslErrors");
6345 QTest::addColumn<QNetworkReply::NetworkError>(name: "expectedNetworkError");
6346
6347 QList<QSslError> expectedSslErrors;
6348 QList<QSslCertificate> certs = QSslCertificate::fromPath(path: testDataDir + certsFilePath);
6349 QSslError rightError(FLUKE_CERTIFICATE_ERROR, certs.at(i: 0));
6350 QSslError wrongError(FLUKE_CERTIFICATE_ERROR);
6351
6352 QTest::newRow(dataTag: "SSL-failure-empty-list") << expectedSslErrors << QNetworkReply::SslHandshakeFailedError;
6353 expectedSslErrors.append(t: wrongError);
6354 QTest::newRow(dataTag: "SSL-failure-wrong-error") << expectedSslErrors << QNetworkReply::SslHandshakeFailedError;
6355 expectedSslErrors.append(t: rightError);
6356 QTest::newRow(dataTag: "allErrorsInExpectedList1") << expectedSslErrors << QNetworkReply::NoError;
6357 expectedSslErrors.removeAll(t: wrongError);
6358 QTest::newRow(dataTag: "allErrorsInExpectedList2") << expectedSslErrors << QNetworkReply::NoError;
6359 expectedSslErrors.removeAll(t: rightError);
6360 QTest::newRow(dataTag: "SSL-failure-empty-list-again") << expectedSslErrors << QNetworkReply::SslHandshakeFailedError;
6361}
6362
6363void tst_QNetworkReply::ignoreSslErrorsList()
6364{
6365 QString url(QLatin1String("https://") + QtNetworkSettings::httpServerName() + QLatin1String("/index.html"));
6366 QNetworkRequest request(url);
6367 QNetworkReplyPtr reply(manager.get(request));
6368
6369 QFETCH(QList<QSslError>, expectedSslErrors);
6370 reply->ignoreSslErrors(errors: expectedSslErrors);
6371
6372 QVERIFY(waitForFinish(reply) != Timeout);
6373
6374 QFETCH(QNetworkReply::NetworkError, expectedNetworkError);
6375 QCOMPARE(reply->error(), expectedNetworkError);
6376}
6377
6378void tst_QNetworkReply::ignoreSslErrorsListWithSlot_data()
6379{
6380 ignoreSslErrorsList_data();
6381}
6382
6383// this is not a test, just a slot called in the test below
6384void tst_QNetworkReply::ignoreSslErrorListSlot(QNetworkReply *reply, const QList<QSslError> &)
6385{
6386 reply->ignoreSslErrors(errors: storedExpectedSslErrors);
6387}
6388
6389// do the same as in ignoreSslErrorsList, but ignore the errors in the slot
6390void tst_QNetworkReply::ignoreSslErrorsListWithSlot()
6391{
6392 QString url(QLatin1String("https://") + QtNetworkSettings::httpServerName() + QLatin1String("/index.html"));
6393 QNetworkRequest request(url);
6394 QNetworkReplyPtr reply(manager.get(request));
6395
6396 QFETCH(QList<QSslError>, expectedSslErrors);
6397 // store the errors to ignore them later in the slot connected below
6398 storedExpectedSslErrors = expectedSslErrors;
6399 connect(sender: &manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
6400 receiver: this, SLOT(ignoreSslErrorListSlot(QNetworkReply*,QList<QSslError>)));
6401
6402
6403 QVERIFY(waitForFinish(reply) != Timeout);
6404
6405 QFETCH(QNetworkReply::NetworkError, expectedNetworkError);
6406 QCOMPARE(reply->error(), expectedNetworkError);
6407}
6408
6409void tst_QNetworkReply::sslConfiguration_data()
6410{
6411 QTest::addColumn<QSslConfiguration>(name: "configuration");
6412 QTest::addColumn<bool>(name: "works");
6413
6414 QTest::newRow(dataTag: "empty") << QSslConfiguration() << false;
6415 QSslConfiguration conf = QSslConfiguration::defaultConfiguration();
6416 QTest::newRow(dataTag: "default") << conf << false; // does not contain test server cert
6417 QList<QSslCertificate> testServerCert = QSslCertificate::fromPath(path: testDataDir + certsFilePath);
6418 conf.setCaCertificates(testServerCert);
6419 QTest::newRow(dataTag: "set-root-cert") << conf << true;
6420 conf.setProtocol(QSsl::SecureProtocols);
6421 QTest::newRow(dataTag: "secure") << conf << true;
6422}
6423
6424void tst_QNetworkReply::encrypted()
6425{
6426 qDebug() << QtNetworkSettings::httpServerName();
6427 QUrl url("https://" + QtNetworkSettings::httpServerName());
6428 QNetworkRequest request(url);
6429 QNetworkReply *reply = manager.get(request);
6430 reply->ignoreSslErrors();
6431
6432 QSignalSpy spy(reply, SIGNAL(encrypted()));
6433 connect(sender: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
6434 QTestEventLoop::instance().enterLoop(secs: 20);
6435 QVERIFY(!QTestEventLoop::instance().timeout());
6436
6437 QCOMPARE(spy.count(), 1);
6438
6439 reply->deleteLater();
6440}
6441
6442void tst_QNetworkReply::abortOnEncrypted()
6443{
6444 SslServer server;
6445 server.listen();
6446 if (!server.isListening())
6447 QSKIP("Server fails to listen. Skipping since QTcpServer is covered in another test.");
6448
6449 server.connect(sender: &server, signal: &SslServer::newEncryptedConnection, slot: [&server]() {
6450 // MSVC 201X C4573-misunderstands connect() or QObject::connect(), so use server.connect():
6451 server.connect(sender: server.socket, signal: &QTcpSocket::readyRead, context: server.socket, slot: []() {
6452 // This slot must not be invoked!
6453 QVERIFY(false);
6454 });
6455 });
6456
6457 QNetworkAccessManager nm;
6458 QNetworkReply *reply = nm.get(request: QNetworkRequest(QUrl(QString("https://localhost:%1").arg(a: server.serverPort()))));
6459 reply->ignoreSslErrors();
6460
6461 connect(sender: reply, signal: &QNetworkReply::encrypted, slot: [reply, &nm]() {
6462 reply->abort();
6463 nm.clearConnectionCache();
6464 });
6465
6466 QSignalSpy spyEncrypted(reply, &QNetworkReply::encrypted);
6467 QTRY_COMPARE(spyEncrypted.count(), 1);
6468
6469 // Wait for the socket to be closed again in order to be sure QTcpSocket::readyRead would have been emitted.
6470 QTRY_VERIFY(server.socket != nullptr);
6471 QTRY_COMPARE(server.socket->state(), QAbstractSocket::UnconnectedState);
6472}
6473
6474void tst_QNetworkReply::sslConfiguration()
6475{
6476 QNetworkRequest request(QUrl("https://" + QtNetworkSettings::httpServerName() + "/index.html"));
6477 QFETCH(QSslConfiguration, configuration);
6478 request.setSslConfiguration(configuration);
6479 QNetworkReplyPtr reply(manager.get(request));
6480
6481 QVERIFY(waitForFinish(reply) != Timeout);
6482
6483 QFETCH(bool, works);
6484 QNetworkReply::NetworkError expectedError = works ? QNetworkReply::NoError : QNetworkReply::SslHandshakeFailedError;
6485 QCOMPARE(reply->error(), expectedError);
6486}
6487
6488#ifdef QT_BUILD_INTERNAL
6489
6490void tst_QNetworkReply::sslSessionSharing_data()
6491{
6492 QTest::addColumn<bool>(name: "sessionSharingEnabled");
6493 QTest::newRow(dataTag: "enabled") << true;
6494 QTest::newRow(dataTag: "disabled") << false;
6495}
6496
6497void tst_QNetworkReply::sslSessionSharing()
6498{
6499#if QT_CONFIG(schannel) || defined(QT_SECURETRANSPORT)
6500 QSKIP("Not implemented with SecureTransport/Schannel");
6501#endif
6502
6503 QString urlString("https://" + QtNetworkSettings::httpServerName());
6504 QList<QNetworkReplyPtr> replies;
6505
6506 // warm up SSL session cache
6507 QNetworkRequest warmupRequest(urlString);
6508 QFETCH(bool, sessionSharingEnabled);
6509 warmupRequest.setAttribute(code: QNetworkRequest::User, value: sessionSharingEnabled); // so we can read it from the slot
6510 // Make sure the socket is closed when the request is finished to guarantee that
6511 // the _socket_ is not reused, but rather the ssl session
6512 warmupRequest.setRawHeader(headerName: "Connection", value: "close");
6513 if (! sessionSharingEnabled) {
6514 QSslConfiguration configuration(QSslConfiguration::defaultConfiguration());
6515 configuration.setSslOption(option: QSsl::SslOptionDisableSessionSharing, on: true);
6516 warmupRequest.setSslConfiguration(configuration);
6517 }
6518 QNetworkReply *reply = manager.get(request: warmupRequest);
6519 reply->ignoreSslErrors();
6520 connect(sender: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
6521 QTestEventLoop::instance().enterLoop(secs: 20);
6522 QVERIFY(!QTestEventLoop::instance().timeout());
6523 QCOMPARE(reply->error(), QNetworkReply::NoError);
6524 reply->deleteLater();
6525
6526 // now send several requests at the same time, so we open more sockets and reuse the SSL session
6527 for (int a = 0; a < 6; a++) {
6528 QNetworkRequest request(warmupRequest);
6529 replies.append(t: QNetworkReplyPtr(manager.get(request)));
6530 connect(ptr: replies.at(i: a), SIGNAL(finished()), receiver: this, SLOT(sslSessionSharingHelperSlot()));
6531 }
6532 QTestEventLoop::instance().enterLoop(secs: 20);
6533 QVERIFY(!QTestEventLoop::instance().timeout());
6534}
6535
6536void tst_QNetworkReply::sslSessionSharingHelperSlot()
6537{
6538 static int count = 0;
6539
6540 // check that SSL session sharing was used in at least one of the replies
6541 static bool sslSessionSharingWasUsed = false;
6542 QNetworkReply *reply = qobject_cast<QNetworkReply *>(object: sender());
6543 bool sslSessionSharingWasUsedInReply = QSslConfigurationPrivate::peerSessionWasShared(configuration: reply->sslConfiguration());
6544 if (sslSessionSharingWasUsedInReply)
6545 sslSessionSharingWasUsed = true;
6546
6547 QString urlQueryString = reply->url().query();
6548 QCOMPARE(reply->error(), QNetworkReply::NoError);
6549
6550 count++;
6551
6552 if (count == 6) { // all replies have finished
6553 QTestEventLoop::instance().exitLoop();
6554 bool sessionSharingWasEnabled = reply->request().attribute(code: QNetworkRequest::User).toBool();
6555 QCOMPARE(sslSessionSharingWasUsed, sessionSharingWasEnabled);
6556 count = 0; // reset for next row
6557 sslSessionSharingWasUsed = false; // reset for next row
6558 }
6559}
6560
6561void tst_QNetworkReply::sslSessionSharingFromPersistentSession_data()
6562{
6563 QTest::addColumn<bool>(name: "sessionPersistenceEnabled");
6564 QTest::newRow(dataTag: "enabled") << true;
6565 QTest::newRow(dataTag: "disabled") << false;
6566}
6567
6568void tst_QNetworkReply::sslSessionSharingFromPersistentSession()
6569{
6570#if QT_CONFIG(schannel) || defined(QT_SECURETRANSPORT)
6571 QSKIP("Not implemented with SecureTransport/Schannel");
6572#endif
6573
6574 QString urlString("https://" + QtNetworkSettings::httpServerName());
6575
6576 // warm up SSL session cache to get a working session
6577 QNetworkRequest warmupRequest(urlString);
6578 QFETCH(bool, sessionPersistenceEnabled);
6579 if (sessionPersistenceEnabled) {
6580 QSslConfiguration warmupConfiguration(QSslConfiguration::defaultConfiguration());
6581 warmupConfiguration.setSslOption(option: QSsl::SslOptionDisableSessionPersistence, on: false);
6582 warmupRequest.setSslConfiguration(warmupConfiguration);
6583 }
6584 QNetworkReply *warmupReply = manager.get(request: warmupRequest);
6585 warmupReply->ignoreSslErrors();
6586 connect(sender: warmupReply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
6587 QTestEventLoop::instance().enterLoop(secs: 20);
6588 QVERIFY(!QTestEventLoop::instance().timeout());
6589 QCOMPARE(warmupReply->error(), QNetworkReply::NoError);
6590 QByteArray sslSession = warmupReply->sslConfiguration().sessionTicket();
6591 QCOMPARE(!sslSession.isEmpty(), sessionPersistenceEnabled);
6592
6593 // test server sends a life time hint of 0 (old server) or 300 (new server),
6594 // without session ticket we get -1
6595 QList<int> expectedSessionTicketLifeTimeHint = sessionPersistenceEnabled
6596 ? QList<int>() << 0 << 300 : QList<int>() << -1;
6597 QVERIFY2(expectedSessionTicketLifeTimeHint.contains(
6598 warmupReply->sslConfiguration().sessionTicketLifeTimeHint()),
6599 "server did not send expected session life time hint");
6600
6601 warmupReply->deleteLater();
6602
6603 // now send another request with a new QNAM and the persisted session,
6604 // to verify it can be resumed without any internal state
6605 QNetworkRequest request(warmupRequest);
6606 if (sessionPersistenceEnabled) {
6607 QSslConfiguration configuration = request.sslConfiguration();
6608 configuration.setSessionTicket(sslSession);
6609 request.setSslConfiguration(configuration);
6610 }
6611 QNetworkAccessManager newManager;
6612 QNetworkReply *reply = newManager.get(request);
6613 reply->ignoreSslErrors();
6614 connect(sender: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
6615 QTestEventLoop::instance().enterLoop(secs: 20);
6616 QVERIFY(!QTestEventLoop::instance().timeout());
6617 QCOMPARE(reply->error(), QNetworkReply::NoError);
6618
6619 bool sslSessionSharingWasUsedInReply = QSslConfigurationPrivate::peerSessionWasShared(
6620 configuration: reply->sslConfiguration());
6621 QCOMPARE(sessionPersistenceEnabled, sslSessionSharingWasUsedInReply);
6622}
6623
6624#endif // QT_BUILD_INTERNAL
6625#endif // QT_NO_SSL
6626
6627void tst_QNetworkReply::getAndThenDeleteObject_data()
6628{
6629 QTest::addColumn<bool>(name: "replyFirst");
6630
6631 QTest::newRow(dataTag: "delete-reply-first") << true;
6632 QTest::newRow(dataTag: "delete-qnam-first") << false;
6633}
6634
6635void tst_QNetworkReply::getAndThenDeleteObject()
6636{
6637 QSKIP("unstable test - reply may be finished too early");
6638 // yes, this will leak if the testcase fails. I don't care. It must not fail then :P
6639 QNetworkAccessManager *manager = new QNetworkAccessManager();
6640 QNetworkRequest request("http://" + QtNetworkSettings::httpServerName() + "/qtest/bigfile");
6641 QNetworkReply *reply = manager->get(request);
6642 reply->setReadBufferSize(1);
6643 reply->setParent((QObject*)0); // must be 0 because else it is the manager
6644
6645 QTRY_VERIFY_WITH_TIMEOUT(reply->bytesAvailable(), 30000);
6646 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
6647 QVERIFY(!reply->isFinished()); // must not be finished
6648
6649 QFETCH(bool, replyFirst);
6650
6651 if (replyFirst) {
6652 delete reply;
6653 delete manager;
6654 } else {
6655 delete manager;
6656 delete reply;
6657 }
6658}
6659
6660// see https://bugs.webkit.org/show_bug.cgi?id=38935
6661void tst_QNetworkReply::symbianOpenCDataUrlCrash()
6662{
6663 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==");
6664 QUrl url = QUrl::fromEncoded(url: requestUrl.toLatin1());
6665 QNetworkRequest req(url);
6666 QNetworkReplyPtr reply;
6667
6668 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, req, reply));
6669
6670 QCOMPARE(reply->url(), url);
6671 QCOMPARE(reply->error(), QNetworkReply::NoError);
6672
6673 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(598));
6674}
6675
6676void tst_QNetworkReply::getFromHttpIntoBuffer_data()
6677{
6678 QTest::addColumn<QUrl>(name: "url");
6679
6680 QTest::newRow(dataTag: "rfc-internal") << QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt");
6681}
6682
6683// Please note that the whole "zero copy" download buffer API is private right now. Do not use it.
6684void tst_QNetworkReply::getFromHttpIntoBuffer()
6685{
6686 QFETCH(QUrl, url);
6687 QNetworkRequest request(url);
6688 request.setAttribute(code: QNetworkRequest::MaximumDownloadBufferSizeAttribute, value: 1024*128); // 128 kB
6689
6690 QNetworkAccessManager manager;
6691 QNetworkReply *reply = manager.get(request);
6692 connect(sender: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
6693 QTestEventLoop::instance().enterLoop(secs: 10);
6694 QVERIFY(!QTestEventLoop::instance().timeout());
6695 QVERIFY(reply->isFinished());
6696
6697 QFile reference(testDataDir + "/rfc3252.txt");
6698 QVERIFY(reference.open(QIODevice::ReadOnly));
6699
6700 QCOMPARE(reference.bytesAvailable(), reply->bytesAvailable());
6701 QCOMPARE(reference.size(), reply->size());
6702
6703 // Compare the memory buffer
6704 QVariant downloadBufferAttribute = reply->attribute(code: QNetworkRequest::DownloadBufferAttribute);
6705 QVERIFY(downloadBufferAttribute.isValid());
6706 QSharedPointer<char> sharedPointer = downloadBufferAttribute.value<QSharedPointer<char> >();
6707 bool memoryComparison =
6708 (0 == memcmp(s1: static_cast<void*>(reference.readAll().data()),
6709 s2: sharedPointer.data(), n: reference.size()));
6710 QVERIFY(memoryComparison);
6711
6712 // Make sure the normal reading works
6713 reference.seek(offset: 0);
6714 QCOMPARE(reply->read(42), reference.read(42));
6715 QCOMPARE(reply->getChar(0), reference.getChar(0));
6716 QCOMPARE(reply->peek(23), reference.peek(23));
6717 QCOMPARE(reply->readLine(), reference.readLine());
6718 QCOMPARE(reference.bytesAvailable(), reply->bytesAvailable());
6719 QCOMPARE(reply->readAll(), reference.readAll());
6720 QVERIFY(reply->atEnd());
6721}
6722
6723// FIXME we really need to consolidate all those server implementations
6724class GetFromHttpIntoBuffer2Server : QObject
6725{
6726 Q_OBJECT
6727 qint64 dataSize;
6728 qint64 dataSent;
6729 QTcpServer server;
6730 QTcpSocket *client;
6731 bool serverSendsContentLength;
6732 bool chunkedEncoding;
6733
6734public:
6735 GetFromHttpIntoBuffer2Server (qint64 ds, bool sscl, bool ce)
6736 : dataSize(ds), dataSent(0), client(0),
6737 serverSendsContentLength(sscl), chunkedEncoding(ce)
6738 {
6739 server.listen();
6740 connect(sender: &server, SIGNAL(newConnection()), receiver: this, SLOT(newConnectionSlot()));
6741 }
6742
6743 int serverPort() { return server.serverPort(); }
6744
6745public slots:
6746
6747 void newConnectionSlot()
6748 {
6749 client = server.nextPendingConnection();
6750 client->setParent(this);
6751 connect(sender: client, SIGNAL(readyRead()), receiver: this, SLOT(readyReadSlot()));
6752 connect(sender: client, SIGNAL(bytesWritten(qint64)), receiver: this, SLOT(bytesWrittenSlot(qint64)));
6753 }
6754
6755 void readyReadSlot()
6756 {
6757 client->readAll();
6758 client->write(data: "HTTP/1.0 200 OK\n");
6759 if (serverSendsContentLength)
6760 client->write(data: QString("Content-Length: " + QString::number(dataSize) + "\n").toLatin1());
6761 if (chunkedEncoding)
6762 client->write(data: QString("Transfer-Encoding: chunked\n").toLatin1());
6763 client->write(data: "Connection: close\n\n");
6764 }
6765
6766 void bytesWrittenSlot(qint64 amount)
6767 {
6768 Q_UNUSED(amount);
6769 if (dataSent == dataSize && client) {
6770 // close eventually
6771
6772 // chunked encoding: we have to send a last "empty" chunk
6773 if (chunkedEncoding)
6774 client->write(data: QString("0\r\n\r\n").toLatin1());
6775
6776 client->disconnectFromHost();
6777 server.close();
6778 client = 0;
6779 return;
6780 }
6781
6782 // send data
6783 if (client && client->bytesToWrite() < 100*1024 && dataSent < dataSize) {
6784 qint64 amount = qMin(a: qint64(16*1024), b: dataSize - dataSent);
6785 QByteArray data(amount, '@');
6786
6787 if (chunkedEncoding) {
6788 client->write(data: QByteArray::number(amount, base: 16).toUpper());
6789 client->write(data: "\r\n");
6790 client->write(data: data.constData(), len: amount);
6791 client->write(data: "\r\n");
6792 } else {
6793 client->write(data: data.constData(), len: amount);
6794 }
6795
6796 dataSent += amount;
6797 }
6798 }
6799};
6800
6801class GetFromHttpIntoBuffer2Client : QObject
6802{
6803 Q_OBJECT
6804private:
6805 bool useDownloadBuffer;
6806 QNetworkReply *reply;
6807 qint64 uploadSize;
6808 QList<qint64> bytesAvailableList;
6809public:
6810 GetFromHttpIntoBuffer2Client (QNetworkReply *reply, bool useDownloadBuffer, qint64 uploadSize)
6811 : useDownloadBuffer(useDownloadBuffer), reply(reply), uploadSize(uploadSize)
6812 {
6813 connect(sender: reply, SIGNAL(metaDataChanged()), receiver: this, SLOT(metaDataChangedSlot()));
6814 connect(sender: reply, SIGNAL(readyRead()), receiver: this, SLOT(readyReadSlot()));
6815 connect(sender: reply, SIGNAL(finished()), receiver: this, SLOT(finishedSlot()));
6816 }
6817
6818 public slots:
6819 void metaDataChangedSlot()
6820 {
6821 if (useDownloadBuffer) {
6822 QSharedPointer<char> sharedPointer = qvariant_cast<QSharedPointer<char> >(v: reply->attribute(code: QNetworkRequest::DownloadBufferAttribute));
6823 QVERIFY(!sharedPointer.isNull()); // It will be 0 if it failed
6824 }
6825
6826 // metaDataChanged needs to come before everything else
6827 QVERIFY(bytesAvailableList.isEmpty());
6828 }
6829
6830 void readyReadSlot()
6831 {
6832 QVERIFY(!reply->isFinished());
6833
6834 qint64 bytesAvailable = reply->bytesAvailable();
6835
6836 // bytesAvailable must never be 0
6837 QVERIFY(bytesAvailable != 0);
6838
6839 if (bytesAvailableList.length() < 5) {
6840 // We assume that the first few times the bytes available must be less than the complete size, e.g.
6841 // the bytesAvailable() function works correctly in case of a downloadBuffer.
6842 QVERIFY(bytesAvailable < uploadSize);
6843 }
6844 if (!bytesAvailableList.isEmpty()) {
6845 // Also check that the same bytesAvailable is not coming twice in a row
6846 QVERIFY(bytesAvailableList.last() != bytesAvailable);
6847 }
6848
6849 bytesAvailableList.append(t: bytesAvailable);
6850 // Add bytesAvailable to a list an parse
6851 }
6852
6853 void finishedSlot()
6854 {
6855 // We should have already received all readyRead
6856 QVERIFY(!bytesAvailableList.isEmpty());
6857 QCOMPARE(bytesAvailableList.last(), uploadSize);
6858 }
6859};
6860
6861void tst_QNetworkReply::getFromHttpIntoBuffer2_data()
6862{
6863 QTest::addColumn<bool>(name: "useDownloadBuffer");
6864
6865 QTest::newRow(dataTag: "use-download-buffer") << true;
6866 QTest::newRow(dataTag: "do-not-use-download-buffer") << false;
6867}
6868
6869// This test checks mostly that signal emissions are in correct order
6870// Please note that the whole "zero copy" download buffer API is private right now. Do not use it.
6871void tst_QNetworkReply::getFromHttpIntoBuffer2()
6872{
6873 QFETCH(bool, useDownloadBuffer);
6874
6875 // On my Linux Desktop the results are already visible with 128 kB, however we use this to have good results.
6876 enum {UploadSize = 32*1024*1024}; // 32 MB
6877
6878 GetFromHttpIntoBuffer2Server server(UploadSize, true, false);
6879
6880 QNetworkRequest request(QUrl("http://127.0.0.1:" + QString::number(server.serverPort()) + "/?bare=1"));
6881 if (useDownloadBuffer)
6882 request.setAttribute(code: QNetworkRequest::MaximumDownloadBufferSizeAttribute, value: 1024*1024*128); // 128 MB is max allowed
6883
6884 QNetworkAccessManager manager;
6885 QNetworkReplyPtr reply(manager.get(request));
6886
6887 GetFromHttpIntoBuffer2Client client(reply.data(), useDownloadBuffer, UploadSize);
6888
6889 connect(ptr: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()), ct: Qt::QueuedConnection);
6890 QTestEventLoop::instance().enterLoop(secs: 40);
6891 QCOMPARE(reply->error(), QNetworkReply::NoError);
6892 QVERIFY(!QTestEventLoop::instance().timeout());
6893}
6894
6895
6896void tst_QNetworkReply::getFromHttpIntoBufferCanReadLine()
6897{
6898 QString header("HTTP/1.0 200 OK\r\nContent-Length: 7\r\n\r\nxxx\nxxx");
6899
6900 MiniHttpServer server(header.toLatin1());
6901 server.doClose = true;
6902
6903 QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
6904 request.setAttribute(code: QNetworkRequest::MaximumDownloadBufferSizeAttribute, value: 1024*1024*128); // 128 MB is max allowed
6905 QNetworkReplyPtr reply(manager.get(request));
6906
6907 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
6908
6909 QCOMPARE(reply->error(), QNetworkReply::NoError);
6910 QVERIFY(reply->canReadLine());
6911 QCOMPARE(reply->read(1), QByteArray("x"));
6912 QVERIFY(reply->canReadLine());
6913 QCOMPARE(reply->read(3), QByteArray("xx\n"));
6914 QVERIFY(!reply->canReadLine());
6915 QCOMPARE(reply->readAll(), QByteArray("xxx"));
6916 QVERIFY(!reply->canReadLine());
6917}
6918
6919
6920
6921// Is handled somewhere else too, introduced this special test to have it more accessible
6922void tst_QNetworkReply::ioGetFromHttpWithoutContentLength()
6923{
6924 QByteArray dataToSend("HTTP/1.0 200 OK\r\n\r\nHALLO! 123!");
6925 MiniHttpServer server(dataToSend);
6926 server.doClose = true;
6927
6928 QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
6929 QNetworkReplyPtr reply(manager.get(request));
6930
6931 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
6932
6933 QCOMPARE(reply->url(), request.url());
6934 QVERIFY(reply->isFinished());
6935 QCOMPARE(reply->error(), QNetworkReply::NoError);
6936}
6937
6938// Is handled somewhere else too, introduced this special test to have it more accessible
6939void tst_QNetworkReply::ioGetFromHttpBrokenChunkedEncoding()
6940{
6941 // This is wrong chunked encoding because of the X. What actually has to follow is \r\n
6942 // and then the declaration of the final 0 chunk
6943 QByteArray dataToSend("HTTP/1.0 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n3\r\nABCX");
6944 MiniHttpServer server(dataToSend);
6945 server.doClose = false; // FIXME
6946
6947 QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
6948 QNetworkReplyPtr reply(manager.get(request));
6949
6950 connect(ptr: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
6951 QTestEventLoop::instance().enterLoop(secs: 10);
6952
6953 QEXPECT_FAIL(0, "We should close the socket and not just do nothing", Continue);
6954 QVERIFY(!QTestEventLoop::instance().timeout());
6955 QEXPECT_FAIL(0, "We should close the socket and not just do nothing", Continue);
6956 QVERIFY(reply->isFinished());
6957 QCOMPARE(reply->error(), QNetworkReply::NoError);
6958}
6959
6960// TODO:
6961// Prepare a gzip that has one chunk that expands to the size mentioned in the bugreport.
6962// Then have a custom HTTP server that waits after this chunk so the returning gets
6963// triggered.
6964void tst_QNetworkReply::qtbug12908compressedHttpReply()
6965{
6966 QString header("HTTP/1.0 200 OK\r\nContent-Encoding: gzip\r\nContent-Length: 63\r\n\r\n");
6967
6968 // dd if=/dev/zero of=qtbug-12908 bs=16384 count=1 && gzip qtbug-12908 && base64 -w 0 qtbug-12908.gz
6969 QString encodedFile("H4sICDdDaUwAA3F0YnVnLTEyOTA4AO3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAIC3AYbSVKsAQAAA");
6970 QByteArray decodedFile = QByteArray::fromBase64(base64: encodedFile.toLatin1());
6971 QCOMPARE(decodedFile.size(), 63);
6972
6973 MiniHttpServer server(header.toLatin1() + decodedFile);
6974 server.doClose = true;
6975
6976 QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
6977 QNetworkReplyPtr reply(manager.get(request));
6978
6979 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
6980
6981 QCOMPARE(reply->error(), QNetworkReply::NoError);
6982 QCOMPARE(reply->size(), qint64(16384));
6983 QCOMPARE(reply->readAll(), QByteArray(16384, '\0'));
6984}
6985
6986void tst_QNetworkReply::compressedHttpReplyBrokenGzip()
6987{
6988 QString header("HTTP/1.0 200 OK\r\nContent-Encoding: gzip\r\nContent-Length: 63\r\n\r\n");
6989
6990 // dd if=/dev/zero of=qtbug-12908 bs=16384 count=1 && gzip qtbug-12908 && base64 -w 0 qtbug-12908.gz
6991 // Then change "BMQ" to "BMX"
6992 QString encodedFile("H4sICDdDaUwAA3F0YnVnLTEyOTA4AO3BMXEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAIC3AYbSVKsAQAAA");
6993 QByteArray decodedFile = QByteArray::fromBase64(base64: encodedFile.toLatin1());
6994 QCOMPARE(decodedFile.size(), 63);
6995
6996 MiniHttpServer server(header.toLatin1() + decodedFile);
6997 server.doClose = true;
6998
6999 QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
7000 QNetworkReplyPtr reply(manager.get(request));
7001
7002 QCOMPARE(waitForFinish(reply), int(Failure));
7003
7004 QCOMPARE(reply->error(), QNetworkReply::ProtocolFailure);
7005}
7006
7007// TODO add similar test for FTP
7008void tst_QNetworkReply::getFromUnreachableIp()
7009{
7010 QNetworkAccessManager manager;
7011
7012#ifdef Q_OS_WIN
7013 // This test assumes that attempt to connect to 255.255.255.255 fails more
7014 // or less fast/immediately. This is not what we observe on Windows:
7015 // WSAConnect on non-blocking socket returns SOCKET_ERROR, WSAGetLastError
7016 // returns WSAEWOULDBLOCK (expected) and getsockopt most of the time returns
7017 // NOERROR; so socket engine starts a timer (30 s.) and waits for a timeout/
7018 // error/success. Unfortunately, the test itself is waiting only for 5 s.
7019 // So we have to adjust the connection timeout or skip the test completely
7020 // if the 'bearermanagement' feature is not available.
7021#if QT_CONFIG(bearermanagement)
7022 class ConfigurationGuard
7023 {
7024 public:
7025 explicit ConfigurationGuard(QNetworkAccessManager *m)
7026 : manager(m)
7027 {
7028 Q_ASSERT(m);
7029 auto conf = manager->configuration();
7030 previousTimeout = conf.connectTimeout();
7031 conf.setConnectTimeout(1500);
7032 manager->setConfiguration(conf);
7033 }
7034 ~ConfigurationGuard()
7035 {
7036 Q_ASSERT(manager);
7037 auto conf = manager->configuration();
7038 conf.setConnectTimeout(previousTimeout);
7039 manager->setConfiguration(conf);
7040 }
7041 private:
7042 QNetworkAccessManager *manager = nullptr;
7043 int previousTimeout = 0;
7044
7045 Q_DISABLE_COPY(ConfigurationGuard)
7046 };
7047
7048 const ConfigurationGuard restorer(&manager);
7049#else // bearermanagement
7050 QSKIP("This test is non-deterministic on Windows x86");
7051#endif // !bearermanagement
7052#endif // Q_OS_WIN
7053
7054 QNetworkRequest request(QUrl("http://255.255.255.255/42/23/narf/narf/narf"));
7055 QNetworkReplyPtr reply(manager.get(request));
7056
7057 QCOMPARE(waitForFinish(reply), int(Failure));
7058
7059 QVERIFY(reply->error() != QNetworkReply::NoError);
7060}
7061
7062void tst_QNetworkReply::qtbug4121unknownAuthentication()
7063{
7064 MiniHttpServer server(QByteArray("HTTP/1.1 401 bla\r\nWWW-Authenticate: crap\r\nContent-Length: 0\r\n\r\n"));
7065 server.doClose = false;
7066
7067 QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
7068 QNetworkAccessManager manager;
7069 QNetworkReplyPtr reply(manager.get(request));
7070
7071 QSignalSpy authSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
7072 QSignalSpy finishedSpy(&manager, SIGNAL(finished(QNetworkReply*)));
7073 QSignalSpy errorSpy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
7074
7075 connect(ptr: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()), ct: Qt::QueuedConnection);
7076 QTestEventLoop::instance().enterLoop(secs: 10);
7077 QVERIFY(!QTestEventLoop::instance().timeout());
7078
7079 QCOMPARE(authSpy.count(), 0);
7080 QCOMPARE(finishedSpy.count(), 1);
7081 QCOMPARE(errorSpy.count(), 1);
7082
7083 QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
7084}
7085
7086#ifndef QT_NO_NETWORKPROXY
7087void tst_QNetworkReply::authenticationCacheAfterCancel_data()
7088{
7089 QTest::addColumn<QNetworkProxy>(name: "proxy");
7090 QTest::addColumn<bool>(name: "proxyAuth");
7091 QTest::addColumn<QUrl>(name: "url");
7092 for (int i = 0; i < proxies.count(); ++i) {
7093 QTest::newRow(dataTag: "http" + proxies.at(i).tag)
7094 << proxies.at(i).proxy
7095 << proxies.at(i).requiresAuthentication
7096 << QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfcs-auth/rfc3252.txt");
7097#ifndef QT_NO_SSL
7098 QTest::newRow(dataTag: "https" + proxies.at(i).tag)
7099 << proxies.at(i).proxy
7100 << proxies.at(i).requiresAuthentication
7101 << QUrl("https://" + QtNetworkSettings::httpServerName() + "/qtest/rfcs-auth/rfc3252.txt");
7102#endif
7103 }
7104}
7105
7106class AuthenticationCacheHelper : public QObject
7107{
7108 Q_OBJECT
7109public:
7110 AuthenticationCacheHelper()
7111 {}
7112public slots:
7113 void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth)
7114 {
7115 if (!proxyPassword.isNull()) {
7116 auth->setUser(proxyUserName);
7117 auth->setPassword(proxyPassword);
7118 //clear credentials, if they are asked again, they were bad
7119 proxyUserName.clear();
7120 proxyPassword.clear();
7121 }
7122 }
7123 void authenticationRequired(QNetworkReply*,QAuthenticator *auth)
7124 {
7125 if (!httpPassword.isNull()) {
7126 auth->setUser(httpUserName);
7127 auth->setPassword(httpPassword);
7128 //clear credentials, if they are asked again, they were bad
7129 httpUserName.clear();
7130 httpPassword.clear();
7131 }
7132 }
7133public:
7134 QString httpUserName;
7135 QString httpPassword;
7136 QString proxyUserName;
7137 QString proxyPassword;
7138};
7139
7140/* Purpose of this test is to check credentials are cached correctly.
7141 - If user cancels authentication dialog (i.e. nothing is set to the QAuthenticator by the callback) then this is not cached
7142 - if user supplies a wrong password, then this is not cached
7143 - if user supplies a correct user/password combination then this is cached
7144
7145 Test is checking both the proxyAuthenticationRequired and authenticationRequired signals.
7146 */
7147void tst_QNetworkReply::authenticationCacheAfterCancel()
7148{
7149 QFETCH(QNetworkProxy, proxy);
7150 QFETCH(bool, proxyAuth);
7151 QFETCH(QUrl, url);
7152 QNetworkAccessManager manager;
7153#ifndef QT_NO_SSL
7154 connect(asender: &manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
7155 SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
7156#endif
7157 manager.setProxy(proxy);
7158 QSignalSpy authSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
7159 QSignalSpy proxyAuthSpy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
7160
7161 AuthenticationCacheHelper helper;
7162 connect(sender: &manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), receiver: &helper, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
7163 connect(sender: &manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), receiver: &helper, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
7164
7165 QNetworkRequest request(url);
7166 QNetworkReplyPtr reply;
7167 if (proxyAuth) {
7168 //should fail due to no credentials
7169 reply.reset(t: manager.get(request));
7170 connect(ptr: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()), ct: Qt::QueuedConnection);
7171 QTestEventLoop::instance().enterLoop(secs: 10);
7172 QVERIFY(!QTestEventLoop::instance().timeout());
7173
7174 QCOMPARE(reply->error(), QNetworkReply::ProxyAuthenticationRequiredError);
7175 QCOMPARE(authSpy.count(), 0);
7176 QCOMPARE(proxyAuthSpy.count(), 1);
7177 proxyAuthSpy.clear();
7178
7179 //should fail due to bad credentials
7180 helper.proxyUserName = "qsockstest";
7181 helper.proxyPassword = "badpassword";
7182 reply.reset(t: manager.get(request));
7183 connect(ptr: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()), ct: Qt::QueuedConnection);
7184 QTestEventLoop::instance().enterLoop(secs: 10);
7185 QVERIFY(!QTestEventLoop::instance().timeout());
7186
7187 // Work round known quirk in the old test server (danted -v < v1.1.19):
7188 if (reply->error() != QNetworkReply::HostNotFoundError)
7189 QCOMPARE(reply->error(), QNetworkReply::ProxyAuthenticationRequiredError);
7190 QCOMPARE(authSpy.count(), 0);
7191 QVERIFY(proxyAuthSpy.count() > 0);
7192 proxyAuthSpy.clear();
7193
7194 // QTBUG-23136 workaround (needed even with danted v1.1.19):
7195 if (proxy.port() == 1081) {
7196#ifdef QT_BUILD_INTERNAL
7197 QNetworkAccessManagerPrivate::clearAuthenticationCache(manager: &manager);
7198 QNetworkAccessManagerPrivate::clearConnectionCache(manager: &manager);
7199#else
7200 return;
7201#endif
7202 }
7203
7204 //next proxy auth should succeed, due to correct credentials
7205 helper.proxyUserName = "qsockstest";
7206 helper.proxyPassword = "password";
7207 }
7208
7209 //should fail due to no credentials
7210 reply.reset(t: manager.get(request));
7211 connect(ptr: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()), ct: Qt::QueuedConnection);
7212 QTestEventLoop::instance().enterLoop(secs: 10);
7213 QVERIFY(!QTestEventLoop::instance().timeout());
7214
7215 QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
7216 QVERIFY(authSpy.count() > 0);
7217 authSpy.clear();
7218 if (proxyAuth) {
7219 QVERIFY(proxyAuthSpy.count() > 0);
7220 proxyAuthSpy.clear();
7221 }
7222
7223 //should fail due to bad credentials
7224 helper.httpUserName = "baduser";
7225 helper.httpPassword = "badpassword";
7226 reply.reset(t: manager.get(request));
7227 connect(ptr: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()), ct: Qt::QueuedConnection);
7228 QTestEventLoop::instance().enterLoop(secs: 10);
7229 QVERIFY(!QTestEventLoop::instance().timeout());
7230
7231 QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
7232 QVERIFY(authSpy.count() > 0);
7233 authSpy.clear();
7234 if (proxyAuth) {
7235 //should be supplied from cache
7236 QCOMPARE(proxyAuthSpy.count(), 0);
7237 proxyAuthSpy.clear();
7238 }
7239
7240 //next auth should succeed, due to correct credentials
7241 helper.httpUserName = "httptest";
7242 helper.httpPassword = "httptest";
7243
7244 reply.reset(t: manager.get(request));
7245 connect(ptr: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()), ct: Qt::QueuedConnection);
7246 QTestEventLoop::instance().enterLoop(secs: 10);
7247 QVERIFY(!QTestEventLoop::instance().timeout());
7248
7249 QCOMPARE(reply->error(), QNetworkReply::NoError);
7250 QVERIFY(authSpy.count() > 0);
7251 authSpy.clear();
7252 if (proxyAuth) {
7253 //should be supplied from cache
7254 QCOMPARE(proxyAuthSpy.count(), 0);
7255 proxyAuthSpy.clear();
7256 }
7257
7258 //next auth should use cached credentials
7259 reply.reset(t: manager.get(request));
7260 connect(ptr: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()), ct: Qt::QueuedConnection);
7261 QTestEventLoop::instance().enterLoop(secs: 10);
7262 QVERIFY(!QTestEventLoop::instance().timeout());
7263
7264 QCOMPARE(reply->error(), QNetworkReply::NoError);
7265 //should be supplied from cache
7266 QCOMPARE(authSpy.count(), 0);
7267 authSpy.clear();
7268 if (proxyAuth) {
7269 //should be supplied from cache
7270 QCOMPARE(proxyAuthSpy.count(), 0);
7271 proxyAuthSpy.clear();
7272 }
7273
7274}
7275
7276void tst_QNetworkReply::authenticationWithDifferentRealm()
7277{
7278 AuthenticationCacheHelper helper;
7279 QNetworkAccessManager manager;
7280#ifndef QT_NO_SSL
7281 connect(asender: &manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
7282 SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
7283#endif
7284 connect(sender: &manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), receiver: &helper, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
7285 connect(sender: &manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), receiver: &helper, SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
7286
7287 helper.httpUserName = "httptest";
7288 helper.httpPassword = "httptest";
7289
7290 QNetworkRequest request(QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfcs-auth/rfc3252.txt"));
7291 QNetworkReply* reply = manager.get(request);
7292 connect(sender: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
7293 QTestEventLoop::instance().enterLoop(secs: 10);
7294 QVERIFY(!QTestEventLoop::instance().timeout());
7295 QCOMPARE(reply->error(), QNetworkReply::NoError);
7296
7297 helper.httpUserName = "httptest";
7298 helper.httpPassword = "httptest";
7299
7300 request.setUrl(QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/auth-digest/"));
7301 reply = manager.get(request);
7302 connect(sender: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
7303 QTestEventLoop::instance().enterLoop(secs: 10);
7304 QVERIFY(!QTestEventLoop::instance().timeout());
7305 QCOMPARE(reply->error(), QNetworkReply::NoError);
7306}
7307#endif // !QT_NO_NETWORKPROXY
7308
7309class QtBug13431Helper : public QObject
7310{
7311 Q_OBJECT
7312public:
7313 QNetworkReply* m_reply;
7314 QTimer m_dlTimer;
7315public slots:
7316 void replyFinished(QNetworkReply*) { QTestEventLoop::instance().exitLoop(); }
7317
7318 void onReadAndReschedule()
7319 {
7320 const qint64 bytesReceived = m_reply->bytesAvailable();
7321 if (bytesReceived && m_reply->readBufferSize()) {
7322 QByteArray data = m_reply->read(maxlen: bytesReceived);
7323 // reschedule read
7324 const int millisecDelay = static_cast<int>(bytesReceived * 1000 / m_reply->readBufferSize());
7325 m_dlTimer.start(msec: millisecDelay);
7326 }
7327 else {
7328 // reschedule read
7329 m_dlTimer.start(msec: 200);
7330 }
7331 }
7332};
7333
7334void tst_QNetworkReply::qtbug13431replyThrottling()
7335{
7336 QtBug13431Helper helper;
7337
7338 QNetworkAccessManager nam;
7339 connect(sender: &nam, SIGNAL(finished(QNetworkReply*)), receiver: &helper, SLOT(replyFinished(QNetworkReply*)));
7340
7341 // Download a bigger file
7342 QNetworkRequest netRequest(QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/bigfile"));
7343 helper.m_reply = nam.get(request: netRequest);
7344 // Set the throttle
7345 helper.m_reply->setReadBufferSize(36000);
7346
7347 // Schedule a timer that tries to read
7348
7349 connect(sender: &helper.m_dlTimer, SIGNAL(timeout()), receiver: &helper, SLOT(onReadAndReschedule()));
7350 helper.m_dlTimer.setSingleShot(true);
7351 helper.m_dlTimer.start(msec: 0);
7352
7353 QTestEventLoop::instance().enterLoop(secs: 30);
7354 QVERIFY(!QTestEventLoop::instance().timeout());
7355 QVERIFY(helper.m_reply->isFinished());
7356 QCOMPARE(helper.m_reply->error(), QNetworkReply::NoError);
7357}
7358
7359void tst_QNetworkReply::httpWithNoCredentialUsage()
7360{
7361 QNetworkAccessManager manager;
7362
7363 QSignalSpy authSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
7364 QSignalSpy finishedSpy(&manager, SIGNAL(finished(QNetworkReply*)));
7365
7366 // Get with credentials, to preload authentication cache
7367 {
7368 QNetworkRequest request(QUrl("http://httptest:httptest@" + QtNetworkSettings::httpServerName() + "/qtest/protected/cgi-bin/md5sum.cgi"));
7369 QNetworkReplyPtr reply(manager.get(request));
7370 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
7371 // credentials in URL, so don't expect authentication signal
7372 QCOMPARE(authSpy.count(), 0);
7373 QCOMPARE(finishedSpy.count(), 1);
7374 finishedSpy.clear();
7375 }
7376
7377 // Get with cached credentials (normal usage)
7378 {
7379 QNetworkRequest request(QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/protected/cgi-bin/md5sum.cgi"));
7380 QNetworkReplyPtr reply(manager.get(request));
7381 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
7382 // credentials in cache, so don't expect authentication signal
7383 QCOMPARE(authSpy.count(), 0);
7384 QCOMPARE(finishedSpy.count(), 1);
7385 finishedSpy.clear();
7386 }
7387
7388 // Do not use cached credentials (webkit cross origin usage)
7389 {
7390 QNetworkRequest request(QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/protected/cgi-bin/md5sum.cgi"));
7391 request.setAttribute(code: QNetworkRequest::AuthenticationReuseAttribute, value: QNetworkRequest::Manual);
7392 QNetworkReplyPtr reply(manager.get(request));
7393
7394 QSignalSpy errorSpy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
7395
7396 connect(ptr: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()), ct: Qt::QueuedConnection);
7397 QTestEventLoop::instance().enterLoop(secs: 10);
7398 QVERIFY(!QTestEventLoop::instance().timeout());
7399
7400 // We check if authenticationRequired was emitted, however we do not anything in it so it should be 401
7401 QCOMPARE(authSpy.count(), 1);
7402 QCOMPARE(finishedSpy.count(), 1);
7403 QCOMPARE(errorSpy.count(), 1);
7404
7405 QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
7406 }
7407}
7408
7409void tst_QNetworkReply::qtbug15311doubleContentLength()
7410{
7411 QByteArray response("HTTP/1.0 200 OK\r\nContent-Length: 3\r\nServer: bogus\r\nContent-Length: 3\r\n\r\nABC");
7412 MiniHttpServer server(response);
7413 server.doClose = true;
7414
7415 QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
7416 QNetworkReplyPtr reply(manager.get(request));
7417
7418 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
7419
7420 QVERIFY(reply->isFinished());
7421 QCOMPARE(reply->error(), QNetworkReply::NoError);
7422 QCOMPARE(reply->size(), qint64(3));
7423 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(3));
7424 QCOMPARE(reply->rawHeader("Content-length"), QByteArray("3, 3"));
7425 QCOMPARE(reply->readAll(), QByteArray("ABC"));
7426}
7427
7428void tst_QNetworkReply::qtbug18232gzipContentLengthZero()
7429{
7430 QByteArray response("HTTP/1.0 200 OK\r\nContent-Encoding: gzip\r\nContent-Length: 0\r\n\r\n");
7431 MiniHttpServer server(response);
7432 server.doClose = true;
7433
7434 QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
7435 QNetworkReplyPtr reply(manager.get(request));
7436
7437 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
7438
7439 QVERIFY(reply->isFinished());
7440 QCOMPARE(reply->error(), QNetworkReply::NoError);
7441 QCOMPARE(reply->size(), qint64(0));
7442 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(0));
7443 QCOMPARE(reply->readAll(), QByteArray());
7444}
7445
7446// Reproduced a crash in QHttpNetworkReplyPrivate::gunzipBodyPartiallyEnd
7447// where zlib inflateEnd was called for uninitialized zlib stream
7448void tst_QNetworkReply::qtbug22660gzipNoContentLengthEmptyContent()
7449{
7450 // Response with no Content-Length in header and empty content
7451 QByteArray response("HTTP/1.0 200 OK\r\nContent-Encoding: gzip\r\n\r\n");
7452 MiniHttpServer server(response);
7453 server.doClose = true;
7454
7455 QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
7456 QNetworkReplyPtr reply(manager.get(request));
7457
7458 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
7459
7460 QVERIFY(reply->isFinished());
7461 QCOMPARE(reply->error(), QNetworkReply::NoError);
7462 QCOMPARE(reply->size(), qint64(0));
7463 QVERIFY(!reply->header(QNetworkRequest::ContentLengthHeader).isValid());
7464 QCOMPARE(reply->readAll(), QByteArray());
7465}
7466
7467class QtBug27161Helper : public QObject
7468{
7469 Q_OBJECT
7470public:
7471 QtBug27161Helper(MiniHttpServer & server, const QByteArray & data):
7472 m_server(server),
7473 m_data(data)
7474 {
7475 connect(sender: &m_server, SIGNAL(newConnection()), receiver: this, SLOT(newConnectionSlot()));
7476 }
7477public slots:
7478 void newConnectionSlot()
7479 {
7480 connect(sender: m_server.client, SIGNAL(bytesWritten(qint64)), receiver: this, SLOT(bytesWrittenSlot()));
7481 }
7482
7483 void bytesWrittenSlot()
7484 {
7485 disconnect(sender: m_server.client, SIGNAL(bytesWritten(qint64)), receiver: this, SLOT(bytesWrittenSlot()));
7486 m_Timer.singleShot(msec: 100, receiver: this, SLOT(timeoutSlot()));
7487 }
7488
7489 void timeoutSlot()
7490 {
7491 m_server.doClose = true;
7492 // we need to emulate the bytesWrittenSlot call if the data is empty.
7493 if (m_data.size() == 0)
7494 QMetaObject::invokeMethod(obj: &m_server, member: "bytesWrittenSlot", type: Qt::QueuedConnection);
7495 else
7496 m_server.client->write(data: m_data);
7497 }
7498
7499private:
7500 MiniHttpServer & m_server;
7501 QByteArray m_data;
7502 QTimer m_Timer;
7503};
7504
7505void tst_QNetworkReply::qtbug27161httpHeaderMayBeDamaged_data(){
7506 QByteArray response("HTTP/1.0 200 OK\r\nServer: bogus\r\nContent-Length: 3\r\n\r\nABC");
7507 QTest::addColumn<QByteArray>(name: "firstPacket");
7508 QTest::addColumn<QByteArray>(name: "secondPacket");
7509
7510 for (int i = 1; i < response.size(); i++){
7511 QByteArray dataTag("Iteration: ");
7512 dataTag.append(a: QByteArray::number(i - 1));
7513 QTest::newRow(dataTag: dataTag.constData()) << response.left(len: i) << response.mid(index: i);
7514 }
7515}
7516
7517/*
7518 * Purpose of this test is to check whether a content from server is parsed correctly
7519 * if it is split into two parts.
7520 */
7521void tst_QNetworkReply::qtbug27161httpHeaderMayBeDamaged(){
7522 QFETCH(QByteArray, firstPacket);
7523 QFETCH(QByteArray, secondPacket);
7524 MiniHttpServer server(firstPacket);
7525 server.doClose = false;
7526 QtBug27161Helper helper(server, secondPacket);
7527
7528 QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
7529 QNetworkReplyPtr reply(manager.get(request));
7530
7531 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
7532
7533 QVERIFY(reply->isFinished());
7534 QCOMPARE(reply->error(), QNetworkReply::NoError);
7535 QCOMPARE(reply->size(), qint64(3));
7536 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(3));
7537 QCOMPARE(reply->rawHeader("Content-length"), QByteArray("3"));
7538 QCOMPARE(reply->rawHeader("Server"), QByteArray("bogus"));
7539 QCOMPARE(reply->readAll(), QByteArray("ABC"));
7540}
7541
7542void tst_QNetworkReply::qtbug28035browserDoesNotLoadQtProjectOrgCorrectly() {
7543 QByteArray getReply =
7544 "HTTP/1.1 200\r\n"
7545 "Connection: keep-alive\r\n"
7546 "Content-Type: text/plain\r\n"
7547 "Cache-control: max-age = 6000\r\n"
7548 "\r\n"
7549 "GET";
7550
7551 QByteArray postReply =
7552 "HTTP/1.1 200\r\n"
7553 "Connection: keep-alive\r\n"
7554 "Content-Type: text/plain\r\n"
7555 "Cache-control: max-age = 6000\r\n"
7556 "Content-length: 4\r\n"
7557 "\r\n"
7558 "POST";
7559
7560 QByteArray putReply =
7561 "HTTP/1.1 201\r\n"
7562 "Connection: keep-alive\r\n"
7563 "Content-Type: text/plain\r\n"
7564 "Cache-control: max-age = 6000\r\n"
7565 "\r\n";
7566
7567 QByteArray postData = "ACT=100";
7568
7569 QTemporaryDir tempDir(QDir::tempPath() + "/tmp_cache_28035");
7570 QVERIFY2(tempDir.isValid(), qPrintable(tempDir.errorString()));
7571 tempDir.setAutoRemove(true);
7572
7573 QNetworkDiskCache *diskCache = new QNetworkDiskCache();
7574 diskCache->setCacheDirectory(tempDir.path());
7575 manager.setCache(diskCache);
7576
7577 MiniHttpServer server(getReply);
7578
7579 QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
7580 QNetworkReplyPtr reply(manager.get(request));
7581
7582 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
7583
7584 QVERIFY(reply->isFinished());
7585 QCOMPARE(reply->error(), QNetworkReply::NoError);
7586 QCOMPARE(reply->readAll(), QByteArray("GET"));
7587 QCOMPARE(reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(), false);
7588
7589 server.clearHeaderParserState();
7590 server.setDataToTransmit(getReply);
7591 reply.reset(t: manager.get(request));
7592 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
7593
7594 QVERIFY(reply->isFinished());
7595 QCOMPARE(reply->error(), QNetworkReply::NoError);
7596 QCOMPARE(reply->readAll(), QByteArray("GET"));
7597 QCOMPARE(reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(), true);
7598
7599 server.clearHeaderParserState();
7600 server.setDataToTransmit(postReply);
7601 request.setRawHeader(headerName: "Content-Type", value: "text/plain");
7602 reply.reset(t: manager.post(request, data: postData));
7603
7604 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
7605
7606 QVERIFY(reply->isFinished());
7607 QCOMPARE(reply->error(), QNetworkReply::NoError);
7608 QCOMPARE(reply->rawHeader("Content-length"), QByteArray("4"));
7609 QCOMPARE(reply->readAll(), QByteArray("POST"));
7610 QCOMPARE(reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(), false);
7611
7612 server.clearHeaderParserState();
7613 server.setDataToTransmit(getReply);
7614 reply.reset(t: manager.get(request));
7615
7616 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
7617
7618 QVERIFY(reply->isFinished());
7619 QCOMPARE(reply->error(), QNetworkReply::NoError);
7620 QCOMPARE(reply->readAll(), QByteArray("GET"));
7621 QCOMPARE(reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(), false);
7622
7623 server.clearHeaderParserState();
7624 server.setDataToTransmit(getReply);
7625 reply.reset(t: manager.get(request));
7626
7627 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
7628
7629 QVERIFY(reply->isFinished());
7630 QCOMPARE(reply->error(), QNetworkReply::NoError);
7631 QCOMPARE(reply->readAll(), QByteArray("GET"));
7632 QCOMPARE(reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(), true);
7633
7634 server.clearHeaderParserState();
7635 server.setDataToTransmit(putReply);
7636 reply.reset(t: manager.put(request, data: postData));
7637
7638 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
7639
7640 QVERIFY(reply->isFinished());
7641 QCOMPARE(reply->error(), QNetworkReply::NoError);
7642 QCOMPARE(reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(), false);
7643
7644 server.clearHeaderParserState();
7645 server.setDataToTransmit(getReply);
7646 reply.reset(t: manager.get(request));
7647
7648 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
7649
7650 QVERIFY(reply->isFinished());
7651 QCOMPARE(reply->error(), QNetworkReply::NoError);
7652 QCOMPARE(reply->readAll(), QByteArray("GET"));
7653 QCOMPARE(reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(), false);
7654
7655 server.clearHeaderParserState();
7656 server.setDataToTransmit(getReply);
7657 reply.reset(t: manager.get(request));
7658
7659 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
7660
7661 QVERIFY(reply->isFinished());
7662 QCOMPARE(reply->error(), QNetworkReply::NoError);
7663 QCOMPARE(reply->readAll(), QByteArray("GET"));
7664 QCOMPARE(reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(), true);
7665}
7666
7667void tst_QNetworkReply::qtbug45581WrongReplyStatusCode()
7668{
7669 const QUrl url("file:" + testDataDir + "/element.xml");
7670 QNetworkRequest request(url);
7671
7672 QNetworkReplyPtr reply;
7673 QSignalSpy finishedSpy(&manager, SIGNAL(finished(QNetworkReply*)));
7674 QSignalSpy sslErrorsSpy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
7675 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply, 0));
7676 QVERIFY(reply->isFinished());
7677
7678 const QByteArray expectedContent =
7679 "<root attr=\"value\" attr2=\"value2\">"
7680 "<person /><fruit /></root>"
7681#ifdef Q_OS_WIN
7682 "\r"
7683#endif
7684 "\n";
7685
7686 QCOMPARE(reply->readAll(), expectedContent);
7687
7688 QCOMPARE(finishedSpy.count(), 0);
7689 QCOMPARE(sslErrorsSpy.count(), 0);
7690
7691 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), expectedContent.size());
7692
7693 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
7694 QCOMPARE(reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(), QLatin1String("OK"));
7695
7696 reply->deleteLater();
7697}
7698
7699void tst_QNetworkReply::synchronousRequest_data()
7700{
7701 QTest::addColumn<QUrl>(name: "url");
7702 QTest::addColumn<QString>(name: "expected");
7703 QTest::addColumn<bool>(name: "checkContentLength");
7704 QTest::addColumn<QString>(name: "mimeType");
7705
7706 // ### cache, auth, proxies
7707
7708 QTest::newRow(dataTag: "http")
7709 << QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt")
7710 << QString("file:" + testDataDir + "/rfc3252.txt")
7711 << true
7712 << QString("text/plain");
7713
7714 QTest::newRow(dataTag: "http-gzip")
7715 << QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/deflate/rfc3252.txt")
7716 << QString("file:" + testDataDir + "/rfc3252.txt")
7717 << false // don't check content length, because it's gzip encoded
7718 // ### we would need to enflate (un-deflate) the file content and compare the sizes
7719 << QString("text/plain");
7720
7721#ifndef QT_NO_SSL
7722 QTest::newRow(dataTag: "https")
7723 << QUrl("https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt")
7724 << QString("file:" + testDataDir + "/rfc3252.txt")
7725 << true
7726 << QString("text/plain");
7727#endif
7728
7729 QTest::newRow(dataTag: "data")
7730 << QUrl(QString::fromLatin1(str: "data:text/plain,hello world"))
7731 << QString("data:hello world")
7732 << true // check content length
7733 << QString("text/plain");
7734
7735 QTest::newRow(dataTag: "simple-file")
7736 << QUrl::fromLocalFile(localfile: testDataDir + "/rfc3252.txt")
7737 << QString("file:" + testDataDir + "/rfc3252.txt")
7738 << true
7739 << QString();
7740}
7741
7742// FIXME add testcase for failing network etc
7743void tst_QNetworkReply::synchronousRequest()
7744{
7745 QFETCH(QUrl, url);
7746 QFETCH(QString, expected);
7747 QFETCH(bool, checkContentLength);
7748 QFETCH(QString, mimeType);
7749
7750 QNetworkRequest request(url);
7751
7752#ifndef QT_NO_SSL
7753 // workaround for HTTPS requests: add self-signed server cert to list of CA certs,
7754 // since we cannot react to the sslErrors() signal
7755 // to fix this properly we would need to have an ignoreSslErrors() method in the
7756 // QNetworkRequest, see QTBUG-14774
7757 if (url.scheme() == "https") {
7758 QSslConfiguration sslConf;
7759 QList<QSslCertificate> certs = QSslCertificate::fromPath(path: testDataDir + certsFilePath);
7760 sslConf.setCaCertificates(certs);
7761 request.setSslConfiguration(sslConf);
7762 }
7763#endif
7764
7765 request.setAttribute(
7766 code: QNetworkRequest::SynchronousRequestAttribute,
7767 value: true);
7768
7769 QNetworkReplyPtr reply;
7770 QSignalSpy finishedSpy(&manager, SIGNAL(finished(QNetworkReply*)));
7771 QSignalSpy sslErrorsSpy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
7772 RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply, 0));
7773 QVERIFY(reply->isFinished());
7774 QCOMPARE(finishedSpy.count(), 0);
7775 QCOMPARE(sslErrorsSpy.count(), 0);
7776
7777 QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader).toString(), mimeType);
7778
7779 QByteArray expectedContent;
7780
7781 if (expected.startsWith(s: "file:")) {
7782 QString path = expected.mid(position: 5);
7783 QFile file(path);
7784 file.open(flags: QIODevice::ReadOnly);
7785 expectedContent = file.readAll();
7786 } else if (expected.startsWith(s: "data:")) {
7787 expectedContent = expected.mid(position: 5).toUtf8();
7788 }
7789
7790 if (checkContentLength)
7791 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(expectedContent.size()));
7792 QCOMPARE(reply->readAll(), expectedContent);
7793
7794 reply->deleteLater();
7795}
7796
7797#ifndef QT_NO_SSL
7798void tst_QNetworkReply::synchronousRequestSslFailure()
7799{
7800 // test that SSL won't be accepted with self-signed certificate,
7801 // and that we do not emit the sslError signal (in the manager that is,
7802 // in the reply we don't care)
7803
7804 QUrl url("https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt");
7805 QNetworkRequest request(url);
7806 request.setAttribute(
7807 code: QNetworkRequest::SynchronousRequestAttribute,
7808 value: true);
7809 QNetworkReplyPtr reply;
7810 QSignalSpy sslErrorsSpy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
7811 runSimpleRequest(op: QNetworkAccessManager::GetOperation, request, reply, data: 0);
7812 QVERIFY(reply->isFinished());
7813 QCOMPARE(reply->error(), QNetworkReply::SslHandshakeFailedError);
7814 QCOMPARE(sslErrorsSpy.count(), 0);
7815}
7816#endif
7817
7818class HttpAbortHelper : public QObject
7819{
7820 Q_OBJECT
7821public:
7822 HttpAbortHelper(QNetworkReply *parent)
7823 : QObject(parent)
7824 {
7825 mReply = parent;
7826 connect(sender: parent, SIGNAL(readyRead()), receiver: this, SLOT(readyRead()));
7827 }
7828
7829 ~HttpAbortHelper()
7830 {
7831 }
7832
7833public slots:
7834 void readyRead()
7835 {
7836 mReply->abort();
7837 QMetaObject::invokeMethod(obj: &QTestEventLoop::instance(), member: "exitLoop", type: Qt::QueuedConnection);
7838 }
7839
7840private:
7841 QNetworkReply *mReply;
7842};
7843
7844void tst_QNetworkReply::httpAbort()
7845{
7846 // FIXME Also implement one where we do a big upload and then abort().
7847 // It must not crash either.
7848
7849 // Abort after the first readyRead()
7850 QNetworkRequest request("http://" + QtNetworkSettings::httpServerName() + "/qtest/bigfile");
7851 QNetworkReplyPtr reply(manager.get(request));
7852 HttpAbortHelper replyHolder(reply.data());
7853 QTestEventLoop::instance().enterLoop(secs: 10);
7854 QVERIFY(!QTestEventLoop::instance().timeout());
7855 QCOMPARE(reply->error(), QNetworkReply::OperationCanceledError);
7856 QVERIFY(reply->isFinished());
7857
7858 // Abort immediately after the get()
7859 QNetworkReplyPtr reply2(manager.get(request));
7860 connect(ptr: reply2, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
7861 reply2->abort();
7862 QCOMPARE(reply2->error(), QNetworkReply::OperationCanceledError);
7863 QVERIFY(reply2->isFinished());
7864
7865 // Abort after the finished()
7866 QNetworkRequest request3("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt");
7867 QNetworkReplyPtr reply3(manager.get(request: request3));
7868
7869 QCOMPARE(waitForFinish(reply3), int(Success));
7870
7871 QVERIFY(reply3->isFinished());
7872 reply3->abort();
7873 QCOMPARE(reply3->error(), QNetworkReply::NoError);
7874}
7875
7876void tst_QNetworkReply::dontInsertPartialContentIntoTheCache()
7877{
7878 QByteArray reply206 =
7879 "HTTP/1.0 206\r\n"
7880 "Connection: keep-alive\r\n"
7881 "Content-Type: text/plain\r\n"
7882 "Cache-control: no-cache\r\n"
7883 "Content-Range: bytes 2-6/8\r\n"
7884 "Content-length: 4\r\n"
7885 "\r\n"
7886 "load";
7887
7888 MiniHttpServer server(reply206);
7889 server.doClose = false;
7890
7891 MySpyMemoryCache *memoryCache = new MySpyMemoryCache(&manager);
7892 manager.setCache(memoryCache);
7893
7894 QUrl url = "http://localhost:" + QString::number(server.serverPort());
7895 QNetworkRequest request(url);
7896 request.setRawHeader(headerName: "Range", value: "bytes=2-6");
7897
7898 QNetworkReplyPtr reply(manager.get(request));
7899
7900 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
7901
7902 QVERIFY(server.totalConnections > 0);
7903 QCOMPARE(reply->readAll().constData(), "load");
7904 QCOMPARE(memoryCache->m_insertedUrls.count(), 0);
7905}
7906
7907void tst_QNetworkReply::httpUserAgent()
7908{
7909 QByteArray response("HTTP/1.0 200 OK\r\n\r\n");
7910 MiniHttpServer server(response);
7911 server.doClose = true;
7912
7913 QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
7914 request.setHeader(header: QNetworkRequest::UserAgentHeader, value: "abcDEFghi");
7915 QNetworkReplyPtr reply(manager.get(request));
7916
7917 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
7918
7919 QVERIFY(reply->isFinished());
7920 QCOMPARE(reply->error(), QNetworkReply::NoError);
7921 QVERIFY(server.receivedData.contains("\r\nUser-Agent: abcDEFghi\r\n"));
7922}
7923
7924void tst_QNetworkReply::synchronousAuthenticationCache()
7925{
7926 class MiniAuthServer : public MiniHttpServer
7927 {
7928 public:
7929 MiniAuthServer(QThread *thread) : MiniHttpServer(QByteArray(), false, thread) {}
7930 virtual void reply()
7931 {
7932
7933 dataToTransmit =
7934 "HTTP/1.0 401 Unauthorized\r\n"
7935 "WWW-Authenticate: Basic realm=\"QNetworkAccessManager Test Realm\"\r\n"
7936 "Content-Length: 4\r\n"
7937 "Connection: close\r\n"
7938 "Content-Type: text/plain\r\n"
7939 "\r\n"
7940 "auth";
7941 QRegularExpression rx("Authorization: Basic ([^\r\n]*)\r\n");
7942 QRegularExpressionMatch match = rx.match(subject: receivedData);
7943 if (match.hasMatch()) {
7944 if (QByteArray::fromBase64(base64: match.captured(nth: 1).toLatin1()) == "login:password") {
7945 dataToTransmit =
7946 "HTTP/1.0 200 OK\r\n"
7947 "Content-Type: text/plain\r\n"
7948 "Content-Length: 2\r\n"
7949 "\r\n"
7950 "OK";
7951 }
7952 }
7953 receivedData.clear();
7954 MiniHttpServer::reply();
7955 }
7956 };
7957
7958 // when using synchronous commands, we need a different event loop for
7959 // the server thread, because the client is never returning to the
7960 // event loop
7961 QScopedPointer<QThread, QThreadCleanup> serverThread(new QThread);
7962 QScopedPointer<MiniHttpServer, QDeleteLaterCleanup> server(new MiniAuthServer(serverThread.data()));
7963 server->doClose = true;
7964
7965 //1) URL without credentials, we are not authenticated
7966 {
7967 QUrl url = "http://localhost:" + QString::number(server->serverPort()) + "/path";
7968 QNetworkRequest request(url);
7969 request.setAttribute(code: QNetworkRequest::SynchronousRequestAttribute, value: true);
7970
7971 QNetworkReplyPtr reply(manager.get(request));
7972 QVERIFY(reply->isFinished());
7973 QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
7974 }
7975
7976 //2) URL with credentials, we are authenticated
7977 {
7978 QUrl url = "http://login:password@localhost:" + QString::number(server->serverPort()) + "/path2";
7979 QNetworkRequest request(url);
7980 request.setAttribute(code: QNetworkRequest::SynchronousRequestAttribute, value: true);
7981
7982 QNetworkReplyPtr reply(manager.get(request));
7983 QVERIFY(reply->isFinished());
7984 QCOMPARE(reply->error(), QNetworkReply::NoError);
7985 QCOMPARE(reply->readAll().constData(), "OK");
7986 }
7987
7988 //3) URL without credentials, we are authenticated because they are cached
7989 {
7990 QUrl url = "http://localhost:" + QString::number(server->serverPort()) + "/path3";
7991 QNetworkRequest request(url);
7992 request.setAttribute(code: QNetworkRequest::SynchronousRequestAttribute, value: true);
7993
7994 QNetworkReplyPtr reply(manager.get(request));
7995 QVERIFY(reply->isFinished());
7996 QCOMPARE(reply->error(), QNetworkReply::NoError);
7997 QCOMPARE(reply->readAll().constData(), "OK");
7998 }
7999}
8000
8001void tst_QNetworkReply::pipelining()
8002{
8003 QString urlString("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/echo.cgi?");
8004 QList<QNetworkReplyPtr> replies;
8005 for (int a = 0; a < 20; a++) {
8006 QNetworkRequest request(urlString + QString::number(a));
8007 request.setAttribute(code: QNetworkRequest::HttpPipeliningAllowedAttribute, value: QVariant(true));
8008 replies.append(t: QNetworkReplyPtr(manager.get(request)));
8009 connect(ptr: replies.at(i: a), SIGNAL(finished()), receiver: this, SLOT(pipeliningHelperSlot()));
8010 }
8011 QTestEventLoop::instance().enterLoop(secs: 20);
8012 QVERIFY(!QTestEventLoop::instance().timeout());
8013}
8014
8015void tst_QNetworkReply::pipeliningHelperSlot() {
8016 static int a = 0;
8017
8018 // check that pipelining was used in at least one of the replies
8019 static bool pipeliningWasUsed = false;
8020 QNetworkReply *reply = qobject_cast<QNetworkReply *>(object: sender());
8021 bool pipeliningWasUsedInReply = reply->attribute(code: QNetworkRequest::HttpPipeliningWasUsedAttribute).toBool();
8022 if (pipeliningWasUsedInReply)
8023 pipeliningWasUsed = true;
8024
8025 // check that the contents match (the response to echo.cgi?3 should return 3 etc.)
8026 QString urlQueryString = reply->url().query();
8027 QString content = reply->readAll();
8028 QVERIFY2(urlQueryString == content, "data corruption with pipelining detected");
8029
8030 a++;
8031
8032 if (a == 20) { // all replies have finished
8033 QTestEventLoop::instance().exitLoop();
8034 QVERIFY2(pipeliningWasUsed, "pipelining was not used in any of the replies when trying to test pipelining");
8035 }
8036}
8037
8038void tst_QNetworkReply::emitErrorForAllRepliesSlot() {
8039 static int a = 0;
8040 if (++a == 3)
8041 QTestEventLoop::instance().exitLoop();
8042}
8043
8044void tst_QNetworkReply::closeDuringDownload_data()
8045{
8046 QTest::addColumn<QUrl>(name: "url");
8047 QTest::newRow(dataTag: "http") << QUrl("http://" + QtNetworkSettings::httpServerName() + "/bigfile");
8048 QTest::newRow(dataTag: "ftp") << QUrl("ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/bigfile");
8049}
8050
8051void tst_QNetworkReply::closeDuringDownload()
8052{
8053 QFETCH(QUrl, url);
8054 QNetworkRequest request(url);
8055 QNetworkReply* reply = manager.get(request);
8056 QSignalSpy readyReadSpy(reply, &QNetworkReply::readyRead);
8057 QVERIFY(readyReadSpy.wait(10000));
8058 QSignalSpy destroySpy(reply, &QObject::destroyed);
8059 reply->close();
8060 reply->deleteLater();
8061 // Wait for destruction to avoid a warning caused by test's cleanup()
8062 // destroying the connection cache before the abort is finished
8063 QVERIFY(destroySpy.wait());
8064}
8065
8066void tst_QNetworkReply::ftpAuthentication_data()
8067{
8068 QTest::addColumn<QString>(name: "referenceName");
8069 QTest::addColumn<QString>(name: "url");
8070 QTest::addColumn<int>(name: "error");
8071
8072 QTest::newRow(dataTag: "invalidPassword") << (testDataDir + "/rfc3252.txt") << "ftp://ftptest:invalid@" + QtNetworkSettings::ftpServerName() + "/home/qt-test-server/ftp/qtest/rfc3252.txt" << int(QNetworkReply::AuthenticationRequiredError);
8073 QTest::newRow(dataTag: "validPassword") << (testDataDir + "/rfc3252.txt") << "ftp://ftptest:password@" + QtNetworkSettings::ftpServerName() + "/home/qt-test-server/ftp/qtest/rfc3252.txt" << int(QNetworkReply::NoError);
8074}
8075
8076void tst_QNetworkReply::ftpAuthentication()
8077{
8078 QFETCH(QString, referenceName);
8079 QFETCH(QString, url);
8080 QFETCH(int, error);
8081
8082 QFile reference(referenceName);
8083 QVERIFY(reference.open(QIODevice::ReadOnly));
8084
8085 QNetworkRequest request(url);
8086 QNetworkReplyPtr reply;
8087 runSimpleRequest(op: QNetworkAccessManager::GetOperation, request, reply);
8088
8089 QCOMPARE(reply->url(), request.url());
8090 QCOMPARE(reply->error(), QNetworkReply::NetworkError(error));
8091}
8092
8093void tst_QNetworkReply::emitErrorForAllReplies() // QTBUG-36890
8094{
8095 // port 100 is not well-known and should be closed
8096 QList<QUrl> urls = QList<QUrl>() << QUrl("http://localhost:100/request1")
8097 << QUrl("http://localhost:100/request2")
8098 << QUrl("http://localhost:100/request3");
8099 QList<QNetworkReply *> replies;
8100 QList<QSignalSpy *> errorSpies;
8101 QList<QSignalSpy *> finishedSpies;
8102 for (int a = 0; a < urls.count(); ++a) {
8103 QNetworkRequest request(urls.at(i: a));
8104 QNetworkReply *reply = manager.get(request);
8105 replies.append(t: reply);
8106 QSignalSpy *errorSpy = new QSignalSpy(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
8107 errorSpies.append(t: errorSpy);
8108 QSignalSpy *finishedSpy = new QSignalSpy(reply, SIGNAL(finished()));
8109 finishedSpies.append(t: finishedSpy);
8110 QObject::connect(asender: reply, SIGNAL(finished()), SLOT(emitErrorForAllRepliesSlot()));
8111 }
8112 QTestEventLoop::instance().enterLoop(secs: 10);
8113 QVERIFY(!QTestEventLoop::instance().timeout());
8114 for (int a = 0; a < urls.count(); ++a) {
8115 QVERIFY(replies.at(a)->isFinished());
8116 QCOMPARE(errorSpies.at(a)->count(), 1);
8117 errorSpies.at(i: a)->deleteLater();
8118 QCOMPARE(finishedSpies.at(a)->count(), 1);
8119 finishedSpies.at(i: a)->deleteLater();
8120 replies.at(i: a)->deleteLater();
8121 }
8122}
8123
8124#ifdef QT_BUILD_INTERNAL
8125void tst_QNetworkReply::backgroundRequest_data()
8126{
8127#ifndef QT_NO_BEARERMANAGEMENT
8128 QTest::addColumn<QUrl>(name: "url");
8129 QTest::addColumn<bool>(name: "background");
8130 QTest::addColumn<int>(name: "policy");
8131 QTest::addColumn<QNetworkReply::NetworkError>(name: "error");
8132
8133 QUrl httpurl("http://" + QtNetworkSettings::httpServerName());
8134 QUrl httpsurl("https://" + QtNetworkSettings::httpServerName());
8135 QUrl ftpurl("ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/rfc3252.txt");
8136
8137 QTest::newRow(dataTag: "http, fg, normal") << httpurl << false << (int)QNetworkSession::NoPolicy << QNetworkReply::NoError;
8138 QTest::newRow(dataTag: "http, bg, normal") << httpurl << true << (int)QNetworkSession::NoPolicy << QNetworkReply::NoError;
8139 QTest::newRow(dataTag: "http, fg, nobg") << httpurl << false << (int)QNetworkSession::NoBackgroundTrafficPolicy << QNetworkReply::NoError;
8140 QTest::newRow(dataTag: "http, bg, nobg") << httpurl << true << (int)QNetworkSession::NoBackgroundTrafficPolicy << QNetworkReply::BackgroundRequestNotAllowedError;
8141
8142#ifndef QT_NO_SSL
8143 QTest::newRow(dataTag: "https, fg, normal") << httpsurl << false << (int)QNetworkSession::NoPolicy << QNetworkReply::NoError;
8144 QTest::newRow(dataTag: "https, bg, normal") << httpsurl << true << (int)QNetworkSession::NoPolicy << QNetworkReply::NoError;
8145 QTest::newRow(dataTag: "https, fg, nobg") << httpsurl << false << (int)QNetworkSession::NoBackgroundTrafficPolicy << QNetworkReply::NoError;
8146 QTest::newRow(dataTag: "https, bg, nobg") << httpsurl << true << (int)QNetworkSession::NoBackgroundTrafficPolicy << QNetworkReply::BackgroundRequestNotAllowedError;
8147#endif
8148
8149 QTest::newRow(dataTag: "ftp, fg, normal") << ftpurl << false << (int)QNetworkSession::NoPolicy << QNetworkReply::NoError;
8150 QTest::newRow(dataTag: "ftp, bg, normal") << ftpurl << true << (int)QNetworkSession::NoPolicy << QNetworkReply::NoError;
8151 QTest::newRow(dataTag: "ftp, fg, nobg") << ftpurl << false << (int)QNetworkSession::NoBackgroundTrafficPolicy << QNetworkReply::NoError;
8152 QTest::newRow(dataTag: "ftp, bg, nobg") << ftpurl << true << (int)QNetworkSession::NoBackgroundTrafficPolicy << QNetworkReply::BackgroundRequestNotAllowedError;
8153#endif // !QT_NO_BEARERMANAGEMENT
8154}
8155#endif
8156
8157//test purpose: background requests can't be started when not allowed
8158#ifdef QT_BUILD_INTERNAL
8159void tst_QNetworkReply::backgroundRequest()
8160{
8161#ifndef QT_NO_BEARERMANAGEMENT
8162 QFETCH(QUrl, url);
8163 QFETCH(bool, background);
8164 QFETCH(int, policy);
8165 QFETCH(QNetworkReply::NetworkError, error);
8166
8167 QNetworkRequest request(url);
8168
8169 if (background)
8170 request.setAttribute(code: QNetworkRequest::BackgroundRequestAttribute, value: QVariant::fromValue(value: true));
8171
8172 //this preconstructs the session so we can change policies in advance
8173 manager.setConfiguration(networkConfiguration);
8174
8175#ifndef QT_NO_SSL
8176 connect(asender: &manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
8177 SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
8178#endif
8179
8180 const QWeakPointer<const QNetworkSession> sessionWeakPtr = QNetworkAccessManagerPrivate::getNetworkSession(manager: &manager);
8181 QVERIFY(!sessionWeakPtr.isNull());
8182 auto session = const_cast<QNetworkSession *>(sessionWeakPtr.toStrongRef().data());
8183 QNetworkSession::UsagePolicies original = session->usagePolicies();
8184 QNetworkSessionPrivate::setUsagePolicies(*session, QNetworkSession::UsagePolicies(policy));
8185
8186 QNetworkReplyPtr reply(manager.get(request));
8187
8188 QVERIFY(waitForFinish(reply) != Timeout);
8189 if (session)
8190 QNetworkSessionPrivate::setUsagePolicies(*session, original);
8191
8192 QVERIFY(reply->isFinished());
8193 QCOMPARE(reply->error(), error);
8194#endif
8195}
8196#endif
8197
8198#ifdef QT_BUILD_INTERNAL
8199void tst_QNetworkReply::backgroundRequestInterruption_data()
8200{
8201 QTest::addColumn<QUrl>(name: "url");
8202 QTest::addColumn<bool>(name: "background");
8203 QTest::addColumn<QNetworkReply::NetworkError>(name: "error");
8204
8205 QUrl httpurl("http://" + QtNetworkSettings::httpServerName() + "/qtest/mediumfile");
8206 QUrl httpsurl("https://" + QtNetworkSettings::httpServerName() + "/qtest/mediumfile");
8207 QUrl ftpurl("ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/bigfile");
8208
8209 QTest::newRow(dataTag: "http, fg, nobg") << httpurl << false << QNetworkReply::NoError;
8210 QTest::newRow(dataTag: "http, bg, nobg") << httpurl << true << QNetworkReply::BackgroundRequestNotAllowedError;
8211
8212#ifndef QT_NO_SSL
8213 QTest::newRow(dataTag: "https, fg, nobg") << httpsurl << false << QNetworkReply::NoError;
8214 QTest::newRow(dataTag: "https, bg, nobg") << httpsurl << true << QNetworkReply::BackgroundRequestNotAllowedError;
8215#endif
8216
8217 QTest::newRow(dataTag: "ftp, fg, nobg") << ftpurl << false << QNetworkReply::NoError;
8218 QTest::newRow(dataTag: "ftp, bg, nobg") << ftpurl << true << QNetworkReply::BackgroundRequestNotAllowedError;
8219
8220}
8221#endif
8222
8223//test purpose: background requests in progress are aborted when policy changes to disallow them
8224#ifdef QT_BUILD_INTERNAL
8225void tst_QNetworkReply::backgroundRequestInterruption()
8226{
8227#ifndef QT_NO_BEARERMANAGEMENT
8228 if (QNetworkStatusMonitor::isEnabled() && QByteArray(QTest::currentDataTag()).startsWith(c: "http"))
8229 QSKIP("This test (currently) doesn't make any sense when QNetworkStatusMonitor is enabled");
8230
8231 QFETCH(QUrl, url);
8232 QFETCH(bool, background);
8233 QFETCH(QNetworkReply::NetworkError, error);
8234
8235 QNetworkRequest request(url);
8236
8237 if (background)
8238 request.setAttribute(code: QNetworkRequest::BackgroundRequestAttribute, value: QVariant::fromValue(value: true));
8239
8240 //this preconstructs the session so we can change policies in advance
8241 manager.setConfiguration(networkConfiguration);
8242
8243#ifndef QT_NO_SSL
8244 connect(asender: &manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
8245 SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
8246#endif
8247
8248 const QWeakPointer<const QNetworkSession> sessionWeakPtr = QNetworkAccessManagerPrivate::getNetworkSession(manager: &manager);
8249 QVERIFY(!sessionWeakPtr.isNull());
8250 auto session = const_cast<QNetworkSession *>(sessionWeakPtr.toStrongRef().data());
8251 QNetworkSession::UsagePolicies original = session->usagePolicies();
8252 QNetworkSessionPrivate::setUsagePolicies(*session, QNetworkSession::NoPolicy);
8253
8254 request.setAttribute(code: QNetworkRequest::MaximumDownloadBufferSizeAttribute, value: 8192);
8255 QNetworkReplyPtr reply(manager.get(request));
8256 reply->setReadBufferSize(1024);
8257
8258 QSignalSpy spy(reply.data(), SIGNAL(readyRead()));
8259 QTRY_VERIFY(spy.count() > 0);
8260
8261 QNetworkSessionPrivate::setUsagePolicies(*session, QNetworkSession::NoBackgroundTrafficPolicy);
8262
8263 // After we have changed the policy we can download at full speed.
8264 reply->setReadBufferSize(0);
8265
8266 QVERIFY(waitForFinish(reply) != Timeout);
8267 if (session)
8268 QNetworkSessionPrivate::setUsagePolicies(*session, original);
8269
8270 QVERIFY(reply->isFinished());
8271 QCOMPARE(reply->error(), error);
8272#endif
8273}
8274#endif
8275
8276#ifdef QT_BUILD_INTERNAL
8277void tst_QNetworkReply::backgroundRequestConnectInBackground_data()
8278{
8279 QTest::addColumn<QUrl>(name: "url");
8280 QTest::addColumn<bool>(name: "background");
8281
8282 QUrl httpurl("http://" + QtNetworkSettings::httpServerName());
8283 QUrl ftpurl("ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/rfc3252.txt");
8284
8285 QTest::newRow(dataTag: "http, fg") << httpurl << false;
8286 QTest::newRow(dataTag: "http, bg") << httpurl << true;
8287
8288 QTest::newRow(dataTag: "ftp, fg") << ftpurl << false;
8289 QTest::newRow(dataTag: "ftp, bg") << ftpurl << true;
8290}
8291#endif
8292
8293//test purpose: check that backgroundness is propagated to the network session
8294#ifdef QT_BUILD_INTERNAL
8295void tst_QNetworkReply::backgroundRequestConnectInBackground()
8296{
8297#ifndef QT_NO_BEARERMANAGEMENT
8298 QFETCH(QUrl, url);
8299 QFETCH(bool, background);
8300
8301 QNetworkRequest request(url);
8302
8303 if (background)
8304 request.setAttribute(code: QNetworkRequest::BackgroundRequestAttribute, value: QVariant::fromValue(value: true));
8305
8306 QWeakPointer<const QNetworkSession> session = QNetworkAccessManagerPrivate::getNetworkSession(manager: &manager);
8307 //force QNAM to reopen the session.
8308 if (session && session.toStrongRef().data()->isOpen()) {
8309 const_cast<QNetworkSession *>(session.toStrongRef().data())->close();
8310 QCoreApplication::processEvents(); //let signals propagate inside QNAM
8311 }
8312
8313 //this preconstructs the session so we can change policies in advance
8314 manager.setConfiguration(networkConfiguration);
8315
8316 session = QNetworkAccessManagerPrivate::getNetworkSession(manager: &manager);
8317 QVERIFY(session);
8318 QNetworkSession::UsagePolicies original = session.toStrongRef().data()->usagePolicies();
8319 QNetworkSessionPrivate::setUsagePolicies(*const_cast<QNetworkSession *>(session.toStrongRef().data()), QNetworkSession::NoPolicy);
8320
8321 QNetworkReplyPtr reply(manager.get(request));
8322
8323 QVERIFY(waitForFinish(reply) != Timeout);
8324 session = QNetworkAccessManagerPrivate::getNetworkSession(manager: &manager);
8325 if (session) {
8326 QVariant cib = session.toStrongRef().data()->sessionProperty(QStringLiteral("ConnectInBackground"));
8327 if (!cib.isValid())
8328 QSKIP("inconclusive - ConnectInBackground session property not supported by the bearer plugin");
8329 QCOMPARE(cib.toBool(), background);
8330 QNetworkSessionPrivate::setUsagePolicies(*const_cast<QNetworkSession *>(session.toStrongRef().data()), original);
8331 } else {
8332 QSKIP("inconclusive - network session has been destroyed");
8333 }
8334
8335 QVERIFY(reply->isFinished());
8336#endif
8337}
8338#endif
8339
8340class RateLimitedUploadDevice : public QIODevice
8341{
8342 Q_OBJECT
8343public:
8344 QByteArray data;
8345 QBuffer buffer;
8346 qint64 read;
8347 qint64 bandwidthQuota;
8348 QTimer timer;
8349
8350 RateLimitedUploadDevice(QByteArray d) : QIODevice(),data(d),read(0),bandwidthQuota(0)
8351 {
8352 buffer.setData(data);
8353 buffer.open(openMode: QIODevice::ReadOnly);
8354 timer.setInterval(200);
8355 QObject::connect(sender: &timer, SIGNAL(timeout()), receiver: this, SLOT(timeoutSlot()));
8356 timer.start();
8357 }
8358
8359 virtual qint64 writeData(const char* , qint64 )
8360 {
8361 Q_ASSERT(false);
8362 return 0;
8363 }
8364
8365 virtual qint64 readData(char* data, qint64 maxlen)
8366 {
8367 //qDebug() << Q_FUNC_INFO << maxlen << bandwidthQuota;
8368 maxlen = qMin(a: maxlen, b: buffer.bytesAvailable());
8369 maxlen = qMin(a: maxlen, b: bandwidthQuota);
8370 if (maxlen <= 0) { // no quota or at end
8371 return 0;
8372 }
8373 bandwidthQuota -= maxlen; // reduce quota
8374
8375 qint64 ret = buffer.read(data, maxlen);
8376 if (ret == -1) {
8377 return -1;
8378 }
8379 read += ret;
8380 //qDebug() << Q_FUNC_INFO << maxlen << bandwidthQuota << read << ret << buffer.bytesAvailable();
8381 return ret;
8382 }
8383 virtual bool atEnd() const { return buffer.atEnd(); }
8384 virtual qint64 size() const { return data.length(); }
8385 qint64 bytesAvailable() const
8386 {
8387 return buffer.bytesAvailable() + QIODevice::bytesAvailable();
8388 }
8389 virtual bool isSequential() const { return false; } // random access, we can seek
8390 virtual bool seek (qint64 pos) { return buffer.seek(off: pos); }
8391protected slots:
8392 void timeoutSlot()
8393 {
8394 //qDebug() << Q_FUNC_INFO;
8395 bandwidthQuota = 8*1024; // fill quota
8396 emit readyRead();
8397 // Emitting readyRead() several times triggers a bug ("QIODevice::read: Called with maxSize < 0") we fix with this commit
8398 emit readyRead();
8399 }
8400};
8401
8402void tst_QNetworkReply::putWithRateLimiting()
8403{
8404 QFile reference(testDataDir + "/rfc3252.txt");
8405 reference.open(flags: QIODevice::ReadOnly);
8406 QByteArray data = reference.readAll();
8407 QVERIFY(data.length() > 0);
8408
8409 QUrl url = QUrl::fromUserInput(userInput: "http://" + QtNetworkSettings::httpServerName()+ "/qtest/cgi-bin/echo.cgi?");
8410
8411 QNetworkRequest request(url);
8412 QNetworkReplyPtr reply;
8413
8414 RateLimitedUploadDevice rateLimitedUploadDevice(data);
8415 rateLimitedUploadDevice.open(mode: QIODevice::ReadOnly);
8416
8417 RUN_REQUEST(runCustomRequest(request, reply,QByteArray("POST"), &rateLimitedUploadDevice));
8418 QCOMPARE(reply->error(), QNetworkReply::NoError);
8419 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
8420
8421 QByteArray uploadedData = reply->readAll();
8422 QCOMPARE(uploadedData.length(), data.length());
8423 QCOMPARE(uploadedData, data);
8424}
8425
8426void tst_QNetworkReply::ioHttpSingleRedirect()
8427{
8428 QUrl localhost = QUrl("http://localhost");
8429
8430 // Setup server to which the second server will redirect to
8431 MiniHttpServer server2(httpEmpty200Response);
8432
8433 QUrl redirectUrl = QUrl(localhost);
8434 redirectUrl.setPort(server2.serverPort());
8435
8436 QByteArray tempRedirectReply =
8437 tempRedirectReplyStr().arg(a: QString(redirectUrl.toEncoded())).toLatin1();
8438
8439
8440 // Setup redirect server
8441 MiniHttpServer server(tempRedirectReply);
8442
8443 localhost.setPort(server.serverPort());
8444 QNetworkRequest request(localhost);
8445 request.setAttribute(code: QNetworkRequest::FollowRedirectsAttribute, value: true);
8446
8447 QNetworkReplyPtr reply(manager.get(request));
8448 QSignalSpy redSpy(reply.data(), SIGNAL(redirected(QUrl)));
8449 QSignalSpy finSpy(reply.data(), SIGNAL(finished()));
8450
8451 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
8452
8453 // Redirected and finished should be emitted exactly once
8454 QCOMPARE(redSpy.count(), 1);
8455 QCOMPARE(finSpy.count(), 1);
8456
8457 // Original URL should not be changed after redirect
8458 QCOMPARE(request.url(), localhost);
8459
8460 // Verify Redirect url
8461 QList<QVariant> args = redSpy.takeFirst();
8462 QCOMPARE(args.at(0).toUrl(), redirectUrl);
8463
8464 // Reply url is set to the redirect url
8465 QCOMPARE(reply->url(), redirectUrl);
8466 QCOMPARE(reply->error(), QNetworkReply::NoError);
8467 QVERIFY(validateRedirectedResponseHeaders(reply));
8468}
8469
8470void tst_QNetworkReply::ioHttpChangeMaxRedirects()
8471{
8472 QUrl localhost = QUrl("http://localhost");
8473
8474 MiniHttpServer server1("");
8475 MiniHttpServer server2("");
8476 MiniHttpServer server3(httpEmpty200Response);
8477
8478 QUrl server2Url(localhost);
8479 server2Url.setPort(server2.serverPort());
8480 server1.setDataToTransmit(tempRedirectReplyStr().arg(
8481 a: QString(server2Url.toEncoded())).toLatin1());
8482
8483 QUrl server3Url(localhost);
8484 server3Url.setPort(server3.serverPort());
8485 server2.setDataToTransmit(tempRedirectReplyStr().arg(
8486 a: QString(server3Url.toEncoded())).toLatin1());
8487
8488 localhost.setPort(server1.serverPort());
8489 QNetworkRequest request(localhost);
8490 request.setAttribute(code: QNetworkRequest::FollowRedirectsAttribute, value: true);
8491
8492 // Set Max redirects to 1. This will cause TooManyRedirectsError
8493 request.setMaximumRedirectsAllowed(1);
8494
8495 QNetworkReplyPtr reply(manager.get(request));
8496 QSignalSpy redSpy(reply.data(), SIGNAL(redirected(QUrl)));
8497 QSignalSpy spy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
8498
8499 QCOMPARE(waitForFinish(reply), int(Failure));
8500
8501 QCOMPARE(redSpy.count(), request.maximumRedirectsAllowed());
8502 QCOMPARE(spy.count(), 1);
8503 QCOMPARE(reply->error(), QNetworkReply::TooManyRedirectsError);
8504
8505 // Increase max redirects to allow successful completion
8506 request.setMaximumRedirectsAllowed(3);
8507
8508 QNetworkReplyPtr reply2(manager.get(request));
8509 QSignalSpy redSpy2(reply2.data(), SIGNAL(redirected(QUrl)));
8510
8511 QVERIFY2(waitForFinish(reply2) == Success, msgWaitForFinished(reply2));
8512
8513 QCOMPARE(redSpy2.count(), 2);
8514 QCOMPARE(reply2->url(), server3Url);
8515 QCOMPARE(reply2->error(), QNetworkReply::NoError);
8516 QVERIFY(validateRedirectedResponseHeaders(reply2));
8517}
8518
8519void tst_QNetworkReply::ioHttpRedirectErrors_data()
8520{
8521 QTest::addColumn<QString>(name: "url");
8522 QTest::addColumn<QString>(name: "dataToSend");
8523 QTest::addColumn<QNetworkReply::NetworkError>(name: "error");
8524
8525 QString tempRedirectReply = QString("HTTP/1.1 307 Temporary Redirect\r\n"
8526 "Content-Type: text/plain\r\n"
8527 "location: http://localhost:%1\r\n\r\n");
8528
8529 QTest::newRow(dataTag: "too-many-redirects") << "http://localhost" << tempRedirectReply << QNetworkReply::TooManyRedirectsError;
8530#if QT_CONFIG(ssl)
8531 QTest::newRow(dataTag: "insecure-redirect") << "https://localhost" << tempRedirectReply << QNetworkReply::InsecureRedirectError;
8532#endif
8533 QTest::newRow(dataTag: "unknown-redirect") << "http://localhost"<< tempRedirectReply.replace(before: "http", after: "bad_protocol") << QNetworkReply::ProtocolUnknownError;
8534}
8535
8536void tst_QNetworkReply::ioHttpRedirectErrors()
8537{
8538 QFETCH(QString, url);
8539 QFETCH(QString, dataToSend);
8540 QFETCH(QNetworkReply::NetworkError, error);
8541
8542 QUrl localhost(url);
8543 MiniHttpServer server("", localhost.scheme() == QLatin1String("https"));
8544
8545 localhost.setPort(server.serverPort());
8546
8547 QByteArray d2s = dataToSend.arg(
8548 a: QString::number(server.serverPort())).toLatin1();
8549 server.setDataToTransmit(d2s);
8550
8551 QNetworkRequest request(localhost);
8552 request.setAttribute(code: QNetworkRequest::FollowRedirectsAttribute, value: true);
8553 QNetworkReplyPtr reply(manager.get(request));
8554 if (localhost.scheme() == "https")
8555 reply.data()->ignoreSslErrors();
8556
8557 QEventLoop eventLoop;
8558 QTimer watchDog;
8559 watchDog.setSingleShot(true);
8560
8561 reply->connect(sender: reply.data(), signal: &QNetworkReply::errorOccurred,
8562 slot: [&eventLoop](QNetworkReply::NetworkError){
8563 eventLoop.exit(returnCode: Failure);
8564 });
8565
8566 watchDog.connect(sender: &watchDog, signal: &QTimer::timeout, slot: [&eventLoop](){
8567 eventLoop.exit(returnCode: Timeout);
8568 });
8569
8570 watchDog.start(msec: 5000);
8571
8572 QCOMPARE(eventLoop.exec(), int(Failure));
8573 QCOMPARE(reply->error(), error);
8574}
8575
8576struct SameOriginRedirector : MiniHttpServer
8577{
8578 SameOriginRedirector(const QByteArray &data, bool ssl = false)
8579 : MiniHttpServer(data, ssl)
8580 { }
8581
8582 std::vector<QByteArray> responses;
8583
8584 void reply() override
8585 {
8586 if (responses.empty()) {
8587 dataToTransmit.clear();
8588 } else {
8589 dataToTransmit = responses.back();
8590 responses.pop_back();
8591 }
8592
8593 MiniHttpServer::reply();
8594 }
8595};
8596
8597void tst_QNetworkReply::ioHttpRedirectPolicy_data()
8598{
8599 QTest::addColumn<QNetworkRequest::RedirectPolicy>(name: "policy");
8600 QTest::addColumn<bool>(name: "ssl");
8601 QTest::addColumn<int>(name: "redirectCount");
8602 QTest::addColumn<int>(name: "statusCode");
8603
8604 QTest::newRow(dataTag: "manual-nossl") << QNetworkRequest::ManualRedirectPolicy << false << 0 << 307;
8605 QTest::newRow(dataTag: "nolesssafe-nossl") << QNetworkRequest::NoLessSafeRedirectPolicy << false << 1 << 200;
8606 QTest::newRow(dataTag: "same-origin-nossl") << QNetworkRequest::SameOriginRedirectPolicy << false << 1 << 200;
8607#if QT_CONFIG(ssl)
8608 QTest::newRow(dataTag: "manual-ssl") << QNetworkRequest::ManualRedirectPolicy << true << 0 << 307;
8609 QTest::newRow(dataTag: "nolesssafe-ssl") << QNetworkRequest::NoLessSafeRedirectPolicy << true << 1 << 200;
8610 QTest::newRow(dataTag: "same-origin-ssl") << QNetworkRequest::SameOriginRedirectPolicy << true << 1 << 200;
8611#endif
8612}
8613
8614void tst_QNetworkReply::ioHttpRedirectPolicy()
8615{
8616 QFETCH(const QNetworkRequest::RedirectPolicy, policy);
8617
8618 QFETCH(const bool, ssl);
8619
8620 QFETCH(const int, redirectCount);
8621 QFETCH(const int, statusCode);
8622
8623 // Setup HTTP server.
8624 SameOriginRedirector redirectServer("", ssl);
8625
8626 QUrl url(QLatin1String(ssl ? "https://localhost" : "http://localhost"));
8627
8628 url.setPort(redirectServer.serverPort());
8629 redirectServer.responses.push_back(x: httpEmpty200Response);
8630 redirectServer.responses.push_back(x: tempRedirectReplyStr().arg(a: QString(url.toEncoded())).toLatin1());
8631
8632 // This is the default one we preserve between tests.
8633 QCOMPARE(manager.redirectPolicy(), QNetworkRequest::ManualRedirectPolicy);
8634
8635 manager.setRedirectPolicy(policy);
8636 QCOMPARE(manager.redirectPolicy(), policy);
8637 QNetworkReplyPtr reply(manager.get(request: QNetworkRequest(url)));
8638 if (ssl)
8639 reply->ignoreSslErrors();
8640
8641 // Restore default:
8642 manager.setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
8643 QSignalSpy redirectSpy(reply.data(), SIGNAL(redirected(QUrl)));
8644 QSignalSpy finishedSpy(reply.data(), SIGNAL(finished()));
8645 QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
8646 QCOMPARE(finishedSpy.count(), 1);
8647 QCOMPARE(redirectSpy.count(), redirectCount);
8648 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), statusCode);
8649 QVERIFY(validateRedirectedResponseHeaders(reply) || statusCode != 200);
8650}
8651
8652void tst_QNetworkReply::ioHttpRedirectPolicyErrors_data()
8653{
8654 QTest::addColumn<QNetworkRequest::RedirectPolicy>(name: "policy");
8655 QTest::addColumn<bool>(name: "ssl");
8656 QTest::addColumn<QString>(name: "location");
8657 QTest::addColumn<int>(name: "maxRedirects");
8658 QTest::addColumn<QNetworkReply::NetworkError>(name: "expectedError");
8659
8660 // 1. NoLessSafeRedirectsPolicy
8661 QTest::newRow(dataTag: "nolesssafe-nossl-nossl-too-many") << QNetworkRequest::NoLessSafeRedirectPolicy
8662 << false << QString("http://localhost:%1") << 0 << QNetworkReply::TooManyRedirectsError;
8663#if QT_CONFIG(ssl)
8664 QTest::newRow(dataTag: "nolesssafe-ssl-ssl-too-many") << QNetworkRequest::NoLessSafeRedirectPolicy
8665 << true << QString("https:/localhost:%1") << 0 << QNetworkReply::TooManyRedirectsError;
8666 QTest::newRow(dataTag: "nolesssafe-ssl-nossl-insecure-redirect") << QNetworkRequest::NoLessSafeRedirectPolicy
8667 << true << QString("http://localhost:%1") << 50 << QNetworkReply::InsecureRedirectError;
8668#endif
8669 // 2. SameOriginRedirectsPolicy
8670 QTest::newRow(dataTag: "same-origin-nossl-nossl-too-many") << QNetworkRequest::SameOriginRedirectPolicy
8671 << false << QString("http://localhost:%1") << 0 << QNetworkReply::TooManyRedirectsError;
8672#if QT_CONFIG(ssl)
8673 QTest::newRow(dataTag: "same-origin-ssl-ssl-too-many") << QNetworkRequest::SameOriginRedirectPolicy
8674 << true << QString("https://localhost:%1") << 0 << QNetworkReply::TooManyRedirectsError;
8675 QTest::newRow(dataTag: "same-origin-https-http-wrong-protocol") << QNetworkRequest::SameOriginRedirectPolicy
8676 << true << QString("http://localhost:%1") << 50 << QNetworkReply::InsecureRedirectError;
8677#endif
8678 QTest::newRow(dataTag: "same-origin-http-https-wrong-protocol") << QNetworkRequest::SameOriginRedirectPolicy
8679 << false << QString("https://localhost:%1") << 50 << QNetworkReply::InsecureRedirectError;
8680 QTest::newRow(dataTag: "same-origin-http-http-wrong-host") << QNetworkRequest::SameOriginRedirectPolicy
8681 << false << QString("http://not-so-localhost:%1") << 50 << QNetworkReply::InsecureRedirectError;
8682#if QT_CONFIG(ssl)
8683 QTest::newRow(dataTag: "same-origin-https-https-wrong-host") << QNetworkRequest::SameOriginRedirectPolicy
8684 << true << QString("https://not-so-localhost:%1") << 50 << QNetworkReply::InsecureRedirectError;
8685#endif
8686 QTest::newRow(dataTag: "same-origin-http-http-wrong-port") << QNetworkRequest::SameOriginRedirectPolicy
8687 << false << QString("http://localhost/%1") << 50 << QNetworkReply::InsecureRedirectError;
8688#if QT_CONFIG(ssl)
8689 QTest::newRow(dataTag: "same-origin-https-https-wrong-port") << QNetworkRequest::SameOriginRedirectPolicy
8690 << true << QString("https://localhost/%1") << 50 << QNetworkReply::InsecureRedirectError;
8691#endif
8692}
8693
8694void tst_QNetworkReply::ioHttpRedirectPolicyErrors()
8695{
8696 QFETCH(const QNetworkRequest::RedirectPolicy, policy);
8697 // This should never happen:
8698 QVERIFY(policy != QNetworkRequest::ManualRedirectPolicy);
8699
8700 QFETCH(const bool, ssl);
8701 QFETCH(const QString, location);
8702 QFETCH(const int, maxRedirects);
8703 QFETCH(const QNetworkReply::NetworkError, expectedError);
8704
8705 // Setup the server.
8706 MiniHttpServer server("", ssl);
8707 server.setDataToTransmit(tempRedirectReplyStr().arg(a: location.arg(a: server.serverPort())).toLatin1());
8708
8709 QUrl url(QLatin1String(ssl ? "https://localhost" : "http://localhost"));
8710 url.setPort(server.serverPort());
8711
8712 QNetworkRequest request(url);
8713 request.setMaximumRedirectsAllowed(maxRedirects);
8714 // We always reset the policy to the default one ('Manual') after any related
8715 // test is finished:
8716 QCOMPARE(manager.redirectPolicy(), QNetworkRequest::ManualRedirectPolicy);
8717 manager.setRedirectPolicy(policy);
8718 QCOMPARE(manager.redirectPolicy(), policy);
8719
8720 QNetworkReplyPtr reply(manager.get(request));
8721 // Set it back to default:
8722 manager.setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
8723
8724 if (ssl)
8725 reply->ignoreSslErrors();
8726
8727 QSignalSpy spy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
8728
8729 QCOMPARE(waitForFinish(reply), int(Failure));
8730 QCOMPARE(spy.count(), 1);
8731 QCOMPARE(reply->error(), expectedError);
8732}
8733
8734void tst_QNetworkReply::ioHttpUserVerifiedRedirect_data()
8735{
8736 QTest::addColumn<bool>(name: "followRedirect");
8737 QTest::addColumn<int>(name: "statusCode");
8738
8739 QTest::newRow(dataTag: "allow-redirect") << true << 200;
8740 QTest::newRow(dataTag: "reject-redirect") << false << 307;
8741}
8742
8743void tst_QNetworkReply::ioHttpUserVerifiedRedirect()
8744{
8745 QFETCH(const bool, followRedirect);
8746 QFETCH(const int, statusCode);
8747
8748 // Setup HTTP server.
8749 MiniHttpServer target(httpEmpty200Response, false);
8750 QUrl url("http://localhost");
8751 url.setPort(target.serverPort());
8752
8753 MiniHttpServer redirectServer("", false);
8754 redirectServer.setDataToTransmit(tempRedirectReplyStr().arg(a: QString(url.toEncoded())).toLatin1());
8755 url.setPort(redirectServer.serverPort());
8756
8757 QCOMPARE(manager.redirectPolicy(), QNetworkRequest::ManualRedirectPolicy);
8758 manager.setRedirectPolicy(QNetworkRequest::UserVerifiedRedirectPolicy);
8759 QCOMPARE(manager.redirectPolicy(), QNetworkRequest::UserVerifiedRedirectPolicy);
8760
8761 QNetworkReplyPtr reply(manager.get(request: QNetworkRequest(url)));
8762 reply->connect(sender: reply.data(), signal: &QNetworkReply::redirected,
8763 slot: [&](const QUrl &redirectUrl) {
8764 qDebug() << "redirect to:" << redirectUrl;
8765 if (followRedirect) {
8766 qDebug() << "confirmed.";
8767 emit reply->redirectAllowed();
8768 } else{
8769 qDebug() << "rejected.";
8770 emit reply->abort();
8771 }
8772 });
8773
8774 // Before any test failed, reset the policy to default:
8775 manager.setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
8776 QCOMPARE(manager.redirectPolicy(), QNetworkRequest::ManualRedirectPolicy);
8777
8778 QSignalSpy finishedSpy(reply.data(), SIGNAL(finished()));
8779 waitForFinish(reply);
8780 QCOMPARE(finishedSpy.count(), 1);
8781 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), statusCode);
8782 QVERIFY(validateRedirectedResponseHeaders(reply) || statusCode != 200);
8783}
8784
8785void tst_QNetworkReply::ioHttpCookiesDuringRedirect()
8786{
8787 MiniHttpServer target(httpEmpty200Response, false);
8788
8789 const QString cookieHeader = QStringLiteral("Set-Cookie: hello=world; Path=/;\r\n");
8790 QString redirect = tempRedirectReplyStr();
8791 // Insert 'cookieHeader' before the final \r\n
8792 redirect.insert(i: redirect.length() - 2, s: cookieHeader);
8793
8794 QUrl url("http://localhost/");
8795 url.setPort(target.serverPort());
8796 redirect = redirect.arg(a: url.toString());
8797 MiniHttpServer redirectServer(redirect.toLatin1(), false);
8798
8799 url = QUrl("http://localhost/");
8800 url.setPort(redirectServer.serverPort());
8801 QNetworkRequest request(url);
8802 auto oldRedirectPolicy = manager.redirectPolicy();
8803 manager.setRedirectPolicy(QNetworkRequest::RedirectPolicy::NoLessSafeRedirectPolicy);
8804 QNetworkReplyPtr reply(manager.get(request));
8805 // Set policy back to whatever it was
8806 manager.setRedirectPolicy(oldRedirectPolicy);
8807
8808 QVERIFY(waitForFinish(reply) == Success);
8809 QVERIFY(target.receivedData.contains("\r\nCookie: hello=world\r\n"));
8810 QVERIFY(validateRedirectedResponseHeaders(reply));
8811}
8812
8813void tst_QNetworkReply::ioHttpRedirect_data()
8814{
8815 QTest::addColumn<QString>(name: "status");
8816
8817 QTest::addRow(format: "301") << "301 Moved Permanently";
8818 QTest::addRow(format: "302") << "302 Found";
8819 QTest::addRow(format: "303") << "303 See Other";
8820 QTest::addRow(format: "305") << "305 Use Proxy";
8821 QTest::addRow(format: "307") << "307 Temporary Redirect";
8822 QTest::addRow(format: "308") << "308 Permanent Redirect";
8823}
8824
8825void tst_QNetworkReply::ioHttpRedirect()
8826{
8827 QFETCH(QString, status);
8828
8829 MiniHttpServer target(httpEmpty200Response, false);
8830 QUrl targetUrl("http://localhost/");
8831 targetUrl.setPort(target.serverPort());
8832
8833 QString redirectReply = QStringLiteral("HTTP/1.1 %1\r\n"
8834 "Content-Type: text/plain\r\n"
8835 "location: %2\r\n"
8836 "\r\n").arg(args&: status, args: targetUrl.toString());
8837 MiniHttpServer redirectServer(redirectReply.toLatin1(), false);
8838 QUrl url("http://localhost/");
8839 url.setPort(redirectServer.serverPort());
8840 QNetworkRequest request(url);
8841 auto oldRedirectPolicy = manager.redirectPolicy();
8842 manager.setRedirectPolicy(QNetworkRequest::RedirectPolicy::NoLessSafeRedirectPolicy);
8843 QNetworkReplyPtr reply(manager.get(request));
8844 // Set policy back to what it was
8845 manager.setRedirectPolicy(oldRedirectPolicy);
8846
8847 QCOMPARE(waitForFinish(reply), int(Success));
8848 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
8849 QVERIFY(validateRedirectedResponseHeaders(reply));
8850}
8851
8852void tst_QNetworkReply::ioHttpRedirectFromLocalToRemote()
8853{
8854 QUrl targetUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt");
8855
8856 QString redirectReply = tempRedirectReplyStr().arg(a: targetUrl.toString());
8857 MiniHttpServer redirectServer(redirectReply.toLatin1(), false);
8858 QUrl url("http://localhost/");
8859 url.setPort(redirectServer.serverPort());
8860
8861 QFile reference(testDataDir + "/rfc3252.txt");
8862 QVERIFY(reference.open(QIODevice::ReadOnly));
8863 QNetworkRequest request(url);
8864
8865 auto oldRedirectPolicy = manager.redirectPolicy();
8866 manager.setRedirectPolicy(QNetworkRequest::RedirectPolicy::NoLessSafeRedirectPolicy);
8867 QNetworkReplyPtr reply(manager.get(request));
8868 // Restore previous policy
8869 manager.setRedirectPolicy(oldRedirectPolicy);
8870
8871 QCOMPARE(waitForFinish(reply), int(Success));
8872
8873 QCOMPARE(reply->url(), targetUrl);
8874 QCOMPARE(reply->error(), QNetworkReply::NoError);
8875 QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
8876 QCOMPARE(reply->readAll(), reference.readAll());
8877}
8878
8879void tst_QNetworkReply::ioHttpRedirectPostPut_data()
8880{
8881 QTest::addColumn<bool>(name: "usePost");
8882 QTest::addColumn<QString>(name: "status");
8883 QTest::addColumn<QByteArray>(name: "data");
8884 QTest::addColumn<QString>(name: "contentType");
8885
8886 QByteArray data;
8887 data = "hello world";
8888 QTest::addRow(format: "post-307") << true << "307 Temporary Redirect" << data << "text/plain";
8889 QTest::addRow(format: "put-307") << false << "307 Temporary Redirect" << data << "text/plain";
8890 QString permanentRedirect = "308 Permanent Redirect";
8891 QTest::addRow(format: "post-308") << true << permanentRedirect << data << "text/plain";
8892 QTest::addRow(format: "put-308") << false << permanentRedirect << data << "text/plain";
8893
8894 // Some data from ::putToFile_data
8895 data = "";
8896 QTest::newRow(dataTag: "post-empty") << true << permanentRedirect << data << "application/octet-stream";
8897 QTest::newRow(dataTag: "put-empty") << false << permanentRedirect << data << "application/octet-stream";
8898
8899 data = QByteArray("abcd\0\1\2\abcd",12);
8900 QTest::newRow(dataTag: "post-with-nul") << true << permanentRedirect << data << "application/octet-stream";
8901 QTest::newRow(dataTag: "put-with-nul") << false << permanentRedirect << data << "application/octet-stream";
8902
8903 data = QByteArray(4097, '\4');
8904 QTest::newRow(dataTag: "post-4k+1") << true << permanentRedirect << data << "application/octet-stream";
8905 QTest::newRow(dataTag: "put-4k+1") << false << permanentRedirect << data << "application/octet-stream";
8906
8907 data = QByteArray(128*1024+1, '\177');
8908 QTest::newRow(dataTag: "post-128k+1") << true << permanentRedirect << data << "application/octet-stream";
8909 QTest::newRow(dataTag: "put-128k+1") << false << permanentRedirect << data << "application/octet-stream";
8910
8911 data = QByteArray(2*1024*1024+1, '\177');
8912 QTest::newRow(dataTag: "post-2MB+1") << true << permanentRedirect << data << "application/octet-stream";
8913 QTest::newRow(dataTag: "put-2MB+1") << false << permanentRedirect << data << "application/octet-stream";
8914}
8915
8916void tst_QNetworkReply::ioHttpRedirectPostPut()
8917{
8918 QFETCH(bool, usePost);
8919 QFETCH(QString, status);
8920 QFETCH(QByteArray, data);
8921 QFETCH(QString, contentType);
8922
8923 QUrl targetUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/cgi-bin/md5sum.cgi");
8924
8925 QString redirectReply = QStringLiteral("HTTP/1.1 %1\r\n"
8926 "Content-Type: text/plain\r\n"
8927 "location: %2\r\n"
8928 "\r\n").arg(args&: status, args: targetUrl.toString());
8929 MiniHttpServer redirectServer(redirectReply.toLatin1());
8930 QUrl url("http://localhost/");
8931 url.setPort(redirectServer.serverPort());
8932 QNetworkRequest request(url);
8933 request.setHeader(header: QNetworkRequest::KnownHeaders::ContentTypeHeader, value: contentType);
8934 auto oldRedirectPolicy = manager.redirectPolicy();
8935 manager.setRedirectPolicy(QNetworkRequest::RedirectPolicy::NoLessSafeRedirectPolicy);
8936
8937 QNetworkReplyPtr reply(usePost ? manager.post(request, data) : manager.put(request, data));
8938 // Restore previous policy:
8939 manager.setRedirectPolicy(oldRedirectPolicy);
8940
8941 QCOMPARE(waitForFinish(reply), int(Success));
8942 QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
8943}
8944
8945void tst_QNetworkReply::ioHttpRedirectMultipartPost_data()
8946{
8947 postToHttpMultipart_data();
8948}
8949
8950void tst_QNetworkReply::ioHttpRedirectMultipartPost()
8951{
8952 // Note: This code is heavily based on postToHttpMultipart
8953 QFETCH(QUrl, url);
8954
8955 static QSet<QByteArray> boundaries;
8956
8957 QNetworkReplyPtr reply;
8958
8959 QFETCH(QHttpMultiPart *, multiPart);
8960 QFETCH(QByteArray, expectedReplyData);
8961 QFETCH(QByteArray, contentType);
8962
8963 QString redirectReply = tempRedirectReplyStr().arg(a: url.toString());
8964 MiniHttpServer redirectServer(redirectReply.toLatin1());
8965 QUrl redirectUrl("http://localhost/");
8966 redirectUrl.setPort(redirectServer.serverPort());
8967 QNetworkRequest request(redirectUrl);
8968
8969 // Restore policy when we leave this scope:
8970 struct PolicyRestorer
8971 {
8972 QNetworkAccessManager &qnam;
8973 QNetworkRequest::RedirectPolicy policy;
8974 PolicyRestorer(QNetworkAccessManager &qnam)
8975 : qnam(qnam), policy(qnam.redirectPolicy())
8976 { qnam.setRedirectPolicy(QNetworkRequest::RedirectPolicy::NoLessSafeRedirectPolicy); }
8977 ~PolicyRestorer() { qnam.setRedirectPolicy(policy); }
8978 } policyRestorer(manager);
8979
8980 // hack for testing the setting of the content-type header by hand:
8981 if (contentType == "custom") {
8982 QByteArray contentType("multipart/custom; boundary=\"" + multiPart->boundary() + "\"");
8983 request.setHeader(header: QNetworkRequest::ContentTypeHeader, value: contentType);
8984 }
8985
8986 QVERIFY2(! boundaries.contains(multiPart->boundary()), "boundary '" + multiPart->boundary() + "' has been created twice");
8987 boundaries.insert(value: multiPart->boundary());
8988
8989 RUN_REQUEST(runMultipartRequest(request, reply, multiPart, "POST"));
8990 multiPart->deleteLater();
8991
8992 QCOMPARE(reply->url(), url);
8993 QCOMPARE(reply->error(), QNetworkReply::NoError);
8994
8995 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 OK
8996
8997 QVERIFY(multiPart->boundary().count() > 20); // check that there is randomness after the "boundary_.oOo._" string
8998 QVERIFY(multiPart->boundary().count() < 70);
8999 QByteArray replyData = reply->readAll();
9000
9001 expectedReplyData.prepend(a: "content type: multipart/" + contentType + "; boundary=\"" + multiPart->boundary() + "\"\n");
9002// QEXPECT_FAIL("nested", "the server does not understand nested multipart messages", Continue); // see above
9003 QCOMPARE(replyData, expectedReplyData);
9004}
9005
9006void tst_QNetworkReply::ioHttpRedirectDelete()
9007{
9008 MiniHttpServer target(httpEmpty200Response, false);
9009 QUrl targetUrl("http://localhost/");
9010 targetUrl.setPort(target.serverPort());
9011
9012 QString redirectReply = tempRedirectReplyStr().arg(a: targetUrl.toString());
9013 MiniHttpServer redirectServer(redirectReply.toLatin1());
9014 QUrl url("http://localhost/");
9015 url.setPort(redirectServer.serverPort());
9016 QNetworkRequest request(url);
9017 auto oldRedirectPolicy = manager.redirectPolicy();
9018 manager.setRedirectPolicy(QNetworkRequest::RedirectPolicy::NoLessSafeRedirectPolicy);
9019
9020 QNetworkReplyPtr reply(manager.deleteResource(request));
9021 // Restore previous policy:
9022 manager.setRedirectPolicy(oldRedirectPolicy);
9023
9024 QCOMPARE(waitForFinish(reply), int(Success));
9025 QVERIFY2(target.receivedData.startsWith("DELETE"), "Target server called with the wrong method");
9026}
9027
9028void tst_QNetworkReply::ioHttpRedirectCustom()
9029{
9030 MiniHttpServer target(httpEmpty200Response, false);
9031 QUrl targetUrl("http://localhost/");
9032 targetUrl.setPort(target.serverPort());
9033
9034 QString redirectReply = tempRedirectReplyStr().arg(a: targetUrl.toString());
9035 MiniHttpServer redirectServer(redirectReply.toLatin1());
9036 QUrl url("http://localhost/");
9037 url.setPort(redirectServer.serverPort());
9038 QNetworkRequest request(url);
9039 auto oldRedirectPolicy = manager.redirectPolicy();
9040 manager.setRedirectPolicy(QNetworkRequest::RedirectPolicy::NoLessSafeRedirectPolicy);
9041
9042 QNetworkReplyPtr reply(manager.sendCustomRequest(request, QByteArrayLiteral("CUSTOM")));
9043 // Restore previous policy:
9044 manager.setRedirectPolicy(oldRedirectPolicy);
9045
9046 QCOMPARE(waitForFinish(reply), int(Success));
9047 QVERIFY2(target.receivedData.startsWith("CUSTOM"), "Target server called with the wrong method");
9048}
9049
9050void tst_QNetworkReply::ioHttpRedirectWithUploadDevice_data()
9051{
9052 QTest::addColumn<QByteArray>(name: "method");
9053 QTest::addColumn<QString>(name: "status");
9054 QTest::addColumn<bool>(name: "keepMethod");
9055
9056 QTest::addRow(format: "post-301") << QByteArray("POST") << "301 Moved Permanently" << false;
9057 QTest::addRow(format: "post-307") << QByteArray("POST") << "307 Temporary Redirect" << true;
9058
9059 const QByteArray customVerb = QByteArray("CUSTOM_WITH_PAYLOAD");
9060 QTest::addRow(format: "custom-301") << customVerb << "301 Moved Permanently" << false;
9061 QTest::addRow(format: "custom-307") << customVerb << "307 Temporary Redirect" << true;
9062}
9063
9064/*
9065 Tests that we properly disregard the upload device when redirecting
9066 and changing method to GET, and that it gets reset properly when
9067 redirecting (without changing method) for POST and a custom method.
9068*/
9069void tst_QNetworkReply::ioHttpRedirectWithUploadDevice()
9070{
9071 QFETCH(QByteArray, method);
9072 QFETCH(QString, status);
9073 QFETCH(bool, keepMethod);
9074
9075 MiniHttpServer target(httpEmpty200Response, false);
9076 QUrl targetUrl("http://localhost/");
9077 targetUrl.setPort(target.serverPort());
9078
9079 QString redirectReply = QStringLiteral("HTTP/1.1 %1\r\n"
9080 "Content-Type: text/plain\r\n"
9081 "location: %2\r\n"
9082 "\r\n").arg(args&: status, args: targetUrl.toString());
9083 MiniHttpServer redirectServer(redirectReply.toLatin1());
9084 QUrl url("http://localhost/");
9085 url.setPort(redirectServer.serverPort());
9086
9087 QNetworkRequest request(url);
9088 request.setHeader(header: QNetworkRequest::ContentTypeHeader, value: QByteArray("text/plain"));
9089 auto oldRedirectPolicy = manager.redirectPolicy();
9090 manager.setRedirectPolicy(QNetworkRequest::RedirectPolicy::NoLessSafeRedirectPolicy);
9091
9092 QByteArray data = "Hello world";
9093 QBuffer buffer(&data);
9094
9095 QNetworkReplyPtr reply;
9096 if (method == "POST")
9097 reply.reset(t: manager.post(request, data: &buffer));
9098 else
9099 reply.reset(t: manager.sendCustomRequest(request, verb: method, data: &buffer));
9100 // Restore previous policy:
9101 manager.setRedirectPolicy(oldRedirectPolicy);
9102
9103 QCOMPARE(waitForFinish(reply), int(Success));
9104 QByteArray expectedMethod = method;
9105 if (!keepMethod)
9106 expectedMethod = "GET";
9107 QVERIFY2(target.receivedData.startsWith(expectedMethod), "Target server called with the wrong method");
9108 if (keepMethod) { // keepMethod also means we resend the data
9109 QVERIFY2(target.receivedData.endsWith(data), "Target server didn't receive the data");
9110 } else {
9111 // we shouldn't send Content-Length with not content (esp. for GET)
9112 QVERIFY2(!target.receivedData.contains("Content-Length"),
9113 "Target server should not have received a Content-Length header");
9114 QVERIFY2(!target.receivedData.contains("Content-Type"),
9115 "Target server should not have received a Content-Type header");
9116 }
9117}
9118
9119#ifndef QT_NO_SSL
9120
9121class PutWithServerClosingConnectionImmediatelyHandler: public QObject
9122{
9123 Q_OBJECT
9124public:
9125 bool m_parsedHeaders;
9126 QByteArray m_receivedData;
9127 QByteArray m_expectedData;
9128 QSslSocket *m_socket;
9129 PutWithServerClosingConnectionImmediatelyHandler(QSslSocket *s, QByteArray expected) :m_parsedHeaders(false), m_expectedData(expected), m_socket(s)
9130 {
9131 m_socket->setParent(this);
9132 connect(asender: m_socket, SIGNAL(readyRead()), SLOT(readyReadSlot()));
9133 connect(asender: m_socket, SIGNAL(disconnected()), SLOT(disconnectedSlot()));
9134 }
9135signals:
9136 void correctFileUploadReceived();
9137 void corruptFileUploadReceived();
9138
9139public slots:
9140 void closeDelayed() { m_socket->close(); }
9141
9142 void readyReadSlot()
9143 {
9144 QByteArray data = m_socket->readAll();
9145 m_receivedData += data;
9146 if (!m_parsedHeaders && m_receivedData.contains(c: "\r\n\r\n")) {
9147 m_parsedHeaders = true;
9148 QTimer::singleShot(msec: QRandomGenerator::global()->bounded(highest: 60), receiver: this, SLOT(closeDelayed())); // simulate random network latency
9149 // This server simulates a web server connection closing, e.g. because of Apaches MaxKeepAliveRequests or KeepAliveTimeout
9150 // In this case QNAM needs to re-send the upload data but it had a bug which then corrupts the upload
9151 // This test catches that.
9152 }
9153
9154 }
9155 void disconnectedSlot()
9156 {
9157 if (m_parsedHeaders) {
9158 //qDebug() << m_receivedData.left(m_receivedData.indexOf("\r\n\r\n"));
9159 m_receivedData = m_receivedData.mid(index: m_receivedData.indexOf(c: "\r\n\r\n")+4); // check only actual data
9160 }
9161 if (m_receivedData.length() > 0 && !m_expectedData.startsWith(a: m_receivedData)) {
9162 // We had received some data but it is corrupt!
9163 qDebug() << "CORRUPT" << m_receivedData.count();
9164
9165#if 0 // Use this to track down the pattern of the corruption and conclude the source
9166 QFile a("/tmp/corrupt");
9167 a.open(QIODevice::WriteOnly);
9168 a.write(m_receivedData);
9169 a.close();
9170
9171 QFile b("/tmp/correct");
9172 b.open(QIODevice::WriteOnly);
9173 b.write(m_expectedData);
9174 b.close();
9175 //exit(1);
9176#endif
9177 emit corruptFileUploadReceived();
9178 } else {
9179 emit correctFileUploadReceived();
9180 }
9181 }
9182};
9183
9184class PutWithServerClosingConnectionImmediatelyServer: public SslServer
9185{
9186 Q_OBJECT
9187public:
9188 int m_correctUploads;
9189 int m_corruptUploads;
9190 int m_repliesFinished;
9191 int m_expectedReplies;
9192 QByteArray m_expectedData;
9193 PutWithServerClosingConnectionImmediatelyServer()
9194 : SslServer(), m_correctUploads(0), m_corruptUploads(0),
9195 m_repliesFinished(0), m_expectedReplies(0)
9196 {
9197 QObject::connect(sender: this, SIGNAL(newEncryptedConnection(QSslSocket*)), receiver: this, SLOT(createHandlerForConnection(QSslSocket*)));
9198 QObject::connect(sender: this, SIGNAL(newPlainConnection(QSslSocket*)), receiver: this, SLOT(createHandlerForConnection(QSslSocket*)));
9199 }
9200
9201public slots:
9202 void createHandlerForConnection(QSslSocket* s)
9203 {
9204 PutWithServerClosingConnectionImmediatelyHandler *handler = new PutWithServerClosingConnectionImmediatelyHandler(s, m_expectedData);
9205 handler->setParent(this);
9206 QObject::connect(sender: handler, SIGNAL(correctFileUploadReceived()), receiver: this, SLOT(increaseCorrect()));
9207 QObject::connect(sender: handler, SIGNAL(corruptFileUploadReceived()), receiver: this, SLOT(increaseCorrupt()));
9208 }
9209 void increaseCorrect() { m_correctUploads++; }
9210 void increaseCorrupt() { m_corruptUploads++; }
9211 void replyFinished()
9212 {
9213 m_repliesFinished++;
9214 if (m_repliesFinished == m_expectedReplies) {
9215 QTestEventLoop::instance().exitLoop();
9216 }
9217 }
9218};
9219
9220
9221
9222void tst_QNetworkReply::putWithServerClosingConnectionImmediately()
9223{
9224 const int numUploads = 40;
9225 qint64 wantedSize = 512*1024; // 512 kB
9226 QByteArray sourceFile;
9227 for (int i = 0; i < wantedSize; ++i) {
9228 sourceFile += (char)'a' +(i%26);
9229 }
9230 bool withSsl = false;
9231
9232 for (int s = 0; s <= 1; s++) {
9233 withSsl = (s == 1);
9234 // Test also needs to run several times because of 9c2ecf89
9235 for (int j = 0; j < 20; j++) {
9236 // emulate a minimal https server
9237 PutWithServerClosingConnectionImmediatelyServer server;
9238 server.m_ssl = withSsl;
9239 server.m_expectedData = sourceFile;
9240 server.m_expectedReplies = numUploads;
9241 server.listen(address: QHostAddress(QHostAddress::LocalHost), port: 0);
9242
9243 QString urlPrefix = QLatin1String("http");
9244 if (withSsl)
9245 urlPrefix += QLatin1Char('s');
9246 urlPrefix += QLatin1String("://127.0.0.1:");
9247 urlPrefix += QString::number(server.serverPort());
9248 urlPrefix += QLatin1String("/file=");
9249 for (int i = 0; i < numUploads; i++) {
9250 // create the request
9251 QNetworkRequest request(QUrl(urlPrefix + QString::number(i)));
9252 QNetworkReply *reply = manager.put(request, data: sourceFile);
9253 connect(sender: reply, SIGNAL(sslErrors(QList<QSslError>)), receiver: reply, SLOT(ignoreSslErrors()));
9254 connect(sender: reply, SIGNAL(finished()), receiver: &server, SLOT(replyFinished()));
9255 reply->setParent(&server);
9256 }
9257
9258 // get the request started and the incoming socket connected
9259 QTestEventLoop::instance().enterLoop(secs: 10);
9260 QVERIFY(!QTestEventLoop::instance().timeout());
9261
9262 //qDebug() << "correct=" << server.m_correctUploads << "corrupt=" << server.m_corruptUploads << "expected=" <<numUploads;
9263
9264 // Sanity check because ecause of 9c2ecf89 most replies will error out but we want to make sure at least some of them worked
9265 QVERIFY(server.m_correctUploads > 2);
9266 // Because actually important is that we don't get any corruption:
9267 QCOMPARE(server.m_corruptUploads, 0);
9268
9269 server.close();
9270 }
9271 }
9272
9273
9274}
9275
9276#endif
9277
9278void tst_QNetworkReply::autoDeleteRepliesAttribute_data()
9279{
9280 QTest::addColumn<QUrl>(name: "destination");
9281
9282 QTest::newRow(dataTag: "http") << QUrl("http://QInvalidDomain.qt/test");
9283 QTest::newRow(dataTag: "https") << QUrl("https://QInvalidDomain.qt/test");
9284 QTest::newRow(dataTag: "ftp") << QUrl("ftp://QInvalidDomain.qt/test");
9285 QTest::newRow(dataTag: "file") << QUrl("file:///thisfolderdoesn'texist/probably.txt");
9286#ifdef Q_OS_WIN
9287 // Only supported on windows.
9288 QTest::newRow("remote-file") << QUrl("file://QInvalidHost/thisfolderdoesn'texist/probably.txt");
9289#endif
9290 QTest::newRow(dataTag: "qrc") << QUrl("qrc:///path/to/nowhere");
9291 QTest::newRow(dataTag: "data") << QUrl("data:,Some%20plaintext%20data");
9292}
9293
9294void tst_QNetworkReply::autoDeleteRepliesAttribute()
9295{
9296 QFETCH(QUrl, destination);
9297 {
9298 // Get
9299 QNetworkRequest request(destination);
9300 request.setAttribute(code: QNetworkRequest::AutoDeleteReplyOnFinishAttribute, value: true);
9301 QNetworkReply *reply = manager.get(request);
9302 QSignalSpy finishedSpy(reply, &QNetworkReply::finished);
9303 QSignalSpy destroyedSpy(reply, &QObject::destroyed);
9304 QVERIFY(finishedSpy.wait());
9305 QCOMPARE(destroyedSpy.count(), 0);
9306 QVERIFY(destroyedSpy.wait());
9307 }
9308 {
9309 // Post
9310 QNetworkRequest request(destination);
9311 request.setAttribute(code: QNetworkRequest::AutoDeleteReplyOnFinishAttribute, value: true);
9312 QNetworkReply *reply = manager.post(request, QByteArrayLiteral("datastring"));
9313 QSignalSpy finishedSpy(reply, &QNetworkReply::finished);
9314 QSignalSpy destroyedSpy(reply, &QObject::destroyed);
9315 QVERIFY(finishedSpy.wait());
9316 QCOMPARE(destroyedSpy.count(), 0);
9317 QVERIFY(destroyedSpy.wait());
9318 }
9319 // Now repeated, but without the attribute to make sure it does not get deleted automatically.
9320 // We need two calls to processEvents to test that the QNetworkReply doesn't get deleted.
9321 // The first call executes a metacall event which adds the deleteLater meta event which
9322 // would be executed in the second call. But that shouldn't happen without the attribute.
9323 {
9324 // Get
9325 QNetworkRequest request(destination);
9326 QScopedPointer<QNetworkReply> reply(manager.get(request));
9327 QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished);
9328 QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed);
9329 QVERIFY(finishedSpy.wait());
9330 QCOMPARE(destroyedSpy.count(), 0);
9331 QCoreApplication::processEvents();
9332 QCoreApplication::processEvents();
9333 QCOMPARE(destroyedSpy.count(), 0);
9334 }
9335 {
9336 // Post
9337 QNetworkRequest request(destination);
9338 QScopedPointer<QNetworkReply> reply(manager.post(request, QByteArrayLiteral("datastring")));
9339 QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished);
9340 QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed);
9341 QVERIFY(finishedSpy.wait());
9342 QCOMPARE(destroyedSpy.count(), 0);
9343 QCoreApplication::processEvents();
9344 QCoreApplication::processEvents();
9345 QCOMPARE(destroyedSpy.count(), 0);
9346 }
9347}
9348
9349void tst_QNetworkReply::autoDeleteReplies_data()
9350{
9351 autoDeleteRepliesAttribute_data();
9352}
9353
9354void tst_QNetworkReply::autoDeleteReplies()
9355{
9356 QFETCH(QUrl, destination);
9357 manager.setAutoDeleteReplies(true);
9358 auto cleanup = qScopeGuard(f: [this] { manager.setAutoDeleteReplies(false); });
9359 {
9360 // Get
9361 QNetworkRequest request(destination);
9362 QNetworkReply *reply = manager.get(request);
9363 QSignalSpy finishedSpy(reply, &QNetworkReply::finished);
9364 QSignalSpy destroyedSpy(reply, &QObject::destroyed);
9365 QVERIFY(finishedSpy.wait());
9366 QCOMPARE(destroyedSpy.count(), 0);
9367 QVERIFY(destroyedSpy.wait());
9368 }
9369 {
9370 // Post
9371 QNetworkRequest request(destination);
9372 QNetworkReply *reply = manager.post(request, QByteArrayLiteral("datastring"));
9373 QSignalSpy finishedSpy(reply, &QNetworkReply::finished);
9374 QSignalSpy destroyedSpy(reply, &QObject::destroyed);
9375 QVERIFY(finishedSpy.wait());
9376 QCOMPARE(destroyedSpy.count(), 0);
9377 QVERIFY(destroyedSpy.wait());
9378 }
9379 // Here we repeat the test, but override the auto-deletion in the QNetworkRequest
9380 // We need two calls to processEvents to test that the QNetworkReply doesn't get deleted.
9381 // The first call executes a metacall event which adds the deleteLater meta event which
9382 // would be executed in the second call. But that shouldn't happen in this case.
9383 {
9384 // Get
9385 QNetworkRequest request(destination);
9386 request.setAttribute(code: QNetworkRequest::AutoDeleteReplyOnFinishAttribute, value: false);
9387 QScopedPointer<QNetworkReply> reply(manager.get(request));
9388 QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished);
9389 QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed);
9390 QVERIFY(finishedSpy.wait());
9391 QCOMPARE(destroyedSpy.count(), 0);
9392 QCoreApplication::processEvents();
9393 QCoreApplication::processEvents();
9394 QCOMPARE(destroyedSpy.count(), 0);
9395 }
9396 {
9397 // Post
9398 QNetworkRequest request(destination);
9399 request.setAttribute(code: QNetworkRequest::AutoDeleteReplyOnFinishAttribute, value: false);
9400 QScopedPointer<QNetworkReply> reply(manager.post(request, QByteArrayLiteral("datastring")));
9401 QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished);
9402 QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed);
9403 QVERIFY(finishedSpy.wait());
9404 QCOMPARE(destroyedSpy.count(), 0);
9405 QCoreApplication::processEvents();
9406 QCoreApplication::processEvents();
9407 QCOMPARE(destroyedSpy.count(), 0);
9408 }
9409 // Now we repeat the test with autoDeleteReplies set to false
9410 cleanup.dismiss();
9411 manager.setAutoDeleteReplies(false);
9412 {
9413 // Get
9414 QNetworkRequest request(destination);
9415 QScopedPointer<QNetworkReply> reply(manager.get(request));
9416 QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished);
9417 QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed);
9418 QVERIFY(finishedSpy.wait());
9419 QCOMPARE(destroyedSpy.count(), 0);
9420 QCoreApplication::processEvents();
9421 QCoreApplication::processEvents();
9422 QCOMPARE(destroyedSpy.count(), 0);
9423 }
9424 {
9425 // Post
9426 QNetworkRequest request(destination);
9427 QScopedPointer<QNetworkReply> reply(manager.post(request, QByteArrayLiteral("datastring")));
9428 QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished);
9429 QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed);
9430 QVERIFY(finishedSpy.wait());
9431 QCOMPARE(destroyedSpy.count(), 0);
9432 QCoreApplication::processEvents();
9433 QCoreApplication::processEvents();
9434 QCOMPARE(destroyedSpy.count(), 0);
9435 }
9436}
9437
9438void tst_QNetworkReply::getWithTimeout()
9439{
9440 MiniHttpServer server(tst_QNetworkReply::httpEmpty200Response, false);
9441
9442 QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
9443 QNetworkReplyPtr reply(manager.get(request));
9444 QSignalSpy spy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
9445
9446 QCOMPARE(waitForFinish(reply), int(Success));
9447
9448 QCOMPARE(spy.count(), 0);
9449 QVERIFY(reply->error() == QNetworkReply::NoError);
9450
9451 request.setTransferTimeout(1000);
9452 server.stopTransfer = true;
9453
9454 QNetworkReplyPtr reply2(manager.get(request));
9455 QSignalSpy spy2(reply2.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
9456
9457 QCOMPARE(waitForFinish(reply2), int(Failure));
9458
9459 QCOMPARE(spy2.count(), 1);
9460 QVERIFY(reply2->error() == QNetworkReply::OperationCanceledError);
9461
9462 request.setTransferTimeout(0);
9463 manager.setTransferTimeout(1000);
9464
9465 QNetworkReplyPtr reply3(manager.get(request));
9466 QSignalSpy spy3(reply3.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
9467
9468 QCOMPARE(waitForFinish(reply3), int(Failure));
9469
9470 QCOMPARE(spy3.count(), 1);
9471 QVERIFY(reply3->error() == QNetworkReply::OperationCanceledError);
9472
9473 manager.setTransferTimeout(0);
9474}
9475
9476void tst_QNetworkReply::postWithTimeout()
9477{
9478 MiniHttpServer server(tst_QNetworkReply::httpEmpty200Response, false);
9479
9480 QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
9481 request.setRawHeader(headerName: "Content-Type", value: "application/octet-stream");
9482 QByteArray postData("Just some nonsense");
9483 QNetworkReplyPtr reply(manager.post(request, data: postData));
9484 QSignalSpy spy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
9485
9486 QCOMPARE(waitForFinish(reply), int(Success));
9487
9488 QCOMPARE(spy.count(), 0);
9489 QVERIFY(reply->error() == QNetworkReply::NoError);
9490
9491 request.setTransferTimeout(1000);
9492 server.stopTransfer = true;
9493
9494 QNetworkReplyPtr reply2(manager.post(request, data: postData));
9495 QSignalSpy spy2(reply2.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
9496
9497 QCOMPARE(waitForFinish(reply2), int(Failure));
9498
9499 QCOMPARE(spy2.count(), 1);
9500 QVERIFY(reply2->error() == QNetworkReply::OperationCanceledError);
9501
9502 request.setTransferTimeout(0);
9503 manager.setTransferTimeout(1000);
9504
9505 QNetworkReplyPtr reply3(manager.post(request, data: postData));
9506 QSignalSpy spy3(reply3.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
9507
9508 QCOMPARE(waitForFinish(reply3), int(Failure));
9509
9510 QCOMPARE(spy3.count(), 1);
9511 QVERIFY(reply3->error() == QNetworkReply::OperationCanceledError);
9512
9513 manager.setTransferTimeout(0);
9514}
9515
9516// NOTE: This test must be last testcase in tst_qnetworkreply!
9517void tst_QNetworkReply::parentingRepliesToTheApp()
9518{
9519 QNetworkRequest request(QUrl("http://" + QtNetworkSettings::httpServerName()));
9520 manager.get(request)->setParent(this); // parent to this object
9521 manager.get(request)->setParent(qApp); // parent to the app
9522}
9523
9524QTEST_MAIN(tst_QNetworkReply)
9525
9526#include "tst_qnetworkreply.moc"
9527

source code of qtbase/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp