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 |
79 | Q_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 | |
98 | Q_DECLARE_METATYPE(QAuthenticator*) |
99 | #ifndef QT_NO_NETWORKPROXY |
100 | Q_DECLARE_METATYPE(QNetworkProxyQuery) |
101 | #endif |
102 | |
103 | #include "emulationdetector.h" |
104 | |
105 | typedef QSharedPointer<QNetworkReply> QNetworkReplyPtr; |
106 | |
107 | #ifndef QT_NO_OPENSSL |
108 | QT_BEGIN_NAMESPACE |
109 | void qt_ForceTlsSecurityLevel(); |
110 | QT_END_NAMESPACE |
111 | #endif |
112 | |
113 | class MyCookieJar; |
114 | class 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 | |
182 | public: |
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 | |
194 | public 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 | |
211 | protected Q_SLOTS: |
212 | void nestedEventLoops_slot(); |
213 | void notEnoughData(); |
214 | |
215 | private 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(); |
526 | private: |
527 | void cleanupTestData(); |
528 | |
529 | QString testDataDir; |
530 | bool notEnoughDataForFastSender; |
531 | }; |
532 | |
533 | const QByteArray tst_QNetworkReply::httpEmpty200Response = |
534 | "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" ; |
535 | const QString tst_QNetworkReply::filePermissionFileName = "/etc/shadow" ; |
536 | |
537 | bool 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 | |
546 | static bool (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 |
558 | static 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). |
574 | const 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. |
577 | const QString tst_QNetworkReply::certsFilePath = "/certs/qt-test-server-host-network-cacert.pem" ; |
578 | #endif // QT_TEST_SERVER_NAME |
579 | #else |
580 | const 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). |
588 | class MiniHttpServer: public QTcpServer |
589 | { |
590 | Q_OBJECT |
591 | public: |
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 () |
632 | { |
633 | contentLength = 0; |
634 | receivedData.clear(); |
635 | } |
636 | |
637 | protected: |
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 | } |
678 | private: |
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 | |
701 | private 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 | |
717 | public 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 = 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 | |
764 | class MyCookieJar: public QNetworkCookieJar |
765 | { |
766 | public: |
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 |
774 | class MyProxyFactory: public QNetworkProxyFactory |
775 | { |
776 | public: |
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 | |
798 | class MyMemoryCache: public QAbstractNetworkCache |
799 | { |
800 | public: |
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 | }; |
855 | Q_DECLARE_METATYPE(MyMemoryCache::CachedContent) |
856 | Q_DECLARE_METATYPE(MyMemoryCache::CacheData) |
857 | |
858 | class MySpyMemoryCache: public QAbstractNetworkCache |
859 | { |
860 | public: |
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 | |
915 | class DataReader: public QObject |
916 | { |
917 | Q_OBJECT |
918 | public: |
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 | |
930 | public 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 | |
949 | class SocketPair: public QObject |
950 | { |
951 | Q_OBJECT |
952 | public: |
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. |
988 | class BlockingTcpServer : public QTcpServer |
989 | { |
990 | Q_OBJECT |
991 | public: |
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 | } |
1023 | private 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 | |
1032 | private: |
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. |
1040 | class 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; |
1050 | public: |
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 | |
1111 | protected: |
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 | } |
1196 | signals: |
1197 | void dataReady(); |
1198 | void notEnoughData(); |
1199 | }; |
1200 | |
1201 | class RateControlledReader: public QObject |
1202 | { |
1203 | Q_OBJECT |
1204 | QIODevice *device; |
1205 | int bytesToRead; |
1206 | int interval; |
1207 | int readBufferSize; |
1208 | public: |
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 | |
1248 | private slots: |
1249 | void slotDataReady() |
1250 | { |
1251 | //qDebug() << "RateControlledReader: ready to go"; |
1252 | startTimer(interval); |
1253 | } |
1254 | |
1255 | protected: |
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 | |
1291 | tst_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 | |
1336 | tst_QNetworkReply::~tst_QNetworkReply() |
1337 | { |
1338 | } |
1339 | |
1340 | |
1341 | void tst_QNetworkReply::authenticationRequired(QNetworkReply*, QAuthenticator* auth) |
1342 | { |
1343 | auth->setUser("httptest" ); |
1344 | auth->setPassword("httptest" ); |
1345 | } |
1346 | |
1347 | void tst_QNetworkReply::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator* auth) |
1348 | { |
1349 | auth->setUser("qsockstest" ); |
1350 | auth->setPassword("password" ); |
1351 | } |
1352 | |
1353 | #ifndef QT_NO_SSL |
1354 | void tst_QNetworkReply::sslErrors(QNetworkReply *reply, const QList<QSslError> &errors) |
1355 | { |
1356 | reply->ignoreSslErrors(); |
1357 | QVERIFY(!errors.isEmpty()); |
1358 | QVERIFY(!reply->sslConfiguration().isNull()); |
1359 | } |
1360 | |
1361 | void 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 | |
1370 | QString 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 | |
1402 | QString 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 | |
1470 | QString 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 | |
1496 | static 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 | |
1510 | int 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 | |
1533 | void tst_QNetworkReply::finished() |
1534 | { |
1535 | if (loop) |
1536 | loop->exit(returnCode: returnCode = Success); |
1537 | } |
1538 | |
1539 | void 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 | |
1546 | void 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 | |
1599 | void 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 | |
1612 | void 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 | |
1635 | void 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 | |
1664 | void 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 | |
1678 | void 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 | |
1754 | void 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 | |
1774 | void 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 | |
1782 | void 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 | |
1829 | void 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 | |
1842 | void 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 | |
1868 | void 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 | |
1882 | void 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 | |
1901 | void 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 | |
1921 | void 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 | |
1939 | void 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 |
1967 | void 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 | |
2018 | void 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 | |
2057 | void 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 | |
2116 | static 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 | |
2126 | void 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 | |
2175 | static inline QByteArray md5sum(const QByteArray &data) |
2176 | { |
2177 | return QCryptographicHash::hash(data, method: QCryptographicHash::Md5); |
2178 | } |
2179 | |
2180 | void 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 | |
2208 | void 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 | |
2231 | void tst_QNetworkReply::putToFtp_data() |
2232 | { |
2233 | putToFile_data(); |
2234 | } |
2235 | |
2236 | void 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 | |
2282 | void 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 | |
2304 | void tst_QNetworkReply::putToHttp_data() |
2305 | { |
2306 | putToFile_data(); |
2307 | } |
2308 | |
2309 | void 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 | |
2340 | void tst_QNetworkReply::putToHttpSynchronous_data() |
2341 | { |
2342 | uniqueExtension = createUniqueExtension(); |
2343 | putToFile_data(); |
2344 | } |
2345 | |
2346 | void 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 | |
2381 | void tst_QNetworkReply::postToHttp_data() |
2382 | { |
2383 | putToFile_data(); |
2384 | } |
2385 | |
2386 | void 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 | |
2408 | void tst_QNetworkReply::postToHttpSynchronous_data() |
2409 | { |
2410 | putToFile_data(); |
2411 | } |
2412 | |
2413 | void 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 | |
2440 | void 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 | |
2668 | void 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 | |
2707 | void 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 | |
2750 | void tst_QNetworkReply::putToHttpMultipart_data() |
2751 | { |
2752 | postToHttpMultipart_data(); |
2753 | } |
2754 | |
2755 | void 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 |
2796 | void tst_QNetworkReply::putToHttps_data() |
2797 | { |
2798 | uniqueExtension = createUniqueExtension(); |
2799 | putToFile_data(); |
2800 | } |
2801 | |
2802 | void 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 | |
2837 | void tst_QNetworkReply::putToHttpsSynchronous_data() |
2838 | { |
2839 | uniqueExtension = createUniqueExtension(); |
2840 | putToFile_data(); |
2841 | } |
2842 | |
2843 | void 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 | |
2882 | void tst_QNetworkReply::postToHttps_data() |
2883 | { |
2884 | putToFile_data(); |
2885 | } |
2886 | |
2887 | void 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 | |
2913 | void tst_QNetworkReply::postToHttpsSynchronous_data() |
2914 | { |
2915 | putToFile_data(); |
2916 | } |
2917 | |
2918 | void 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 | |
2949 | void tst_QNetworkReply::postToHttpsMultipart_data() |
2950 | { |
2951 | postToHttpMultipart_data(); |
2952 | } |
2953 | |
2954 | void 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 | |
2999 | void 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 | |
3029 | void 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 | |
3042 | void 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 | |
3070 | void 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 | |
3105 | void 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 | |
3116 | void 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 | |
3148 | void 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 | |
3181 | void 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 | |
3199 | void 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 | |
3211 | void 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 | |
3235 | void tst_QNetworkReply::ioGetFromFileSpecial_data() |
3236 | { |
3237 | getFromFileSpecial_data(); |
3238 | } |
3239 | |
3240 | void 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 | |
3270 | void tst_QNetworkReply::ioGetFromFile_data() |
3271 | { |
3272 | putToFile_data(); |
3273 | } |
3274 | |
3275 | void 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 | |
3303 | void 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 | |
3314 | void 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 | |
3337 | void 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 | |
3370 | void 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 | |
3391 | void 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 | |
3423 | void 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 | |
3464 | void 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 | |
3509 | void 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 | |
3621 | void 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 |
3640 | void 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 | |
3723 | void 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 | |
3744 | void 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 |
3805 | void 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 | |
3833 | void 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 | |
3861 | void 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 | |
3882 | void 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 | |
3922 | void 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 | |
3940 | void 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 | |
3953 | void 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 | |
3971 | void tst_QNetworkReply::() |
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 | |
3977 | void tst_QNetworkReply::() |
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 | |
3993 | void 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 ; |
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 | |
4153 | void 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, ); |
4174 | QVERIFY(extraHttpHeaders.size() % 2 == 0); |
4175 | for (auto it = extraHttpHeaders.cbegin(), end = extraHttpHeaders.cend(); it != end; /*double-stepping*/) { |
4176 | QString = *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 |
4192 | void 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 | |
4390 | void 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 | |
4455 | void 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 | |
4465 | void 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 | |
4492 | void tst_QNetworkReply::ioPutToFileFromSocket_data() |
4493 | { |
4494 | putToFile_data(); |
4495 | } |
4496 | |
4497 | void 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 | |
4526 | void tst_QNetworkReply::ioPutToFileFromLocalSocket_data() |
4527 | { |
4528 | putToFile_data(); |
4529 | } |
4530 | |
4531 | void 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. |
4575 | void tst_QNetworkReply::ioPutToFileFromProcess_data() |
4576 | { |
4577 | #if QT_CONFIG(process) |
4578 | putToFile_data(); |
4579 | #endif |
4580 | } |
4581 | |
4582 | void 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 | |
4625 | void tst_QNetworkReply::ioPutToFtpFromFile_data() |
4626 | { |
4627 | ioPutToFileFromFile_data(); |
4628 | } |
4629 | |
4630 | void 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 | |
4674 | void tst_QNetworkReply::ioPutToHttpFromFile_data() |
4675 | { |
4676 | ioPutToFileFromFile_data(); |
4677 | } |
4678 | |
4679 | void 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 | |
4717 | void tst_QNetworkReply::ioPostToHttpFromFile_data() |
4718 | { |
4719 | ioPutToFileFromFile_data(); |
4720 | } |
4721 | |
4722 | void 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 |
4749 | void 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 | |
4791 | void 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 | |
4840 | void 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 | |
4868 | void 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. |
4903 | void 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 | |
4929 | void 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 | |
4958 | void 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 | |
4987 | void 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 |
5017 | class SslServer : public QTcpServer |
5018 | { |
5019 | Q_OBJECT |
5020 | public: |
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 | } |
5040 | signals: |
5041 | void newEncryptedConnection(QSslSocket *s); |
5042 | void newPlainConnection(QSslSocket *s); |
5043 | public 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 | |
5055 | public: |
5056 | QSslSocket *socket; |
5057 | bool m_ssl; |
5058 | }; |
5059 | |
5060 | // very similar to ioPostToHttpUploadProgress but for SSL |
5061 | void 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 | |
5125 | void 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 | |
5137 | void 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 | |
5221 | void 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 | |
5291 | void 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 | |
5344 | void 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 | |
5391 | void tst_QNetworkReply::() |
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 = reply->header(header: QNetworkRequest::LastModifiedHeader).toDateTime(); |
5404 | QCOMPARE(header, fileInfo.lastModified()); |
5405 | } |
5406 | |
5407 | void tst_QNetworkReply::() |
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 = 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 | |
5424 | void 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 |
5439 | void 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 |
5454 | void 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 | |
5500 | void 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 | |
5513 | class SlowReader : public QObject |
5514 | { |
5515 | Q_OBJECT |
5516 | public: |
5517 | SlowReader(QIODevice *dev) |
5518 | : device(dev) |
5519 | { |
5520 | connect(sender: device, SIGNAL(readyRead()), receiver: this, SLOT(deviceReady())); |
5521 | } |
5522 | private slots: |
5523 | void deviceReady() |
5524 | { |
5525 | QTimer::singleShot(msec: 100, receiver: this, SLOT(doRead())); |
5526 | } |
5527 | |
5528 | void doRead() |
5529 | { |
5530 | device->readAll(); |
5531 | } |
5532 | private: |
5533 | QIODevice *device; |
5534 | }; |
5535 | |
5536 | void 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 |
5572 | void tst_QNetworkReply::uploadProgress_data() |
5573 | { |
5574 | putToFile_data(); |
5575 | } |
5576 | #endif |
5577 | |
5578 | #ifdef QT_BUILD_INTERNAL |
5579 | void 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 | |
5613 | void tst_QNetworkReply::chaining_data() |
5614 | { |
5615 | putToFile_data(); |
5616 | } |
5617 | |
5618 | void 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 | |
5656 | void 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> , 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 | |
5703 | void 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 | |
5725 | void tst_QNetworkReply::receiveCookiesFromHttpSynchronous_data() |
5726 | { |
5727 | tst_QNetworkReply::receiveCookiesFromHttp_data(); |
5728 | } |
5729 | |
5730 | void 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 | |
5757 | void 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 | |
5798 | void 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 | |
5817 | void tst_QNetworkReply::sendCookiesSynchronous_data() |
5818 | { |
5819 | tst_QNetworkReply::sendCookies_data(); |
5820 | } |
5821 | |
5822 | void 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 | |
5846 | void 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 | |
5857 | void tst_QNetworkReply::notEnoughData() |
5858 | { |
5859 | notEnoughDataForFastSender = true; |
5860 | } |
5861 | |
5862 | void 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 |
5892 | void 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 | |
5910 | void 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 = 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 = proxyServer.receivedData.mid(index: uapos, len: uaend - uapos); |
5946 | QCOMPARE(uaheader, QByteArray("User-Agent: QNetworkReplyAutoTest/1.0" )); |
5947 | } |
5948 | |
5949 | class ProxyChangeHelper : public QObject |
5950 | { |
5951 | Q_OBJECT |
5952 | public: |
5953 | ProxyChangeHelper() : QObject(), signalCount(0) {}; |
5954 | public slots: |
5955 | void finishedSlot() |
5956 | { |
5957 | signalCount++; |
5958 | if (signalCount == 2) |
5959 | QMetaObject::invokeMethod(obj: &QTestEventLoop::instance(), member: "exitLoop" , type: Qt::QueuedConnection); |
5960 | } |
5961 | private: |
5962 | int signalCount; |
5963 | }; |
5964 | |
5965 | void tst_QNetworkReply::httpProxyCommandsSynchronous_data() |
5966 | { |
5967 | httpProxyCommands_data(); |
5968 | } |
5969 | #endif // !QT_NO_NETWORKPROXY |
5970 | |
5971 | struct 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 | |
5983 | struct QDeleteLaterCleanup |
5984 | { |
5985 | static inline void cleanup(QObject *o) |
5986 | { |
5987 | o->deleteLater(); |
5988 | } |
5989 | }; |
5990 | |
5991 | #ifndef QT_NO_NETWORKPROXY |
5992 | void 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 = proxyServer->receivedData.left(len: expectedCommand.length()); |
6024 | QCOMPARE(receivedHeader, expectedCommand); |
6025 | } |
6026 | |
6027 | void 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 | |
6083 | void 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 | |
6106 | void 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 | |
6134 | void 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 | |
6167 | void 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 | |
6174 | void 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 | |
6215 | class HttpReUsingConnectionFromFinishedSlot : public QObject |
6216 | { |
6217 | Q_OBJECT |
6218 | public: |
6219 | QNetworkReply* reply1; |
6220 | QNetworkReply* reply2; |
6221 | QUrl url; |
6222 | QNetworkAccessManager manager; |
6223 | public 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 | |
6241 | void tst_QNetworkReply::httpReUsingConnectionFromFinishedSlot_data() |
6242 | { |
6243 | httpReUsingConnectionSequential_data(); |
6244 | } |
6245 | |
6246 | void 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 | |
6273 | class HttpRecursiveCreationHelper : public QObject |
6274 | { |
6275 | Q_OBJECT |
6276 | public: |
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; |
6289 | public 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 | |
6331 | void 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 |
6342 | void 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 | |
6363 | void 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 | |
6378 | void tst_QNetworkReply::ignoreSslErrorsListWithSlot_data() |
6379 | { |
6380 | ignoreSslErrorsList_data(); |
6381 | } |
6382 | |
6383 | // this is not a test, just a slot called in the test below |
6384 | void 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 |
6390 | void 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 | |
6409 | void 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 | |
6424 | void 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 | |
6442 | void 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 | |
6474 | void 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 | |
6490 | void 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 | |
6497 | void 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 | |
6536 | void 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 | |
6561 | void 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 | |
6568 | void 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 | |
6627 | void 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 | |
6635 | void 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 |
6661 | void 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 | |
6676 | void 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. |
6684 | void 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 |
6724 | class 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 | |
6734 | public: |
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 | |
6745 | public 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 | |
6801 | class GetFromHttpIntoBuffer2Client : QObject |
6802 | { |
6803 | Q_OBJECT |
6804 | private: |
6805 | bool useDownloadBuffer; |
6806 | QNetworkReply *reply; |
6807 | qint64 uploadSize; |
6808 | QList<qint64> bytesAvailableList; |
6809 | public: |
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 | |
6861 | void 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. |
6871 | void 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 | |
6896 | void tst_QNetworkReply::getFromHttpIntoBufferCanReadLine() |
6897 | { |
6898 | QString ("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 |
6922 | void 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 |
6939 | void 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. |
6964 | void tst_QNetworkReply::qtbug12908compressedHttpReply() |
6965 | { |
6966 | QString ("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 | |
6986 | void tst_QNetworkReply::compressedHttpReplyBrokenGzip() |
6987 | { |
6988 | QString ("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 |
7008 | void 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 | |
7062 | void 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 |
7087 | void 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 | |
7106 | class AuthenticationCacheHelper : public QObject |
7107 | { |
7108 | Q_OBJECT |
7109 | public: |
7110 | AuthenticationCacheHelper() |
7111 | {} |
7112 | public 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 | } |
7133 | public: |
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 | */ |
7147 | void 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 | |
7276 | void 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 | |
7309 | class QtBug13431Helper : public QObject |
7310 | { |
7311 | Q_OBJECT |
7312 | public: |
7313 | QNetworkReply* m_reply; |
7314 | QTimer m_dlTimer; |
7315 | public 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 | |
7334 | void 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 | |
7359 | void 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 | |
7409 | void 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 | |
7428 | void 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 |
7448 | void 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 | |
7467 | class QtBug27161Helper : public QObject |
7468 | { |
7469 | Q_OBJECT |
7470 | public: |
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 | } |
7477 | public 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 | |
7499 | private: |
7500 | MiniHttpServer & m_server; |
7501 | QByteArray m_data; |
7502 | QTimer m_Timer; |
7503 | }; |
7504 | |
7505 | void tst_QNetworkReply::(){ |
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 | */ |
7521 | void tst_QNetworkReply::(){ |
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 | |
7542 | void 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 | |
7667 | void 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 (&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 | |
7699 | void 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 |
7743 | void 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 (&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 |
7798 | void 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 (&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 | |
7818 | class HttpAbortHelper : public QObject |
7819 | { |
7820 | Q_OBJECT |
7821 | public: |
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 | |
7833 | public slots: |
7834 | void readyRead() |
7835 | { |
7836 | mReply->abort(); |
7837 | QMetaObject::invokeMethod(obj: &QTestEventLoop::instance(), member: "exitLoop" , type: Qt::QueuedConnection); |
7838 | } |
7839 | |
7840 | private: |
7841 | QNetworkReply *mReply; |
7842 | }; |
7843 | |
7844 | void 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 | |
7876 | void 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 | |
7907 | void 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 | |
7924 | void 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 | |
8001 | void 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 | |
8015 | void 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 | |
8038 | void tst_QNetworkReply::emitErrorForAllRepliesSlot() { |
8039 | static int a = 0; |
8040 | if (++a == 3) |
8041 | QTestEventLoop::instance().exitLoop(); |
8042 | } |
8043 | |
8044 | void 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 | |
8051 | void 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 | |
8066 | void 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 | |
8076 | void 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 | |
8093 | void 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 |
8125 | void 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 |
8159 | void 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 |
8199 | void 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 |
8225 | void 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 |
8277 | void 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 |
8295 | void 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 | |
8340 | class RateLimitedUploadDevice : public QIODevice |
8341 | { |
8342 | Q_OBJECT |
8343 | public: |
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); } |
8391 | protected 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 | |
8402 | void 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 | |
8426 | void 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 | |
8470 | void 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 | |
8519 | void 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 | |
8536 | void 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 | |
8576 | struct 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 | |
8597 | void 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 | |
8614 | void 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 | |
8652 | void 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 | |
8694 | void 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 | |
8734 | void 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 | |
8743 | void 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 | |
8785 | void tst_QNetworkReply::ioHttpCookiesDuringRedirect() |
8786 | { |
8787 | MiniHttpServer target(httpEmpty200Response, false); |
8788 | |
8789 | const QString = 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 | |
8813 | void 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 | |
8825 | void 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 | |
8852 | void 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 | |
8879 | void 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 | |
8916 | void 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 | |
8945 | void tst_QNetworkReply::ioHttpRedirectMultipartPost_data() |
8946 | { |
8947 | postToHttpMultipart_data(); |
8948 | } |
8949 | |
8950 | void 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 | |
9006 | void 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 | |
9028 | void 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 | |
9050 | void 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 | */ |
9069 | void 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 | |
9121 | class PutWithServerClosingConnectionImmediatelyHandler: public QObject |
9122 | { |
9123 | Q_OBJECT |
9124 | public: |
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 | } |
9135 | signals: |
9136 | void correctFileUploadReceived(); |
9137 | void corruptFileUploadReceived(); |
9138 | |
9139 | public 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 | |
9184 | class PutWithServerClosingConnectionImmediatelyServer: public SslServer |
9185 | { |
9186 | Q_OBJECT |
9187 | public: |
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 | |
9201 | public 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 | |
9222 | void 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 | |
9278 | void 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 | |
9294 | void 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 | |
9349 | void tst_QNetworkReply::autoDeleteReplies_data() |
9350 | { |
9351 | autoDeleteRepliesAttribute_data(); |
9352 | } |
9353 | |
9354 | void 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 | |
9438 | void 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 | |
9476 | void 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! |
9517 | void 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 | |
9524 | QTEST_MAIN(tst_QNetworkReply) |
9525 | |
9526 | #include "tst_qnetworkreply.moc" |
9527 | |