1/****************************************************************************
2**
3** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
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 <QtNetwork/QNetworkAccessManager>
32#include <QtNetwork/QNetworkReply>
33#include <QtNetwork/QHttpPart>
34#include <QtNetwork/QHttpMultiPart>
35#include <QtNetwork/QNetworkProxy>
36#include <QtNetwork/QAuthenticator>
37#if defined(QT_BUILD_INTERNAL) && !defined(QT_NO_OPENSSL)
38#include <QtNetwork/private/qsslsocket_openssl_p.h>
39#endif // QT_BUILD_INTERNAL && !QT_NO_OPENSSL
40
41#include "../../../network-settings.h"
42
43Q_DECLARE_METATYPE(QAuthenticator*)
44
45class tst_Spdy: public QObject
46{
47 Q_OBJECT
48
49public:
50 tst_Spdy();
51 ~tst_Spdy();
52
53private Q_SLOTS:
54 void initTestCase();
55 void settingsAndNegotiation_data();
56 void settingsAndNegotiation();
57#ifndef QT_NO_NETWORKPROXY
58 void download_data();
59 void download();
60#endif // !QT_NO_NETWORKPROXY
61 void headerFields();
62#ifndef QT_NO_NETWORKPROXY
63 void upload_data();
64 void upload();
65 void errors_data();
66 void errors();
67#endif // !QT_NO_NETWORKPROXY
68 void multipleRequests_data();
69 void multipleRequests();
70
71private:
72 QNetworkAccessManager m_manager;
73 int m_multipleRequestsCount;
74 int m_multipleRepliesFinishedCount;
75 const QString m_rfc3252FilePath;
76
77protected Q_SLOTS:
78 void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *authenticator);
79 void multipleRequestsFinishedSlot();
80};
81
82tst_Spdy::tst_Spdy()
83 : m_rfc3252FilePath(QFINDTESTDATA("../qnetworkreply/rfc3252.txt"))
84{
85#if defined(QT_BUILD_INTERNAL) && !defined(QT_NO_SSL) && OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
86 qRegisterMetaType<QNetworkReply *>(); // for QSignalSpy
87 qRegisterMetaType<QAuthenticator *>();
88
89 connect(sender: &m_manager, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)),
90 receiver: this, SLOT(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)));
91#else
92 QSKIP("Qt built withouth OpenSSL, or the OpenSSL version is too old");
93#endif // defined(QT_BUILD_INTERNAL) && !defined(QT_NO_SSL) ...
94}
95
96tst_Spdy::~tst_Spdy()
97{
98}
99
100void tst_Spdy::initTestCase()
101{
102 QVERIFY(!m_rfc3252FilePath.isEmpty());
103 if (!QtNetworkSettings::verifyTestNetworkSettings())
104 QSKIP("No network test server available");
105}
106
107void tst_Spdy::settingsAndNegotiation_data()
108{
109 QTest::addColumn<QUrl>(name: "url");
110 QTest::addColumn<bool>(name: "setAttribute");
111 QTest::addColumn<bool>(name: "enabled");
112 QTest::addColumn<QByteArray>(name: "expectedProtocol");
113 QTest::addColumn<QByteArray>(name: "expectedContent");
114
115 QTest::newRow(dataTag: "default-settings") << QUrl("https://" + QtNetworkSettings::serverName()
116 + "/qtest/cgi-bin/echo.cgi?1")
117 << false << false << QByteArray()
118 << QByteArray("1");
119
120 QTest::newRow(dataTag: "http-url") << QUrl("http://" + QtNetworkSettings::serverName()
121 + "/qtest/cgi-bin/echo.cgi?1")
122 << true << true << QByteArray()
123 << QByteArray("1");
124
125 QTest::newRow(dataTag: "spdy-disabled") << QUrl("https://" + QtNetworkSettings::serverName()
126 + "/qtest/cgi-bin/echo.cgi?1")
127 << true << false << QByteArray()
128 << QByteArray("1");
129
130#ifndef QT_NO_OPENSSL
131 QTest::newRow(dataTag: "spdy-enabled") << QUrl("https://" + QtNetworkSettings::serverName()
132 + "/qtest/cgi-bin/echo.cgi?1")
133 << true << true << QByteArray(QSslConfiguration::NextProtocolSpdy3_0)
134 << QByteArray("1");
135#endif // QT_NO_OPENSSL
136}
137
138void tst_Spdy::settingsAndNegotiation()
139{
140 QFETCH(QUrl, url);
141 QFETCH(bool, setAttribute);
142 QFETCH(bool, enabled);
143
144 QNetworkRequest request(url);
145
146 if (setAttribute) {
147 request.setAttribute(code: QNetworkRequest::SpdyAllowedAttribute, value: QVariant(enabled));
148 }
149
150 QNetworkReply *reply = m_manager.get(request);
151 reply->ignoreSslErrors();
152 QSignalSpy metaDataChangedSpy(reply, SIGNAL(metaDataChanged()));
153 QSignalSpy readyReadSpy(reply, SIGNAL(readyRead()));
154 QSignalSpy finishedSpy(reply, SIGNAL(finished()));
155
156 QObject::connect(sender: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
157 QSignalSpy finishedManagerSpy(&m_manager, SIGNAL(finished(QNetworkReply*)));
158
159 QTestEventLoop::instance().enterLoop(secs: 15);
160 QVERIFY(!QTestEventLoop::instance().timeout());
161
162 QFETCH(QByteArray, expectedProtocol);
163
164#ifndef QT_NO_OPENSSL
165 bool expectedSpdyUsed = (expectedProtocol == QSslConfiguration::NextProtocolSpdy3_0);
166 QCOMPARE(reply->attribute(QNetworkRequest::SpdyWasUsedAttribute).toBool(), expectedSpdyUsed);
167#endif // QT_NO_OPENSSL
168
169 QCOMPARE(metaDataChangedSpy.count(), 1);
170 QCOMPARE(finishedSpy.count(), 1);
171
172 int statusCode = reply->attribute(code: QNetworkRequest::HttpStatusCodeAttribute).toInt();
173 QCOMPARE(statusCode, 200);
174
175 QByteArray content = reply->readAll();
176
177 QFETCH(QByteArray, expectedContent);
178 QCOMPARE(expectedContent, content);
179
180#ifndef QT_NO_OPENSSL
181 QSslConfiguration::NextProtocolNegotiationStatus expectedStatus =
182 (expectedProtocol.isEmpty())
183 ? QSslConfiguration::NextProtocolNegotiationNone
184 : QSslConfiguration::NextProtocolNegotiationNegotiated;
185 QCOMPARE(reply->sslConfiguration().nextProtocolNegotiationStatus(),
186 expectedStatus);
187
188 QCOMPARE(reply->sslConfiguration().nextNegotiatedProtocol(), expectedProtocol);
189#endif // QT_NO_OPENSSL
190}
191
192void tst_Spdy::proxyAuthenticationRequired(const QNetworkProxy &/*proxy*/,
193 QAuthenticator *authenticator)
194{
195 authenticator->setUser("qsockstest");
196 authenticator->setPassword("password");
197}
198
199#ifndef QT_NO_NETWORKPROXY
200void tst_Spdy::download_data()
201{
202 QTest::addColumn<QUrl>(name: "url");
203 QTest::addColumn<QString>(name: "fileName");
204 QTest::addColumn<QNetworkProxy>(name: "proxy");
205
206 QTest::newRow(dataTag: "mediumfile") << QUrl("https://" + QtNetworkSettings::serverName()
207 + "/qtest/rfc3252.txt")
208 << m_rfc3252FilePath
209 << QNetworkProxy();
210
211 QHostInfo hostInfo = QHostInfo::fromName(name: QtNetworkSettings::serverName());
212 QString proxyserver = hostInfo.addresses().first().toString();
213
214 QTest::newRow(dataTag: "mediumfile-http-proxy") << QUrl("https://" + QtNetworkSettings::serverName()
215 + "/qtest/rfc3252.txt")
216 << m_rfc3252FilePath
217 << QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3128);
218
219 QTest::newRow(dataTag: "mediumfile-http-proxy-auth") << QUrl("https://" + QtNetworkSettings::serverName()
220 + "/qtest/rfc3252.txt")
221 << m_rfc3252FilePath
222 << QNetworkProxy(QNetworkProxy::HttpProxy,
223 proxyserver, 3129);
224
225 QTest::newRow(dataTag: "mediumfile-socks-proxy") << QUrl("https://" + QtNetworkSettings::serverName()
226 + "/qtest/rfc3252.txt")
227 << m_rfc3252FilePath
228 << QNetworkProxy(QNetworkProxy::Socks5Proxy, proxyserver, 1080);
229
230 QTest::newRow(dataTag: "mediumfile-socks-proxy-auth") << QUrl("https://" + QtNetworkSettings::serverName()
231 + "/qtest/rfc3252.txt")
232 << m_rfc3252FilePath
233 << QNetworkProxy(QNetworkProxy::Socks5Proxy,
234 proxyserver, 1081);
235
236 QTest::newRow(dataTag: "bigfile") << QUrl("https://" + QtNetworkSettings::serverName()
237 + "/qtest/bigfile")
238 << QFINDTESTDATA("../qnetworkreply/bigfile")
239 << QNetworkProxy();
240}
241
242void tst_Spdy::download()
243{
244 QFETCH(QUrl, url);
245 QFETCH(QString, fileName);
246 QFETCH(QNetworkProxy, proxy);
247
248 QNetworkRequest request(url);
249 request.setAttribute(code: QNetworkRequest::SpdyAllowedAttribute, value: true);
250
251 if (proxy.type() != QNetworkProxy::DefaultProxy) {
252 m_manager.setProxy(proxy);
253 }
254 QNetworkReply *reply = m_manager.get(request);
255 reply->ignoreSslErrors();
256 QSignalSpy metaDataChangedSpy(reply, SIGNAL(metaDataChanged()));
257 QSignalSpy downloadProgressSpy(reply, SIGNAL(downloadProgress(qint64, qint64)));
258 QSignalSpy readyReadSpy(reply, SIGNAL(readyRead()));
259 QSignalSpy finishedSpy(reply, SIGNAL(finished()));
260
261 QObject::connect(sender: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
262 QSignalSpy finishedManagerSpy(&m_manager, SIGNAL(finished(QNetworkReply*)));
263 QSignalSpy proxyAuthRequiredSpy(&m_manager, SIGNAL(
264 proxyAuthenticationRequired(const QNetworkProxy &,
265 QAuthenticator *)));
266
267 QTestEventLoop::instance().enterLoop(secs: 15);
268 QVERIFY(!QTestEventLoop::instance().timeout());
269
270 QCOMPARE(finishedManagerSpy.count(), 1);
271 QCOMPARE(metaDataChangedSpy.count(), 1);
272 QCOMPARE(finishedSpy.count(), 1);
273 QVERIFY(downloadProgressSpy.count() > 0);
274 QVERIFY(readyReadSpy.count() > 0);
275
276 QVERIFY(proxyAuthRequiredSpy.count() <= 1);
277
278 QCOMPARE(reply->error(), QNetworkReply::NoError);
279 QCOMPARE(reply->attribute(QNetworkRequest::SpdyWasUsedAttribute).toBool(), true);
280 QCOMPARE(reply->attribute(QNetworkRequest::ConnectionEncryptedAttribute).toBool(), true);
281 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
282
283 QFile file(fileName);
284 QVERIFY(file.open(QIODevice::ReadOnly));
285
286 qint64 contentLength = reply->header(header: QNetworkRequest::ContentLengthHeader).toLongLong();
287 qint64 expectedContentLength = file.bytesAvailable();
288 QCOMPARE(contentLength, expectedContentLength);
289
290 QByteArray expectedContent = file.readAll();
291 QByteArray content = reply->readAll();
292 QCOMPARE(content, expectedContent);
293
294 reply->deleteLater();
295 m_manager.setProxy(QNetworkProxy()); // reset
296}
297#endif // !QT_NO_NETWORKPROXY
298
299void tst_Spdy::headerFields()
300{
301 QUrl url(QUrl("https://" + QtNetworkSettings::serverName()));
302 QNetworkRequest request(url);
303 request.setAttribute(code: QNetworkRequest::SpdyAllowedAttribute, value: true);
304
305 QNetworkReply *reply = m_manager.get(request);
306 reply->ignoreSslErrors();
307
308 QObject::connect(sender: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
309
310 QTestEventLoop::instance().enterLoop(secs: 15);
311 QVERIFY(!QTestEventLoop::instance().timeout());
312
313 QCOMPARE(reply->rawHeader("Content-Type"), QByteArray("text/html"));
314 QVERIFY(reply->rawHeader("Content-Length").toInt() > 0);
315 QVERIFY(reply->rawHeader("server").contains("Apache"));
316
317 QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader).toByteArray(), QByteArray("text/html"));
318 QVERIFY(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong() > 0);
319 QVERIFY(reply->header(QNetworkRequest::LastModifiedHeader).toDateTime().isValid());
320 QVERIFY(reply->header(QNetworkRequest::ServerHeader).toByteArray().contains("Apache"));
321}
322
323static inline QByteArray md5sum(const QByteArray &data)
324{
325 return QCryptographicHash::hash(data, method: QCryptographicHash::Md5).toHex().append(c: '\n');
326}
327
328#ifndef QT_NO_NETWORKPROXY
329void tst_Spdy::upload_data()
330{
331 QTest::addColumn<QUrl>(name: "url");
332 QTest::addColumn<QByteArray>(name: "data");
333 QTest::addColumn<QByteArray>(name: "uploadMethod");
334 QTest::addColumn<QObject *>(name: "uploadObject");
335 QTest::addColumn<QByteArray>(name: "md5sum");
336 QTest::addColumn<QNetworkProxy>(name: "proxy");
337
338
339 // 1. test uploading of byte arrays
340
341 QUrl md5Url("https://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi");
342
343 QByteArray data;
344 data = "";
345 QObject *dummyObject = 0;
346 QTest::newRow(dataTag: "empty") << md5Url << data << QByteArray("POST") << dummyObject
347 << md5sum(data) << QNetworkProxy();
348
349 data = "This is a normal message.";
350 QTest::newRow(dataTag: "generic") << md5Url << data << QByteArray("POST") << dummyObject
351 << md5sum(data) << QNetworkProxy();
352
353 data = "This is a message to show that Qt rocks!\r\n\n";
354 QTest::newRow(dataTag: "small") << md5Url << data << QByteArray("POST") << dummyObject
355 << md5sum(data) << QNetworkProxy();
356
357 data = QByteArray("abcd\0\1\2\abcd",12);
358 QTest::newRow(dataTag: "with-nul") << md5Url << data << QByteArray("POST") << dummyObject
359 << md5sum(data) << QNetworkProxy();
360
361 data = QByteArray(4097, '\4');
362 QTest::newRow(dataTag: "4k+1") << md5Url << data << QByteArray("POST") << dummyObject
363 << md5sum(data)<< QNetworkProxy();
364
365 QHostInfo hostInfo = QHostInfo::fromName(name: QtNetworkSettings::serverName());
366 QString proxyserver = hostInfo.addresses().first().toString();
367
368 QTest::newRow(dataTag: "4k+1-with-http-proxy") << md5Url << data << QByteArray("POST") << dummyObject
369 << md5sum(data)
370 << QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3128);
371
372 QTest::newRow(dataTag: "4k+1-with-http-proxy-auth") << md5Url << data << QByteArray("POST") << dummyObject
373 << md5sum(data)
374 << QNetworkProxy(QNetworkProxy::HttpProxy,
375 proxyserver, 3129);
376
377 QTest::newRow(dataTag: "4k+1-with-socks-proxy") << md5Url << data << QByteArray("POST") << dummyObject
378 << md5sum(data)
379 << QNetworkProxy(QNetworkProxy::Socks5Proxy, proxyserver, 1080);
380
381 QTest::newRow(dataTag: "4k+1-with-socks-proxy-auth") << md5Url << data << QByteArray("POST") << dummyObject
382 << md5sum(data)
383 << QNetworkProxy(QNetworkProxy::Socks5Proxy,
384 proxyserver, 1081);
385
386 data = QByteArray(128*1024+1, '\177');
387 QTest::newRow(dataTag: "128k+1") << md5Url << data << QByteArray("POST") << dummyObject
388 << md5sum(data) << QNetworkProxy();
389
390 data = QByteArray(128*1024+1, '\177');
391 QTest::newRow(dataTag: "128k+1-put") << md5Url << data << QByteArray("PUT") << dummyObject
392 << md5sum(data) << QNetworkProxy();
393
394 data = QByteArray(2*1024*1024+1, '\177');
395 QTest::newRow(dataTag: "2MB+1") << md5Url << data << QByteArray("POST") << dummyObject
396 << md5sum(data) << QNetworkProxy();
397
398
399 // 2. test uploading of files
400
401 QFile *file = new QFile(m_rfc3252FilePath);
402 file->open(flags: QIODevice::ReadOnly);
403 QTest::newRow(dataTag: "file-26K") << md5Url << QByteArray() << QByteArray("POST")
404 << static_cast<QObject *>(file)
405 << QByteArray("b3e32ac459b99d3f59318f3ac31e4bee\n") << QNetworkProxy();
406
407 QFile *file2 = new QFile(QFINDTESTDATA("../qnetworkreply/image1.jpg"));
408 file2->open(flags: QIODevice::ReadOnly);
409 QTest::newRow(dataTag: "file-1MB") << md5Url << QByteArray() << QByteArray("POST")
410 << static_cast<QObject *>(file2)
411 << QByteArray("87ef3bb319b004ba9e5e9c9fa713776e\n") << QNetworkProxy();
412
413
414 // 3. test uploading of multipart
415
416 QUrl multiPartUrl("https://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/multipart.cgi");
417
418 QHttpPart imagePart31;
419 imagePart31.setHeader(header: QNetworkRequest::ContentTypeHeader, value: QVariant("image/jpeg"));
420 imagePart31.setHeader(header: QNetworkRequest::ContentDispositionHeader, value: QVariant("form-data; name=\"testImage1\""));
421 imagePart31.setRawHeader(headerName: "Content-Location", headerValue: "http://my.test.location.tld");
422 imagePart31.setRawHeader(headerName: "Content-ID", headerValue: "my@id.tld");
423 QFile *file31 = new QFile(QFINDTESTDATA("../qnetworkreply/image1.jpg"));
424 file31->open(flags: QIODevice::ReadOnly);
425 imagePart31.setBodyDevice(file31);
426 QHttpMultiPart *imageMultiPart3 = new QHttpMultiPart(QHttpMultiPart::FormDataType);
427 imageMultiPart3->append(httpPart: imagePart31);
428 file31->setParent(imageMultiPart3);
429 QHttpPart imagePart32;
430 imagePart32.setHeader(header: QNetworkRequest::ContentTypeHeader, value: QVariant("image/jpeg"));
431 imagePart32.setHeader(header: QNetworkRequest::ContentDispositionHeader, value: QVariant("form-data; name=\"testImage2\""));
432 QFile *file32 = new QFile(QFINDTESTDATA("../qnetworkreply/image2.jpg"));
433 file32->open(flags: QIODevice::ReadOnly);
434 imagePart32.setBodyDevice(file31); // check that resetting works
435 imagePart32.setBodyDevice(file32);
436 imageMultiPart3->append(httpPart: imagePart32);
437 file32->setParent(imageMultiPart3);
438 QHttpPart imagePart33;
439 imagePart33.setHeader(header: QNetworkRequest::ContentTypeHeader, value: QVariant("image/jpeg"));
440 imagePart33.setHeader(header: QNetworkRequest::ContentDispositionHeader, value: QVariant("form-data; name=\"testImage3\""));
441 QFile *file33 = new QFile(QFINDTESTDATA("../qnetworkreply/image3.jpg"));
442 file33->open(flags: QIODevice::ReadOnly);
443 imagePart33.setBodyDevice(file33);
444 imageMultiPart3->append(httpPart: imagePart33);
445 file33->setParent(imageMultiPart3);
446 QByteArray expectedData = "content type: multipart/form-data; boundary=\""
447 + imageMultiPart3->boundary();
448 expectedData.append(s: "\"\nkey: testImage1, value: 87ef3bb319b004ba9e5e9c9fa713776e\n"
449 "key: testImage2, value: 483761b893f7fb1bd2414344cd1f3dfb\n"
450 "key: testImage3, value: ab0eb6fd4fcf8b4436254870b4513033\n");
451
452 QTest::newRow(dataTag: "multipart-3images") << multiPartUrl << QByteArray() << QByteArray("POST")
453 << static_cast<QObject *>(imageMultiPart3) << expectedData
454 << QNetworkProxy();
455}
456
457void tst_Spdy::upload()
458{
459 QFETCH(QUrl, url);
460 QNetworkRequest request(url);
461 request.setAttribute(code: QNetworkRequest::SpdyAllowedAttribute, value: true);
462
463 QFETCH(QByteArray, data);
464 QFETCH(QByteArray, uploadMethod);
465 QFETCH(QObject *, uploadObject);
466 QFETCH(QNetworkProxy, proxy);
467
468 if (proxy.type() != QNetworkProxy::DefaultProxy) {
469 m_manager.setProxy(proxy);
470 }
471
472 QNetworkReply *reply;
473 QHttpMultiPart *multiPart = 0;
474
475 if (uploadObject) {
476 // upload via device
477 if (QIODevice *device = qobject_cast<QIODevice *>(object: uploadObject)) {
478 reply = m_manager.post(request, data: device);
479 } else if ((multiPart = qobject_cast<QHttpMultiPart *>(object: uploadObject))) {
480 reply = m_manager.post(request, multiPart);
481 } else {
482 QFAIL("got unknown upload device");
483 }
484 } else {
485 // upload via byte array
486 if (uploadMethod == "PUT") {
487 reply = m_manager.put(request, data);
488 } else {
489 reply = m_manager.post(request, data);
490 }
491 }
492
493 reply->ignoreSslErrors();
494 QSignalSpy metaDataChangedSpy(reply, SIGNAL(metaDataChanged()));
495 QSignalSpy uploadProgressSpy(reply, SIGNAL(uploadProgress(qint64, qint64)));
496 QSignalSpy readyReadSpy(reply, SIGNAL(readyRead()));
497 QSignalSpy finishedSpy(reply, SIGNAL(finished()));
498
499 QObject::connect(sender: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
500 QSignalSpy finishedManagerSpy(&m_manager, SIGNAL(finished(QNetworkReply*)));
501
502 QTestEventLoop::instance().enterLoop(secs: 20);
503 QVERIFY(!QTestEventLoop::instance().timeout());
504
505 QCOMPARE(finishedManagerSpy.count(), 1);
506 QCOMPARE(metaDataChangedSpy.count(), 1);
507 QCOMPARE(finishedSpy.count(), 1);
508 QVERIFY(uploadProgressSpy.count() > 0);
509 QVERIFY(readyReadSpy.count() > 0);
510
511 QCOMPARE(reply->error(), QNetworkReply::NoError);
512 QCOMPARE(reply->attribute(QNetworkRequest::SpdyWasUsedAttribute).toBool(), true);
513 QCOMPARE(reply->attribute(QNetworkRequest::ConnectionEncryptedAttribute).toBool(), true);
514 QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
515
516 qint64 contentLength = reply->header(header: QNetworkRequest::ContentLengthHeader).toLongLong();
517 if (!multiPart) // script to test multiparts does not return a content length
518 QCOMPARE(contentLength, 33); // 33 bytes for md5 sums (including new line)
519
520 QFETCH(QByteArray, md5sum);
521 QByteArray content = reply->readAll();
522 QCOMPARE(content, md5sum);
523
524 reply->deleteLater();
525 if (uploadObject)
526 uploadObject->deleteLater();
527
528 m_manager.setProxy(QNetworkProxy()); // reset
529}
530
531void tst_Spdy::errors_data()
532{
533 QTest::addColumn<QUrl>(name: "url");
534 QTest::addColumn<QNetworkProxy>(name: "proxy");
535 QTest::addColumn<bool>(name: "ignoreSslErrors");
536 QTest::addColumn<int>(name: "expectedReplyError");
537
538 QTest::newRow(dataTag: "http-404") << QUrl("https://" + QtNetworkSettings::serverName() + "/non-existent-url")
539 << QNetworkProxy() << true << int(QNetworkReply::ContentNotFoundError);
540
541 QTest::newRow(dataTag: "ssl-errors") << QUrl("https://" + QtNetworkSettings::serverName())
542 << QNetworkProxy() << false << int(QNetworkReply::SslHandshakeFailedError);
543
544 QTest::newRow(dataTag: "host-not-found") << QUrl("https://this-host-does-not.exist")
545 << QNetworkProxy()
546 << true << int(QNetworkReply::HostNotFoundError);
547
548 QTest::newRow(dataTag: "proxy-not-found") << QUrl("https://" + QtNetworkSettings::serverName())
549 << QNetworkProxy(QNetworkProxy::HttpProxy,
550 "https://this-host-does-not.exist", 3128)
551 << true << int(QNetworkReply::HostNotFoundError);
552
553 QHostInfo hostInfo = QHostInfo::fromName(name: QtNetworkSettings::serverName());
554 QString proxyserver = hostInfo.addresses().first().toString();
555
556 QTest::newRow(dataTag: "proxy-unavailable") << QUrl("https://" + QtNetworkSettings::serverName())
557 << QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 10)
558 << true << int(QNetworkReply::UnknownNetworkError);
559
560 QTest::newRow(dataTag: "no-proxy-credentials") << QUrl("https://" + QtNetworkSettings::serverName())
561 << QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3129)
562 << true << int(QNetworkReply::ProxyAuthenticationRequiredError);
563}
564
565void tst_Spdy::errors()
566{
567 QFETCH(QUrl, url);
568 QFETCH(QNetworkProxy, proxy);
569 QFETCH(bool, ignoreSslErrors);
570 QFETCH(int, expectedReplyError);
571
572 QNetworkRequest request(url);
573 request.setAttribute(code: QNetworkRequest::SpdyAllowedAttribute, value: true);
574
575 disconnect(sender: &m_manager, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)),
576 receiver: 0, member: 0);
577 if (proxy.type() != QNetworkProxy::DefaultProxy) {
578 m_manager.setProxy(proxy);
579 }
580 QNetworkReply *reply = m_manager.get(request);
581 if (ignoreSslErrors)
582 reply->ignoreSslErrors();
583 QSignalSpy finishedSpy(reply, SIGNAL(finished()));
584 QSignalSpy errorSpy(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
585
586 QObject::connect(sender: reply, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop()));
587
588 QTestEventLoop::instance().enterLoop(secs: 15);
589 QVERIFY(!QTestEventLoop::instance().timeout());
590
591 QCOMPARE(finishedSpy.count(), 1);
592 QCOMPARE(errorSpy.count(), 1);
593
594 QCOMPARE(reply->error(), static_cast<QNetworkReply::NetworkError>(expectedReplyError));
595
596 m_manager.setProxy(QNetworkProxy()); // reset
597 m_manager.clearAccessCache(); // e.g. to get an SSL error we need a new connection
598 connect(sender: &m_manager, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)),
599 receiver: this, SLOT(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)),
600 Qt::UniqueConnection); // reset
601}
602#endif // !QT_NO_NETWORKPROXY
603
604void tst_Spdy::multipleRequests_data()
605{
606 QTest::addColumn<QList<QUrl> >(name: "urls");
607
608 QString baseUrl = "https://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/echo.cgi?";
609 QList<QUrl> urls;
610 for (int a = 1; a <= 50; ++a)
611 urls.append(t: QUrl(baseUrl + QLatin1String(QByteArray::number(a))));
612
613 QTest::newRow(dataTag: "one-request") << urls.mid(pos: 0, alength: 1);
614 QTest::newRow(dataTag: "two-requests") << urls.mid(pos: 0, alength: 2);
615 QTest::newRow(dataTag: "ten-requests") << urls.mid(pos: 0, alength: 10);
616 QTest::newRow(dataTag: "twenty-requests") << urls.mid(pos: 0, alength: 20);
617 QTest::newRow(dataTag: "fifty-requests") << urls;
618}
619
620void tst_Spdy::multipleRequestsFinishedSlot()
621{
622 m_multipleRepliesFinishedCount++;
623 if (m_multipleRepliesFinishedCount == m_multipleRequestsCount)
624 QTestEventLoop::instance().exitLoop();
625}
626
627void tst_Spdy::multipleRequests()
628{
629 QFETCH(QList<QUrl>, urls);
630 m_multipleRequestsCount = urls.count();
631 m_multipleRepliesFinishedCount = 0;
632
633 QList<QNetworkReply *> replies;
634 QList<QSignalSpy *> metaDataChangedSpies;
635 QList<QSignalSpy *> readyReadSpies;
636 QList<QSignalSpy *> finishedSpies;
637
638 foreach (const QUrl &url, urls) {
639 QNetworkRequest request(url);
640 request.setAttribute(code: QNetworkRequest::SpdyAllowedAttribute, value: true);
641 QNetworkReply *reply = m_manager.get(request);
642 replies.append(t: reply);
643 reply->ignoreSslErrors();
644 QObject::connect(sender: reply, SIGNAL(finished()), receiver: this, SLOT(multipleRequestsFinishedSlot()));
645 QSignalSpy *metaDataChangedSpy = new QSignalSpy(reply, SIGNAL(metaDataChanged()));
646 metaDataChangedSpies << metaDataChangedSpy;
647 QSignalSpy *readyReadSpy = new QSignalSpy(reply, SIGNAL(readyRead()));
648 readyReadSpies << readyReadSpy;
649 QSignalSpy *finishedSpy = new QSignalSpy(reply, SIGNAL(finished()));
650 finishedSpies << finishedSpy;
651 }
652
653 QSignalSpy finishedManagerSpy(&m_manager, SIGNAL(finished(QNetworkReply*)));
654
655 QTestEventLoop::instance().enterLoop(secs: 15);
656 QVERIFY(!QTestEventLoop::instance().timeout());
657
658 QCOMPARE(finishedManagerSpy.count(), m_multipleRequestsCount);
659
660 for (int a = 0; a < replies.count(); ++a) {
661
662#ifndef QT_NO_OPENSSL
663 QCOMPARE(replies.at(a)->sslConfiguration().nextProtocolNegotiationStatus(),
664 QSslConfiguration::NextProtocolNegotiationNegotiated);
665 QCOMPARE(replies.at(a)->sslConfiguration().nextNegotiatedProtocol(),
666 QByteArray(QSslConfiguration::NextProtocolSpdy3_0));
667#endif // QT_NO_OPENSSL
668
669 QCOMPARE(replies.at(a)->error(), QNetworkReply::NoError);
670 QCOMPARE(replies.at(a)->attribute(QNetworkRequest::SpdyWasUsedAttribute).toBool(), true);
671 QCOMPARE(replies.at(a)->attribute(QNetworkRequest::ConnectionEncryptedAttribute).toBool(), true);
672 QCOMPARE(replies.at(a)->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
673
674 // using the echo script, a request to "echo.cgi?1" will return a body of "1"
675 QByteArray expectedContent = replies.at(i: a)->url().query().toUtf8();
676 QByteArray content = replies.at(i: a)->readAll();
677 QCOMPARE(expectedContent, content);
678
679 QCOMPARE(metaDataChangedSpies.at(a)->count(), 1);
680 metaDataChangedSpies.at(i: a)->deleteLater();
681
682 QCOMPARE(finishedSpies.at(a)->count(), 1);
683 finishedSpies.at(i: a)->deleteLater();
684
685 QVERIFY(readyReadSpies.at(a)->count() > 0);
686 readyReadSpies.at(i: a)->deleteLater();
687
688 replies.at(i: a)->deleteLater();
689 }
690}
691
692QTEST_MAIN(tst_Spdy)
693
694#include "tst_spdy.moc"
695

source code of qtbase/tests/auto/network/access/spdy/tst_spdy.cpp