summaryrefslogtreecommitdiffstats
path: root/tests/auto/network
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/network')
-rw-r--r--tests/auto/network/access/access.pro8
-rw-r--r--tests/auto/network/access/hpack/hpack.pro6
-rw-r--r--tests/auto/network/access/hpack/tst_hpack.cpp852
-rw-r--r--tests/auto/network/access/http2/certs/fluke.cert75
-rw-r--r--tests/auto/network/access/http2/certs/fluke.key15
-rw-r--r--tests/auto/network/access/http2/http2.pro9
-rw-r--r--tests/auto/network/access/http2/http2srv.cpp611
-rw-r--r--tests/auto/network/access/http2/http2srv.h166
-rw-r--r--tests/auto/network/access/http2/tst_http2.cpp449
-rw-r--r--tests/auto/network/access/qftp/qftp.pro7
-rw-r--r--tests/auto/network/access/qftp/tst_qftp.cpp9
-rw-r--r--tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp182
-rw-r--r--tests/auto/network/access/qnetworkreply/qnetworkreply.pro2
-rw-r--r--tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp45
-rw-r--r--tests/auto/network/kernel/kernel.pro1
-rw-r--r--tests/auto/network/kernel/qhostaddress/qhostaddress.pro8
-rw-r--r--tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp50
-rw-r--r--tests/auto/network/kernel/qhostinfo/qhostinfo.pro6
-rw-r--r--tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp8
-rw-r--r--tests/auto/network/kernel/qnetworkdatagram/qnetworkdatagram.pro5
-rw-r--r--tests/auto/network/kernel/qnetworkdatagram/tst_qnetworkdatagram.cpp146
-rw-r--r--tests/auto/network/socket/platformsocketengine/platformsocketengine.pri8
-rw-r--r--tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp5
-rw-r--r--tests/auto/network/socket/qhttpsocketengine/tst_qhttpsocketengine.cpp4
-rw-r--r--tests/auto/network/socket/qlocalsocket/test/test.pro8
-rw-r--r--tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp2
-rw-r--r--tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp4
-rw-r--r--tests/auto/network/socket/qtcpserver/crashingServer/main.cpp8
-rw-r--r--tests/auto/network/socket/qtcpserver/test/test.pro11
-rw-r--r--tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp39
-rw-r--r--tests/auto/network/socket/qtcpsocket/qtcpsocket.pro2
-rw-r--r--tests/auto/network/socket/qtcpsocket/test/test.pro8
-rw-r--r--tests/auto/network/socket/qudpsocket/test/test.pro6
-rw-r--r--tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp121
-rw-r--r--tests/auto/network/ssl/qsslcertificate/qsslcertificate.pro2
-rw-r--r--tests/auto/network/ssl/qsslcipher/qsslcipher.pro2
-rw-r--r--tests/auto/network/ssl/qssldiffiehellmanparameters/qssldiffiehellmanparameters.pro8
-rw-r--r--tests/auto/network/ssl/qssldiffiehellmanparameters/tst_qssldiffiehellmanparameters.cpp163
-rw-r--r--tests/auto/network/ssl/qsslellipticcurve/qsslellipticcurve.pro2
-rw-r--r--tests/auto/network/ssl/qsslerror/qsslerror.pro2
-rw-r--r--tests/auto/network/ssl/qsslkey/qsslkey.pro2
-rw-r--r--tests/auto/network/ssl/qsslsocket/qsslsocket.pro12
-rw-r--r--tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp303
-rw-r--r--tests/auto/network/ssl/qsslsocket_onDemandCertificates_member/qsslsocket_onDemandCertificates_member.pro8
-rw-r--r--tests/auto/network/ssl/qsslsocket_onDemandCertificates_static/qsslsocket_onDemandCertificates_static.pro8
-rw-r--r--tests/auto/network/ssl/ssl.pro3
46 files changed, 3015 insertions, 386 deletions
diff --git a/tests/auto/network/access/access.pro b/tests/auto/network/access/access.pro
index bc76190e30..ef0aeac3c8 100644
--- a/tests/auto/network/access/access.pro
+++ b/tests/auto/network/access/access.pro
@@ -12,9 +12,17 @@ SUBDIRS=\
qftp \
qhttpnetworkreply \
qabstractnetworkcache \
+ hpack
!contains(QT_CONFIG, private_tests): SUBDIRS -= \
qhttpnetworkconnection \
qhttpnetworkreply \
qftp \
+ hpack
+contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) {
+ contains(QT_CONFIG, private_tests) {
+ SUBDIRS += \
+ http2
+ }
+}
diff --git a/tests/auto/network/access/hpack/hpack.pro b/tests/auto/network/access/hpack/hpack.pro
new file mode 100644
index 0000000000..3c8b8e7944
--- /dev/null
+++ b/tests/auto/network/access/hpack/hpack.pro
@@ -0,0 +1,6 @@
+QT += core core-private network network-private testlib
+CONFIG += testcase parallel_test c++14
+TEMPLATE = app
+TARGET = tst_hpack
+
+SOURCES += tst_hpack.cpp
diff --git a/tests/auto/network/access/hpack/tst_hpack.cpp b/tests/auto/network/access/hpack/tst_hpack.cpp
new file mode 100644
index 0000000000..bd337c9f5f
--- /dev/null
+++ b/tests/auto/network/access/hpack/tst_hpack.cpp
@@ -0,0 +1,852 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2014 Governikus GmbH & Co. KG.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtNetwork/private/bitstreams_p.h>
+#include <QtNetwork/private/hpack_p.h>
+
+#include <QtCore/qbytearray.h>
+
+#include <cstdlib>
+#include <vector>
+#include <string>
+
+QT_USE_NAMESPACE
+
+using namespace HPack;
+
+class tst_Hpack: public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_Hpack();
+private Q_SLOTS:
+ void bitstreamConstruction();
+ void bitstreamWrite();
+ void bitstreamReadWrite();
+ void bitstreamCompression();
+ void bitstreamErrors();
+
+ void lookupTableConstructor();
+
+ void lookupTableStatic_data();
+ void lookupTableStatic();
+ void lookupTableDynamic();
+
+ void hpackEncodeRequest_data();
+ void hpackEncodeRequest();
+ void hpackDecodeRequest_data();
+ void hpackDecodeRequest();
+
+ void hpackEncodeResponse_data();
+ void hpackEncodeResponse();
+ void hpackDecodeResponse_data();
+ void hpackDecodeResponse();
+
+ // TODO: more-more-more tests needed!
+
+private:
+ void hpackEncodeRequest(bool withHuffman);
+ void hpackEncodeResponse(bool withHuffman);
+
+ HttpHeader header1;
+ std::vector<uchar> buffer1;
+ BitOStream request1;
+
+ HttpHeader header2;
+ std::vector<uchar> buffer2;
+ BitOStream request2;
+
+ HttpHeader header3;
+ std::vector<uchar> buffer3;
+ BitOStream request3;
+};
+
+using StreamError = BitIStream::Error;
+
+tst_Hpack::tst_Hpack()
+ : request1(buffer1),
+ request2(buffer2),
+ request3(buffer3)
+{
+}
+
+void tst_Hpack::bitstreamConstruction()
+{
+ const uchar bytes[] = {0xDE, 0xAD, 0xBE, 0xEF};
+ const int size = int(sizeof bytes);
+
+ // Default ctors:
+ std::vector<uchar> buffer;
+ {
+ const BitOStream out(buffer);
+ QVERIFY(out.bitLength() == 0);
+ QVERIFY(out.byteLength() == 0);
+
+ const BitIStream in;
+ QVERIFY(in.bitLength() == 0);
+ QVERIFY(in.streamOffset() == 0);
+ QVERIFY(in.error() == StreamError::NoError);
+ }
+
+ // Create istream with some data:
+ {
+ BitIStream in(bytes, bytes + size);
+ QVERIFY(in.bitLength() == size * 8);
+ QVERIFY(in.streamOffset() == 0);
+ QVERIFY(in.error() == StreamError::NoError);
+ // 'Read' some data back:
+ for (int i = 0; i < size; ++i) {
+ uchar bitPattern = 0;
+ const auto bitsRead = in.peekBits(i * 8, 8, &bitPattern);
+ QVERIFY(bitsRead == 8);
+ QVERIFY(bitPattern == bytes[i]);
+ }
+ }
+
+ // Copy ctors:
+ {
+ // Ostreams - copy is disabled.
+ // Istreams:
+ const BitIStream in1;
+ const BitIStream in2(in1);
+ QVERIFY(in2.bitLength() == in1.bitLength());
+ QVERIFY(in2.streamOffset() == in1.streamOffset());
+ QVERIFY(in2.error() == StreamError::NoError);
+
+ const BitIStream in3(bytes, bytes + size);
+ const BitIStream in4(in3);
+ QVERIFY(in4.bitLength() == in3.bitLength());
+ QVERIFY(in4.streamOffset() == in3.streamOffset());
+ QVERIFY(in4.error() == StreamError::NoError);
+ }
+}
+
+void tst_Hpack::bitstreamWrite()
+{
+ // Known representations,
+ // https://http2.github.io/http2-spec/compression.html.
+ // 5.1 Integer Representation
+
+ // Test bit/byte lengths of the
+ // resulting data:
+ std::vector<uchar> buffer;
+ BitOStream out(buffer);
+ out.write(3);
+ // 11, fits into 8-bit prefix:
+ QVERIFY(out.bitLength() == 8);
+ QVERIFY(out.byteLength() == 1);
+ QVERIFY(out.begin()[0] == 3);
+
+ out.clear();
+ QVERIFY(out.bitLength() == 0);
+ QVERIFY(out.byteLength() == 0);
+
+ // This number does not fit into 8-bit
+ // prefix we'll need 2 bytes:
+ out.write(256);
+ QVERIFY(out.byteLength() == 2);
+ QVERIFY(out.bitLength() == 16);
+ QVERIFY(out.begin()[0] == 0xff);
+ QVERIFY(out.begin()[1] == 1);
+
+ out.clear();
+
+ // See 5.2 String Literal Representation.
+
+ // We use Huffman code,
+ // char 'a' has a prefix code 00011 (5 bits)
+ out.write(QByteArray("aaa", 3), true);
+ QVERIFY(out.byteLength() == 3);
+ QVERIFY(out.bitLength() == 24);
+ // Now we must have in our stream:
+ // 10000010 | 00011000| 11000111
+ const uchar *encoded = out.begin();
+ QVERIFY(encoded[0] == 0x82);
+ QVERIFY(encoded[1] == 0x18);
+ QVERIFY(encoded[2] == 0xC7);
+ // TODO: add more tests ...
+}
+
+void tst_Hpack::bitstreamReadWrite()
+{
+ // We can write into the bit stream:
+ // 1) bit patterns
+ // 2) integers (see HPACK, 5.1)
+ // 3) string (see HPACK, 5.2)
+ std::vector<uchar> buffer;
+ BitOStream out(buffer);
+ out.writeBits(0xf, 3);
+ QVERIFY(out.byteLength() == 1);
+ QVERIFY(out.bitLength() == 3);
+
+ // Now, read it back:
+ {
+ BitIStream in(out.begin(), out.end());
+ uchar bitPattern = 0;
+ const auto bitsRead = in.peekBits(0, 3, &bitPattern);
+ // peekBits pack into the most significant byte/bit:
+ QVERIFY(bitsRead == 3);
+ QVERIFY((bitPattern >> 5) == 7);
+ }
+
+ const quint32 testInt = 133;
+ out.write(testInt);
+
+ // This integer does not fit into the current 5-bit prefix,
+ // so byteLength == 2.
+ QVERIFY(out.byteLength() == 2);
+ const auto bitLength = out.bitLength();
+ QVERIFY(bitLength > 3);
+
+ // Now, read it back:
+ {
+ BitIStream in(out.begin(), out.end());
+ in.skipBits(3); // Bit pattern
+ quint32 value = 0;
+ QVERIFY(in.read(&value));
+ QVERIFY(in.error() == StreamError::NoError);
+ QCOMPARE(value, testInt);
+ }
+
+ const QByteArray testString("ABCDE", 5);
+ out.write(testString, true); // Compressed
+ out.write(testString, false); // Non-compressed
+ QVERIFY(out.byteLength() > 2);
+ QVERIFY(out.bitLength() > bitLength);
+
+ // Now, read it back:
+ {
+ BitIStream in(out.begin(), out.end());
+ in.skipBits(bitLength); // Bit pattern and integer
+ QByteArray value;
+ // Read compressed string first ...
+ QVERIFY(in.read(&value));
+ QCOMPARE(value, testString);
+ QCOMPARE(in.error(), StreamError::NoError);
+ // Now non-compressed ...
+ QVERIFY(in.read(&value));
+ QCOMPARE(value, testString);
+ QCOMPARE(in.error(), StreamError::NoError);
+ }
+}
+
+void tst_Hpack::bitstreamCompression()
+{
+ // Similar to bitstreamReadWrite but
+ // writes/reads a lot of mixed strings/integers.
+ std::vector<std::string> strings;
+ std::vector<quint32> integers;
+ std::vector<bool> isA; // integer or string.
+ const std::string bytes("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789()[]/*");
+ const unsigned nValues = 100000;
+
+ quint64 totalStringBytes = 0;
+ std::vector<uchar> buffer;
+ BitOStream out(buffer);
+ for (unsigned i = 0; i < nValues; ++i) {
+ const bool isString = std::rand() % 1000 > 500;
+ isA.push_back(isString);
+ if (!isString) {
+ integers.push_back(std::rand() % 1000);
+ out.write(integers.back());
+ } else {
+ const auto start = std::rand() % (bytes.length() / 2);
+ auto end = start * 2;
+ if (!end)
+ end = bytes.length() / 2;
+ strings.push_back(bytes.substr(start, end - start));
+ const auto &s = strings.back();
+ totalStringBytes += s.size();
+ QByteArray data(s.c_str(), int(s.size()));
+ const bool compressed(std::rand() % 1000 > 500);
+ out.write(data, compressed);
+ }
+ }
+
+ qDebug() << "Compressed(?) byte length:" << out.byteLength()
+ << "total string bytes:" << totalStringBytes;
+ qDebug() << "total integer bytes (for quint32):" << integers.size() * sizeof(quint32);
+
+ QVERIFY(out.byteLength() > 0);
+ QVERIFY(out.bitLength() > 0);
+
+ BitIStream in(out.begin(), out.end());
+
+ for (unsigned i = 0, iS = 0, iI = 0; i < nValues; ++i) {
+ if (isA[i]) {
+ QByteArray data;
+ QVERIFY(in.read(&data));
+ QCOMPARE(in.error(), StreamError::NoError);
+ QCOMPARE(data.toStdString(), strings[iS]);
+ ++iS;
+ } else {
+ quint32 value = 0;
+ QVERIFY(in.read(&value));
+ QCOMPARE(in.error(), StreamError::NoError);
+ QCOMPARE(value, integers[iI]);
+ ++iI;
+ }
+ }
+}
+
+void tst_Hpack::bitstreamErrors()
+{
+ {
+ BitIStream in;
+ quint32 val = 0;
+ QVERIFY(!in.read(&val));
+ QCOMPARE(in.error(), StreamError::NotEnoughData);
+ }
+ {
+ // Integer in a stream, that does not fit into quint32.
+ const uchar bytes[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ BitIStream in(bytes, bytes + sizeof bytes);
+ quint32 val = 0;
+ QVERIFY(!in.read(&val));
+ QCOMPARE(in.error(), StreamError::InvalidInteger);
+ }
+ {
+ const uchar byte = 0x82; // 1 - Huffman compressed, 2 - the (fake) byte length.
+ BitIStream in(&byte, &byte + 1);
+ QByteArray val;
+ QVERIFY(!in.read(&val));
+ QCOMPARE(in.error(), StreamError::NotEnoughData);
+ }
+}
+
+void tst_Hpack::lookupTableConstructor()
+{
+ {
+ FieldLookupTable nonIndexed(4096, false);
+ QVERIFY(nonIndexed.dynamicDataSize() == 0);
+ QVERIFY(nonIndexed.numberOfDynamicEntries() == 0);
+ QVERIFY(nonIndexed.numberOfStaticEntries() != 0);
+ QVERIFY(nonIndexed.numberOfStaticEntries() == nonIndexed.numberOfEntries());
+ // Now we add some fake field and verify what 'non-indexed' means ... no search
+ // by name.
+ QVERIFY(nonIndexed.prependField("custom-key", "custom-value"));
+ // 54: 10 + 12 in name/value pair above + 32 required by HPACK specs ...
+ QVERIFY(nonIndexed.dynamicDataSize() == 54);
+ QVERIFY(nonIndexed.numberOfDynamicEntries() == 1);
+ QCOMPARE(nonIndexed.numberOfEntries(), nonIndexed.numberOfStaticEntries() + 1);
+ // Should fail to find it (invalid index 0) - search is disabled.
+ QVERIFY(nonIndexed.indexOf("custom-key", "custom-value") == 0);
+ }
+ {
+ // "key" + "value" == 8 bytes, + 32 (HPACK's requirement) == 40.
+ // Let's ask for a max-size 32 so that entry does not fit:
+ FieldLookupTable nonIndexed(32, false);
+ QVERIFY(nonIndexed.prependField("key", "value"));
+ QVERIFY(nonIndexed.numberOfEntries() == nonIndexed.numberOfStaticEntries());
+ QVERIFY(nonIndexed.indexOf("key", "value") == 0);
+ }
+ {
+ FieldLookupTable indexed(4096, true);
+ QVERIFY(indexed.dynamicDataSize() == 0);
+ QVERIFY(indexed.numberOfDynamicEntries() == 0);
+ QVERIFY(indexed.numberOfStaticEntries() != 0);
+ QVERIFY(indexed.numberOfStaticEntries() == indexed.numberOfEntries());
+ QVERIFY(indexed.prependField("custom-key", "custom-value"));
+ QVERIFY(indexed.dynamicDataSize() == 54);
+ QVERIFY(indexed.numberOfDynamicEntries() == 1);
+ QVERIFY(indexed.numberOfEntries() == indexed.numberOfStaticEntries() + 1);
+ QVERIFY(indexed.indexOf("custom-key") == indexed.numberOfStaticEntries() + 1);
+ QVERIFY(indexed.indexOf("custom-key", "custom-value") == indexed.numberOfStaticEntries() + 1);
+ }
+}
+
+void tst_Hpack::lookupTableStatic_data()
+{
+ QTest::addColumn<QByteArray>("expectedName");
+ QTest::addColumn<QByteArray>("expectedValue");
+
+ // Some predefined fields to find
+ // (they are always defined/required by HPACK).
+ QTest::newRow(":authority|") << QByteArray(":authority") << QByteArray("");
+ QTest::newRow(":method|GET") << QByteArray(":method") << QByteArray("GET");
+ QTest::newRow(":method|POST") << QByteArray(":method") << QByteArray("POST");
+ QTest::newRow(":path|/") << QByteArray(":path") << QByteArray("/");
+ QTest::newRow(":path|/index.html") << QByteArray(":path") << QByteArray("/index.html");
+ QTest::newRow(":scheme|http") << QByteArray(":scheme") << QByteArray("http");
+ QTest::newRow(":scheme|https") << QByteArray(":scheme") << QByteArray("https");
+ QTest::newRow(":status|200") << QByteArray(":status") << QByteArray("200");
+ QTest::newRow(":status|204") << QByteArray(":status") << QByteArray("204");
+ QTest::newRow(":status|206") << QByteArray(":status") << QByteArray("206");
+ QTest::newRow(":status|304") << QByteArray(":status") << QByteArray("304");
+ QTest::newRow(":status|400") << QByteArray(":status") << QByteArray("400");
+ QTest::newRow(":status|404") << QByteArray(":status") << QByteArray("404");
+ QTest::newRow(":status|500") << QByteArray(":status") << QByteArray("500");
+}
+
+void tst_Hpack::lookupTableStatic()
+{
+ const FieldLookupTable table(0, false /*all static, no need in 'search index'*/);
+
+ QFETCH(QByteArray, expectedName);
+ QFETCH(QByteArray, expectedValue);
+
+ const quint32 index = table.indexOf(expectedName, expectedValue);
+ QVERIFY(index != 0);
+
+ QByteArray name, value;
+ QVERIFY(table.field(index, &name, &value));
+ QCOMPARE(name, expectedName);
+ QCOMPARE(value, expectedValue);
+}
+
+void tst_Hpack::lookupTableDynamic()
+{
+ // HPACK's table size:
+ // for every field -> size += field.name.length() + field.value.length() + 32.
+ // Let's set some size limit and try to fill table with enough entries to have several
+ // items evicted.
+ const quint32 tableSize = 8192;
+ const char stringData[] = "abcdefghijklmnopABCDEFGHIJKLMNOP0123456789()[]:";
+ const quint32 dataSize = sizeof stringData - 1;
+
+ FieldLookupTable table(tableSize, true);
+
+ std::vector<QByteArray> fieldsToFind;
+ quint32 evicted = 0;
+
+ while (true) {
+ // Strings are repeating way too often, I want to
+ // have at least some items really evicted and not found,
+ // therefore these weird dances with start/len.
+ const quint32 start = std::rand() % (dataSize - 10);
+ quint32 len = std::rand() % (dataSize - start);
+ if (!len)
+ len = 1;
+
+ const QByteArray val(stringData + start, len);
+ fieldsToFind.push_back(val);
+ const quint32 entriesBefore = table.numberOfDynamicEntries();
+ QVERIFY(table.prependField(val, val));
+ QVERIFY(table.indexOf(val));
+ QVERIFY(table.indexOf(val) == table.indexOf(val, val));
+ QByteArray fieldName, fieldValue;
+ table.field(table.indexOf(val), &fieldName, &fieldValue);
+
+ QVERIFY(val == fieldName);
+ QVERIFY(val == fieldValue);
+
+ if (table.numberOfDynamicEntries() <= entriesBefore) {
+ // We had to evict several items ...
+ evicted += entriesBefore - table.numberOfDynamicEntries() + 1;
+ if (evicted >= 200)
+ break;
+ }
+ }
+
+ QVERIFY(table.dynamicDataSize() <= tableSize);
+ QVERIFY(table.numberOfDynamicEntries() > 0);
+ QVERIFY(table.indexOf(fieldsToFind.back())); // We MUST have it in a table!
+
+ using size_type = std::vector<QByteArray>::size_type;
+ for (size_type i = 0, e = fieldsToFind.size(); i < e; ++i) {
+ const auto &val = fieldsToFind[i];
+ const quint32 index = table.indexOf(val);
+ if (!index) {
+ QVERIFY(i < size_type(evicted));
+ } else {
+ QVERIFY(index == table.indexOf(val, val));
+ QByteArray fieldName, fieldValue;
+ QVERIFY(table.field(index, &fieldName, &fieldValue));
+ QVERIFY(val == fieldName);
+ QVERIFY(val == fieldValue);
+ }
+ }
+
+ table.clearDynamicTable();
+
+ QVERIFY(table.numberOfDynamicEntries() == 0);
+ QVERIFY(table.dynamicDataSize() == 0);
+ QVERIFY(table.indexOf(fieldsToFind.back()) == 0);
+
+ QVERIFY(table.prependField("name1", "value1"));
+ QVERIFY(table.prependField("name2", "value2"));
+
+ QVERIFY(table.indexOf("name1") == table.numberOfStaticEntries() + 2);
+ QVERIFY(table.indexOf("name2", "value2") == table.numberOfStaticEntries() + 1);
+ QVERIFY(table.indexOf("name1", "value2") == 0);
+ QVERIFY(table.indexOf("name2", "value1") == 0);
+ QVERIFY(table.indexOf("name3") == 0);
+
+ QVERIFY(!table.indexIsValid(table.numberOfEntries() + 1));
+
+ QVERIFY(table.prependField("name1", "value1"));
+ QVERIFY(table.numberOfDynamicEntries() == 3);
+ table.evictEntry();
+ QVERIFY(table.indexOf("name1") != 0);
+ table.evictEntry();
+ QVERIFY(table.indexOf("name2") == 0);
+ QVERIFY(table.indexOf("name1") != 0);
+ table.evictEntry();
+ QVERIFY(table.dynamicDataSize() == 0);
+ QVERIFY(table.numberOfDynamicEntries() == 0);
+ QVERIFY(table.indexOf("name1") == 0);
+}
+
+void tst_Hpack::hpackEncodeRequest_data()
+{
+ QTest::addColumn<bool>("compression");
+ QTest::newRow("no-string-compression") << false;
+ QTest::newRow("with-string-compression") << true;
+}
+
+void tst_Hpack::hpackEncodeRequest(bool withHuffman)
+{
+ // This function uses examples from HPACK specs
+ // (see appendix).
+
+ Encoder encoder(4096, withHuffman);
+ // HPACK, C.3.1 First Request
+ /*
+ :method: GET
+ :scheme: http
+ :path: /
+ :authority: www.example.com
+
+ Hex dump of encoded data (without Huffman):
+
+ 8286 8441 0f77 7777 2e65 7861 6d70 6c65 | ...A.www.example
+ 2e63 6f6d
+
+ Hex dump of encoded data (with Huffman):
+
+ 8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ff
+ */
+ request1.clear();
+ header1 = {{":method", "GET"},
+ {":scheme", "http"},
+ {":path", "/"},
+ {":authority", "www.example.com"}};
+ QVERIFY(encoder.encodeRequest(request1, header1));
+ QVERIFY(encoder.dynamicTableSize() == 57);
+
+ // HPACK, C.3.2 Second Request
+ /*
+ Header list to encode:
+
+ :method: GET
+ :scheme: http
+ :path: /
+ :authority: www.example.com
+ cache-control: no-cache
+
+ Hex dump of encoded data (without Huffman):
+
+ 8286 84be 5808 6e6f 2d63 6163 6865
+
+ Hex dump of encoded data (with Huffman):
+
+ 8286 84be 5886 a8eb 1064 9cbf
+ */
+
+ request2.clear();
+ header2 = {{":method", "GET"},
+ {":scheme", "http"},
+ {":path", "/"},
+ {":authority", "www.example.com"},
+ {"cache-control", "no-cache"}};
+ encoder.encodeRequest(request2, header2);
+ QVERIFY(encoder.dynamicTableSize() == 110);
+
+ // HPACK, C.3.3 Third Request
+ /*
+ Header list to encode:
+
+ :method: GET
+ :scheme: https
+ :path: /index.html
+ :authority: www.example.com
+ custom-key: custom-value
+
+ Hex dump of encoded data (without Huffman):
+
+ 8287 85bf 400a 6375 7374 6f6d 2d6b 6579
+ 0c63 7573 746f 6d2d 7661 6c75 65
+
+ Hex dump of encoded data (with Huffman):
+
+ 8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925
+ a849 e95b b8e8 b4bf
+ */
+ request3.clear();
+ header3 = {{":method", "GET"},
+ {":scheme", "https"},
+ {":path", "/index.html"},
+ {":authority", "www.example.com"},
+ {"custom-key", "custom-value"}};
+ encoder.encodeRequest(request3, header3);
+ QVERIFY(encoder.dynamicTableSize() == 164);
+}
+
+void tst_Hpack::hpackEncodeRequest()
+{
+ QFETCH(bool, compression);
+
+ hpackEncodeRequest(compression);
+
+ // See comments above about these hex dumps ...
+ const uchar bytes1NH[] = {0x82, 0x86, 0x84, 0x41,
+ 0x0f, 0x77, 0x77, 0x77,
+ 0x2e, 0x65, 0x78, 0x61,
+ 0x6d, 0x70, 0x6c, 0x65,
+ 0x2e, 0x63, 0x6f, 0x6d};
+
+ const uchar bytes1WH[] = {0x82, 0x86, 0x84, 0x41,
+ 0x8c, 0xf1, 0xe3, 0xc2,
+ 0xe5, 0xf2, 0x3a, 0x6b,
+ 0xa0, 0xab, 0x90, 0xf4,
+ 0xff};
+
+ const uchar *hexDump1 = compression ? bytes1WH : bytes1NH;
+ const quint64 byteLength1 = compression ? sizeof bytes1WH : sizeof bytes1NH;
+
+ QCOMPARE(request1.byteLength(), byteLength1);
+ QCOMPARE(request1.bitLength(), byteLength1 * 8);
+
+ for (quint32 i = 0, e = request1.byteLength(); i < e; ++i)
+ QCOMPARE(hexDump1[i], request1.begin()[i]);
+
+ const uchar bytes2NH[] = {0x82, 0x86, 0x84, 0xbe,
+ 0x58, 0x08, 0x6e, 0x6f,
+ 0x2d, 0x63, 0x61, 0x63,
+ 0x68, 0x65};
+
+ const uchar bytes2WH[] = {0x82, 0x86, 0x84, 0xbe,
+ 0x58, 0x86, 0xa8, 0xeb,
+ 0x10, 0x64, 0x9c, 0xbf};
+
+ const uchar *hexDump2 = compression ? bytes2WH : bytes2NH;
+ const auto byteLength2 = compression ? sizeof bytes2WH : sizeof bytes2NH;
+ QVERIFY(request2.byteLength() == byteLength2);
+ QVERIFY(request2.bitLength() == byteLength2 * 8);
+ for (quint32 i = 0, e = request2.byteLength(); i < e; ++i)
+ QCOMPARE(hexDump2[i], request2.begin()[i]);
+
+ const uchar bytes3NH[] = {0x82, 0x87, 0x85, 0xbf,
+ 0x40, 0x0a, 0x63, 0x75,
+ 0x73, 0x74, 0x6f, 0x6d,
+ 0x2d, 0x6b, 0x65, 0x79,
+ 0x0c, 0x63, 0x75, 0x73,
+ 0x74, 0x6f, 0x6d, 0x2d,
+ 0x76, 0x61, 0x6c, 0x75,
+ 0x65};
+ const uchar bytes3WH[] = {0x82, 0x87, 0x85, 0xbf,
+ 0x40, 0x88, 0x25, 0xa8,
+ 0x49, 0xe9, 0x5b, 0xa9,
+ 0x7d, 0x7f, 0x89, 0x25,
+ 0xa8, 0x49, 0xe9, 0x5b,
+ 0xb8, 0xe8, 0xb4, 0xbf};
+
+ const uchar *hexDump3 = compression ? bytes3WH : bytes3NH;
+ const quint64 byteLength3 = compression ? sizeof bytes3WH : sizeof bytes3NH;
+ QCOMPARE(request3.byteLength(), byteLength3);
+ QCOMPARE(request3.bitLength(), byteLength3 * 8);
+ for (quint32 i = 0, e = request3.byteLength(); i < e; ++i)
+ QCOMPARE(hexDump3[i], request3.begin()[i]);
+}
+
+void tst_Hpack::hpackDecodeRequest_data()
+{
+ QTest::addColumn<bool>("compression");
+ QTest::newRow("no-string-compression") << false;
+ QTest::newRow("with-string-compression") << true;
+}
+
+void tst_Hpack::hpackDecodeRequest()
+{
+ QFETCH(bool, compression);
+ hpackEncodeRequest(compression);
+
+ QVERIFY(request1.byteLength());
+ QVERIFY(request2.byteLength());
+ QVERIFY(request3.byteLength());
+
+ Decoder decoder(4096);
+ BitIStream inputStream1(request1.begin(), request1.end());
+ QVERIFY(decoder.decodeHeaderFields(inputStream1));
+ QCOMPARE(decoder.dynamicTableSize(), quint32(57));
+ {
+ const auto &decoded = decoder.decodedHeader();
+ QVERIFY(decoded == header1);
+ }
+
+ BitIStream inputStream2{request2.begin(), request2.end()};
+ QVERIFY(decoder.decodeHeaderFields(inputStream2));
+ QCOMPARE(decoder.dynamicTableSize(), quint32(110));
+ {
+ const auto &decoded = decoder.decodedHeader();
+ QVERIFY(decoded == header2);
+ }
+
+ BitIStream inputStream3(request3.begin(), request3.end());
+ QVERIFY(decoder.decodeHeaderFields(inputStream3));
+ QCOMPARE(decoder.dynamicTableSize(), quint32(164));
+ {
+ const auto &decoded = decoder.decodedHeader();
+ QVERIFY(decoded == header3);
+ }
+}
+
+void tst_Hpack::hpackEncodeResponse_data()
+{
+ hpackEncodeRequest_data();
+}
+
+void tst_Hpack::hpackEncodeResponse()
+{
+ QFETCH(bool, compression);
+
+ hpackEncodeResponse(compression);
+
+ // TODO: we can also test bytes - using hex dumps from HPACK's specs,
+ // for now only test a table behavior/expected sizes.
+}
+
+void tst_Hpack::hpackEncodeResponse(bool withCompression)
+{
+ Encoder encoder(256, withCompression); // 256 - this will result in entries evicted.
+
+ // HPACK, C.5.1 First Response
+ /*
+ Header list to encode:
+
+ :status: 302
+ cache-control: private
+ date: Mon, 21 Oct 2013 20:13:21 GMT
+ location: https://www.example.com
+ */
+ request1.clear();
+ header1 = {{":status", "302"},
+ {"cache-control", "private"},
+ {"date", "Mon, 21 Oct 2013 20:13:21 GMT"},
+ {"location", "https://www.example.com"}};
+
+ QVERIFY(encoder.encodeResponse(request1, header1));
+ QCOMPARE(encoder.dynamicTableSize(), quint32(222));
+
+ // HPACK, C.5.2 Second Response
+ /*
+
+
+ The (":status", "302") header field is evicted from the dynamic
+ table to free space to allow adding the (":status", "307") header field.
+
+ Header list to encode:
+
+ :status: 307
+ cache-control: private
+ date: Mon, 21 Oct 2013 20:13:21 GMT
+ location: https://www.example.com
+ */
+ request2.clear();
+ header2 = {{":status", "307"},
+ {"cache-control", "private"},
+ {"date", "Mon, 21 Oct 2013 20:13:21 GMT"},
+ {"location", "https://www.example.com"}};
+ QVERIFY(encoder.encodeResponse(request2, header2));
+ QCOMPARE(encoder.dynamicTableSize(), quint32(222));
+
+ // HPACK, C.5.3 Third Response
+ /*
+ Several header fields are evicted from the dynamic table
+ during the processing of this header list.
+
+ Header list to encode:
+
+ :status: 200
+ cache-control: private
+ date: Mon, 21 Oct 2013 20:13:22 GMT
+ location: https://www.example.com
+ content-encoding: gzip
+ set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1
+ */
+ request3.clear();
+ header3 = {{":status", "200"},
+ {"cache-control", "private"},
+ {"date", "Mon, 21 Oct 2013 20:13:22 GMT"},
+ {"location", "https://www.example.com"},
+ {"content-encoding", "gzip"},
+ {"set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"}};
+ QVERIFY(encoder.encodeResponse(request3, header3));
+ QCOMPARE(encoder.dynamicTableSize(), quint32(215));
+}
+
+void tst_Hpack::hpackDecodeResponse_data()
+{
+ hpackEncodeRequest_data();
+}
+
+void tst_Hpack::hpackDecodeResponse()
+{
+ QFETCH(bool, compression);
+
+ hpackEncodeResponse(compression);
+
+ QVERIFY(request1.byteLength());
+ Decoder decoder(256); // This size will result in entries evicted.
+ BitIStream inputStream1(request1.begin(), request1.end());
+ QVERIFY(decoder.decodeHeaderFields(inputStream1));
+ QCOMPARE(decoder.dynamicTableSize(), quint32(222));
+
+ {
+ const auto &decoded = decoder.decodedHeader();
+ QVERIFY(decoded == header1);
+ }
+
+ QVERIFY(request2.byteLength());
+ BitIStream inputStream2(request2.begin(), request2.end());
+ QVERIFY(decoder.decodeHeaderFields(inputStream2));
+ QCOMPARE(decoder.dynamicTableSize(), quint32(222));
+
+ {
+ const auto &decoded = decoder.decodedHeader();
+ QVERIFY(decoded == header2);
+ }
+
+ QVERIFY(request3.byteLength());
+ BitIStream inputStream3(request3.begin(), request3.end());
+ QVERIFY(decoder.decodeHeaderFields(inputStream3));
+ QCOMPARE(decoder.dynamicTableSize(), quint32(215));
+
+ {
+ const auto &decoded = decoder.decodedHeader();
+ QVERIFY(decoded == header3);
+ }
+}
+
+QTEST_MAIN(tst_Hpack)
+
+#include "tst_hpack.moc"
diff --git a/tests/auto/network/access/http2/certs/fluke.cert b/tests/auto/network/access/http2/certs/fluke.cert
new file mode 100644
index 0000000000..ace4e4f0eb
--- /dev/null
+++ b/tests/auto/network/access/http2/certs/fluke.cert
@@ -0,0 +1,75 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 0 (0x0)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=NO, ST=Oslo, L=Nydalen, O=Nokia Corporation and/or its subsidiary(-ies), OU=Development, CN=fluke.troll.no/emailAddress=ahanssen@trolltech.com
+ Validity
+ Not Before: Dec 4 01:10:32 2007 GMT
+ Not After : Apr 21 01:10:32 2035 GMT
+ Subject: C=NO, ST=Oslo, O=Nokia Corporation and/or its subsidiary(-ies), OU=Development, CN=fluke.troll.no
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:a7:c8:a0:4a:c4:19:05:1b:66:ba:32:e2:d2:f1:
+ 1c:6f:17:82:e4:39:2e:01:51:90:db:04:34:32:11:
+ 21:c2:0d:6f:59:d8:53:90:54:3f:83:8f:a9:d3:b3:
+ d5:ee:1a:9b:80:ae:c3:25:c9:5e:a5:af:4b:60:05:
+ aa:a0:d1:91:01:1f:ca:04:83:e3:58:1c:99:32:45:
+ 84:70:72:58:03:98:4a:63:8b:41:f5:08:49:d2:91:
+ 02:60:6b:e4:64:fe:dd:a0:aa:74:08:e9:34:4c:91:
+ 5f:12:3d:37:4d:54:2c:ad:7f:5b:98:60:36:02:8c:
+ 3b:f6:45:f3:27:6a:9b:94:9d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ 21:85:04:3D:23:01:66:E5:F7:9F:1A:84:24:8A:AF:0A:79:F4:E5:AC
+ X509v3 Authority Key Identifier:
+ DirName:/C=NO/ST=Oslo/L=Nydalen/O=Nokia Corporation and/or its subsidiary(-ies)/OU=Development/CN=fluke.troll.no/emailAddress=ahanssen@trolltech.com
+ serial:8E:A8:B4:E8:91:B7:54:2E
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 6d:57:5f:d1:05:43:f0:62:05:ec:2a:71:a5:dc:19:08:f2:c4:
+ a6:bd:bb:25:d9:ca:89:01:0e:e4:cf:1f:c1:8c:c8:24:18:35:
+ 53:59:7b:c0:43:b4:32:e6:98:b2:a6:ef:15:05:0b:48:5f:e1:
+ a0:0c:97:a9:a1:77:d8:35:18:30:bc:a9:8f:d3:b7:54:c7:f1:
+ a9:9e:5d:e6:19:bf:f6:3c:5b:2b:d8:e4:3e:62:18:88:8b:d3:
+ 24:e1:40:9b:0c:e6:29:16:62:ab:ea:05:24:70:36:aa:55:93:
+ ef:02:81:1b:23:10:a2:04:eb:56:95:75:fc:f8:94:b1:5d:42:
+ c5:3f:36:44:85:5d:3a:2e:90:46:8a:a2:b9:6f:87:ae:0c:15:
+ 40:19:31:90:fc:3b:25:bb:ae:f1:66:13:0d:85:90:d9:49:34:
+ 8f:f2:5d:f9:7a:db:4d:5d:27:f6:76:9d:35:8c:06:a6:4c:a3:
+ b1:b2:b6:6f:1d:d7:a3:00:fd:72:eb:9e:ea:44:a1:af:21:34:
+ 7d:c7:42:e2:49:91:19:8b:c0:ad:ba:82:80:a8:71:70:f4:35:
+ 31:91:63:84:20:95:e9:60:af:64:8b:cc:ff:3d:8a:76:74:3d:
+ c8:55:6d:e4:8e:c3:2b:1c:e8:42:18:ae:9f:e6:6b:9c:34:06:
+ ec:6a:f2:c3
+-----BEGIN CERTIFICATE-----
+MIIEEzCCAvugAwIBAgIBADANBgkqhkiG9w0BAQUFADCBnDELMAkGA1UEBhMCTk8x
+DTALBgNVBAgTBE9zbG8xEDAOBgNVBAcTB055ZGFsZW4xFjAUBgNVBAoTDVRyb2xs
+dGVjaCBBU0ExFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5mbHVrZS50
+cm9sbC5ubzElMCMGCSqGSIb3DQEJARYWYWhhbnNzZW5AdHJvbGx0ZWNoLmNvbTAe
+Fw0wNzEyMDQwMTEwMzJaFw0zNTA0MjEwMTEwMzJaMGMxCzAJBgNVBAYTAk5PMQ0w
+CwYDVQQIEwRPc2xvMRYwFAYDVQQKEw1Ucm9sbHRlY2ggQVNBMRQwEgYDVQQLEwtE
+ZXZlbG9wbWVudDEXMBUGA1UEAxMOZmx1a2UudHJvbGwubm8wgZ8wDQYJKoZIhvcN
+AQEBBQADgY0AMIGJAoGBAKfIoErEGQUbZroy4tLxHG8XguQ5LgFRkNsENDIRIcIN
+b1nYU5BUP4OPqdOz1e4am4CuwyXJXqWvS2AFqqDRkQEfygSD41gcmTJFhHByWAOY
+SmOLQfUISdKRAmBr5GT+3aCqdAjpNEyRXxI9N01ULK1/W5hgNgKMO/ZF8ydqm5Sd
+AgMBAAGjggEaMIIBFjAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM
+IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUIYUEPSMBZuX3nxqEJIqv
+Cnn05awwgbsGA1UdIwSBszCBsKGBoqSBnzCBnDELMAkGA1UEBhMCTk8xDTALBgNV
+BAgTBE9zbG8xEDAOBgNVBAcTB055ZGFsZW4xFjAUBgNVBAoTDVRyb2xsdGVjaCBB
+U0ExFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5mbHVrZS50cm9sbC5u
+bzElMCMGCSqGSIb3DQEJARYWYWhhbnNzZW5AdHJvbGx0ZWNoLmNvbYIJAI6otOiR
+t1QuMA0GCSqGSIb3DQEBBQUAA4IBAQBtV1/RBUPwYgXsKnGl3BkI8sSmvbsl2cqJ
+AQ7kzx/BjMgkGDVTWXvAQ7Qy5piypu8VBQtIX+GgDJepoXfYNRgwvKmP07dUx/Gp
+nl3mGb/2PFsr2OQ+YhiIi9Mk4UCbDOYpFmKr6gUkcDaqVZPvAoEbIxCiBOtWlXX8
++JSxXULFPzZEhV06LpBGiqK5b4euDBVAGTGQ/Dslu67xZhMNhZDZSTSP8l35ettN
+XSf2dp01jAamTKOxsrZvHdejAP1y657qRKGvITR9x0LiSZEZi8CtuoKAqHFw9DUx
+kWOEIJXpYK9ki8z/PYp2dD3IVW3kjsMrHOhCGK6f5mucNAbsavLD
+-----END CERTIFICATE-----
diff --git a/tests/auto/network/access/http2/certs/fluke.key b/tests/auto/network/access/http2/certs/fluke.key
new file mode 100644
index 0000000000..9d1664d609
--- /dev/null
+++ b/tests/auto/network/access/http2/certs/fluke.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQCnyKBKxBkFG2a6MuLS8RxvF4LkOS4BUZDbBDQyESHCDW9Z2FOQ
+VD+Dj6nTs9XuGpuArsMlyV6lr0tgBaqg0ZEBH8oEg+NYHJkyRYRwclgDmEpji0H1
+CEnSkQJga+Rk/t2gqnQI6TRMkV8SPTdNVCytf1uYYDYCjDv2RfMnapuUnQIDAQAB
+AoGANFzLkanTeSGNFM0uttBipFT9F4a00dqHz6JnO7zXAT26I5r8sU1pqQBb6uLz
+/+Qz5Zwk8RUAQcsMRgJetuPQUb0JZjF6Duv24hNazqXBCu7AZzUenjafwmKC/8ri
+KpX3fTwqzfzi//FKGgbXQ80yykSSliDL3kn/drATxsLCgQECQQDXhEFWLJ0vVZ1s
+1Ekf+3NITE+DR16X+LQ4W6vyEHAjTbaNWtcTKdAWLA2l6N4WAAPYSi6awm+zMxx4
+VomVTsjdAkEAx0z+e7natLeFcrrq8pbU+wa6SAP1VfhQWKitxL1e7u/QO90NCpxE
+oQYKzMkmmpOOFjQwEMAy1dvFMbm4LHlewQJAC/ksDBaUcQHHqjktCtrUb8rVjAyW
+A8lscckeB2fEYyG5J6dJVaY4ClNOOs5yMDS2Afk1F6H/xKvtQ/5CzInA/QJATDub
+K+BPU8jO9q+gpuIi3VIZdupssVGmCgObVCHLakG4uO04y9IyPhV9lA9tALtoIf4c
+VIvv5fWGXBrZ48kZAQJBAJmVCdzQxd9LZI5vxijUCj5EI4e+x5DRqVUvyP8KCZrC
+AiNyoDP85T+hBZaSXK3aYGpVwelyj3bvo1GrTNwNWLw=
+-----END RSA PRIVATE KEY-----
diff --git a/tests/auto/network/access/http2/http2.pro b/tests/auto/network/access/http2/http2.pro
new file mode 100644
index 0000000000..5dd8bdf9ae
--- /dev/null
+++ b/tests/auto/network/access/http2/http2.pro
@@ -0,0 +1,9 @@
+QT += core core-private network network-private testlib
+
+CONFIG += testcase parallel_test c++11
+TEMPLATE = app
+TARGET = tst_http2
+HEADERS += http2srv.h
+SOURCES += tst_http2.cpp http2srv.cpp
+
+DEFINES += SRCDIR=\\\"$$PWD/\\\"
diff --git a/tests/auto/network/access/http2/http2srv.cpp b/tests/auto/network/access/http2/http2srv.cpp
new file mode 100644
index 0000000000..eb09569cdd
--- /dev/null
+++ b/tests/auto/network/access/http2/http2srv.cpp
@@ -0,0 +1,611 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtNetwork/private/http2protocol_p.h>
+#include <QtNetwork/private/bitstreams_p.h>
+
+#include "http2srv.h"
+
+#include <QtNetwork/qsslconfiguration.h>
+#include <QtNetwork/qhostaddress.h>
+#include <QtNetwork/qsslkey.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qfile.h>
+
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Http2;
+using namespace HPack;
+
+namespace
+{
+
+inline bool is_valid_client_stream(quint32 streamID)
+{
+ // A valid client stream ID is an odd integer number in the range [1, INT_MAX].
+ return (streamID & 0x1) && streamID <= std::numeric_limits<qint32>::max();
+}
+
+}
+
+Http2Server::Http2Server(const Http2Settings &ss, const Http2Settings &cs)
+ : serverSettings(ss)
+{
+ for (const auto &s : cs)
+ expectedClientSettings[quint16(s.identifier)] = s.value;
+
+ responseBody = "<html>\n"
+ "<head>\n"
+ "<title>Sample \"Hello, World\" Application</title>\n"
+ "</head>\n"
+ "<body bgcolor=white>\n"
+ "<table border=\"0\" cellpadding=\"10\">\n"
+ "<tr>\n"
+ "<td>\n"
+ "<img src=\"images/springsource.png\">\n"
+ "</td>\n"
+ "<td>\n"
+ "<h1>Sample \"Hello, World\" Application</h1>\n"
+ "</td>\n"
+ "</tr>\n"
+ "</table>\n"
+ "<p>This is the home page for the HelloWorld Web application. </p>\n"
+ "</body>\n"
+ "</html>";
+}
+
+Http2Server::~Http2Server()
+{
+}
+
+void Http2Server::setResponseBody(const QByteArray &body)
+{
+ responseBody = body;
+}
+
+void Http2Server::startServer()
+{
+ if (listen())
+ emit serverStarted(serverPort());
+}
+
+
+void Http2Server::sendServerSettings()
+{
+ Q_ASSERT(socket);
+
+ if (!serverSettings.size())
+ return;
+
+ outboundFrame.start(FrameType::SETTINGS, FrameFlag::EMPTY, connectionStreamID);
+ for (const auto &s : serverSettings) {
+ outboundFrame.append(s.identifier);
+ outboundFrame.append(s.value);
+ if (s.identifier == Settings::INITIAL_WINDOW_SIZE_ID)
+ streamRecvWindowSize = s.value;
+ }
+ outboundFrame.write(*socket);
+ // Now, let's update our peer on a session recv window size:
+ const quint32 updatedSize = 10 * streamRecvWindowSize;
+ if (sessionRecvWindowSize < updatedSize) {
+ const quint32 delta = updatedSize - sessionRecvWindowSize;
+ sessionRecvWindowSize = updatedSize;
+ sessionCurrRecvWindow = updatedSize;
+ sendWINDOW_UPDATE(connectionStreamID, delta);
+ }
+
+ waitingClientAck = true;
+ settingsSent = true;
+}
+
+void Http2Server::sendGOAWAY(quint32 streamID, quint32 error, quint32 lastStreamID)
+{
+ Q_ASSERT(socket);
+
+ outboundFrame.start(FrameType::GOAWAY, FrameFlag::EMPTY, streamID);
+ outboundFrame.append(lastStreamID);
+ outboundFrame.append(error);
+ outboundFrame.write(*socket);
+}
+
+void Http2Server::sendRST_STREAM(quint32 streamID, quint32 error)
+{
+ Q_ASSERT(socket);
+
+ outboundFrame.start(FrameType::RST_STREAM, FrameFlag::EMPTY, streamID);
+ outboundFrame.append(error);
+ outboundFrame.write(*socket);
+}
+
+void Http2Server::sendDATA(quint32 streamID, quint32 windowSize)
+{
+ Q_ASSERT(socket);
+
+ const auto it = suspendedStreams.find(streamID);
+ Q_ASSERT(it != suspendedStreams.end());
+
+ const quint32 offset = it->second;
+ Q_ASSERT(offset < quint32(responseBody.size()));
+
+ const quint32 bytes = std::min<quint32>(windowSize, responseBody.size() - offset);
+ outboundFrame.start(FrameType::DATA, FrameFlag::EMPTY, streamID);
+ const bool last = offset + bytes == quint32(responseBody.size());
+
+ const quint32 frameSizeLimit(clientSetting(Settings::MAX_FRAME_SIZE_ID, Http2::maxFrameSize));
+ outboundFrame.writeDATA(*socket, frameSizeLimit,
+ reinterpret_cast<const uchar *>(responseBody.constData() + offset),
+ bytes);
+
+ if (last) {
+ outboundFrame.start(FrameType::DATA, FrameFlag::END_STREAM, streamID);
+ outboundFrame.setPayloadSize(0);
+ outboundFrame.write(*socket);
+
+ suspendedStreams.erase(it);
+ activeRequests.erase(streamID);
+
+ Q_ASSERT(closedStreams.find(streamID) == closedStreams.end());
+ closedStreams.insert(streamID);
+ } else {
+ it->second += bytes;
+ }
+}
+
+void Http2Server::sendWINDOW_UPDATE(quint32 streamID, quint32 delta)
+{
+ Q_ASSERT(socket);
+
+ outboundFrame.start(FrameType::WINDOW_UPDATE, FrameFlag::EMPTY, streamID);
+ outboundFrame.append(delta);
+ outboundFrame.write(*socket);
+}
+
+void Http2Server::incomingConnection(qintptr socketDescriptor)
+{
+ socket.reset(new QSslSocket);
+ // Add HTTP2 as supported protocol:
+ auto conf = QSslConfiguration::defaultConfiguration();
+ auto protos = conf.allowedNextProtocols();
+ protos.prepend(QSslConfiguration::ALPNProtocolHTTP2);
+ conf.setAllowedNextProtocols(protos);
+ socket->setSslConfiguration(conf);
+ // SSL-related setup ...
+ socket->setPeerVerifyMode(QSslSocket::VerifyNone);
+ socket->setProtocol(QSsl::TlsV1_2OrLater);
+ connect(socket.data(), SIGNAL(sslErrors(QList<QSslError>)),
+ this, SLOT(ignoreErrorSlot()));
+ QFile file(SRCDIR "certs/fluke.key");
+ file.open(QIODevice::ReadOnly);
+ QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
+ socket->setPrivateKey(key);
+ auto localCert = QSslCertificate::fromPath(SRCDIR "certs/fluke.cert");
+ socket->setLocalCertificateChain(localCert);
+ socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState);
+ // Stop listening.
+ close();
+ // Start SSL handshake and ALPN:
+ connect(socket.data(), SIGNAL(encrypted()),
+ this, SLOT(connectionEncrypted()));
+ socket->startServerEncryption();
+}
+
+quint32 Http2Server::clientSetting(Http2::Settings identifier, quint32 defaultValue)
+{
+ const auto it = expectedClientSettings.find(quint16(identifier));
+ if (it != expectedClientSettings.end())
+ return it->second;
+ return defaultValue;
+}
+
+void Http2Server::connectionEncrypted()
+{
+ using namespace Http2;
+
+ connect(socket.data(), SIGNAL(readyRead()),
+ this, SLOT(readReady()));
+
+ waitingClientPreface = true;
+ waitingClientAck = false;
+ waitingClientSettings = false;
+ settingsSent = false;
+ // We immediately send our settings so that our client
+ // can use flow control correctly.
+ sendServerSettings();
+
+ if (socket->bytesAvailable())
+ readReady();
+}
+
+void Http2Server::ignoreErrorSlot()
+{
+ socket->ignoreSslErrors();
+}
+
+// Now HTTP2 "server" part:
+/*
+This code is overly simplified but it tests the basic HTTP2 expected behavior:
+1. CONNECTION PREFACE
+2. SETTINGS
+3. sends our own settings (to modify the flow control)
+4. collects and reports requests
+5. if asked - sends responds to those requests
+6. does some very basic error handling
+7. tests frames validity/stream logic at the very basic level.
+*/
+
+void Http2Server::readReady()
+{
+ if (connectionError)
+ return;
+
+ if (waitingClientPreface) {
+ handleConnectionPreface();
+ } else {
+ const auto status = inboundFrame.read(*socket);
+ switch (status) {
+ case FrameStatus::incompleteFrame:
+ break;
+ case FrameStatus::goodFrame:
+ handleIncomingFrame();
+ break;
+ default:
+ connectionError = true;
+ sendGOAWAY(connectionStreamID, PROTOCOL_ERROR, connectionStreamID);
+ }
+ }
+
+ if (socket->bytesAvailable())
+ QMetaObject::invokeMethod(this, "readReady", Qt::QueuedConnection);
+}
+
+void Http2Server::handleConnectionPreface()
+{
+ Q_ASSERT(waitingClientPreface);
+
+ if (socket->bytesAvailable() < clientPrefaceLength)
+ return; // Wait for more data ...
+
+ char buf[clientPrefaceLength] = {};
+ socket->read(buf, clientPrefaceLength);
+ if (std::memcmp(buf, Http2clientPreface, clientPrefaceLength)) {
+ sendGOAWAY(connectionStreamID, PROTOCOL_ERROR, connectionStreamID);
+ emit clientPrefaceError();
+ connectionError = true;
+ return;
+ }
+
+ waitingClientPreface = false;
+ waitingClientSettings = true;
+}
+
+void Http2Server::handleIncomingFrame()
+{
+ // Frames that our implementation can send include:
+ // 1. SETTINGS (happens only during connection preface,
+ // handled already by this point)
+ // 2. SETTIGNS with ACK should be sent only as a response
+ // to a server's SETTINGS
+ // 3. HEADERS
+ // 4. CONTINUATION
+ // 5. DATA
+ // 6. PING
+ // 7. RST_STREAM
+ // 8. GOAWAY
+
+ if (continuedRequest.size()) {
+ if (inboundFrame.type != FrameType::CONTINUATION ||
+ inboundFrame.streamID != continuedRequest.front().streamID) {
+ sendGOAWAY(connectionStreamID, PROTOCOL_ERROR, connectionStreamID);
+ emit invalidFrame();
+ connectionError = true;
+ return;
+ }
+ }
+
+ switch (inboundFrame.type) {
+ case FrameType::SETTINGS:
+ handleSETTINGS();
+ break;
+ case FrameType::HEADERS:
+ case FrameType::CONTINUATION:
+ continuedRequest.push_back(std::move(inboundFrame));
+ processRequest();
+ break;
+ case FrameType::DATA:
+ handleDATA();
+ break;
+ case FrameType::RST_STREAM:
+ // TODO: this is not tested for now.
+ break;
+ case FrameType::PING:
+ // TODO: this is not tested for now.
+ break;
+ case FrameType::GOAWAY:
+ // TODO: this is not tested for now.
+ break;
+ case FrameType::WINDOW_UPDATE:
+ handleWINDOW_UPDATE();
+ break;
+ default:;
+ }
+}
+
+void Http2Server::handleSETTINGS()
+{
+ // SETTINGS is either a part of the connection preface,
+ // or a SETTINGS ACK.
+ Q_ASSERT(inboundFrame.type == FrameType::SETTINGS);
+
+ if (inboundFrame.flags.testFlag(FrameFlag::ACK)) {
+ if (!waitingClientAck || inboundFrame.dataSize()) {
+ emit invalidFrame();
+ connectionError = true;
+ waitingClientAck = false;
+ return;
+ }
+
+ waitingClientAck = false;
+ emit serverSettingsAcked();
+ return;
+ }
+
+ // QHttp2ProtocolHandler always sends some settings,
+ // and the size is a multiple of 6.
+ if (!inboundFrame.dataSize() || inboundFrame.dataSize() % 6) {
+ sendGOAWAY(connectionStreamID, FRAME_SIZE_ERROR, connectionStreamID);
+ emit clientPrefaceError();
+ connectionError = true;
+ return;
+ }
+
+ const uchar *src = inboundFrame.dataBegin();
+ const uchar *end = src + inboundFrame.dataSize();
+
+ const auto notFound = expectedClientSettings.end();
+
+ while (src != end) {
+ const auto id = qFromBigEndian<quint16>(src);
+ const auto value = qFromBigEndian<quint32>(src + 2);
+ if (expectedClientSettings.find(id) == notFound ||
+ expectedClientSettings[id] != value) {
+ emit clientPrefaceError();
+ connectionError = true;
+ return;
+ }
+
+ src += 6;
+ }
+
+ // Send SETTINGS ACK:
+ outboundFrame.start(FrameType::SETTINGS, FrameFlag::ACK, connectionStreamID);
+ outboundFrame.write(*socket);
+ waitingClientSettings = false;
+ emit clientPrefaceOK();
+}
+
+void Http2Server::handleDATA()
+{
+ Q_ASSERT(inboundFrame.type == FrameType::DATA);
+
+ const auto streamID = inboundFrame.streamID;
+
+ if (!is_valid_client_stream(streamID) ||
+ closedStreams.find(streamID) != closedStreams.end()) {
+ emit invalidFrame();
+ connectionError = true;
+ sendGOAWAY(connectionStreamID, PROTOCOL_ERROR, connectionStreamID);
+ return;
+ }
+
+ if (sessionCurrRecvWindow < inboundFrame.payloadSize) {
+ // Client does not respect our session window size!
+ emit invalidRequest(streamID);
+ connectionError = true;
+ sendGOAWAY(connectionStreamID, FLOW_CONTROL_ERROR, connectionStreamID);
+ return;
+ }
+
+ auto it = streamWindows.find(streamID);
+ if (it == streamWindows.end())
+ it = streamWindows.insert(std::make_pair(streamID, streamRecvWindowSize)).first;
+
+ if (it->second < inboundFrame.payloadSize) {
+ emit invalidRequest(streamID);
+ connectionError = true;
+ sendGOAWAY(connectionStreamID, FLOW_CONTROL_ERROR, connectionStreamID);
+ return;
+ }
+
+ it->second -= inboundFrame.payloadSize;
+ if (it->second < streamRecvWindowSize / 2) {
+ sendWINDOW_UPDATE(streamID, streamRecvWindowSize / 2);
+ it->second += streamRecvWindowSize / 2;
+ }
+
+ sessionCurrRecvWindow -= inboundFrame.payloadSize;
+
+ if (sessionCurrRecvWindow < sessionRecvWindowSize / 2) {
+ // This is some quite naive and trivial logic on when to update.
+
+ sendWINDOW_UPDATE(connectionStreamID, sessionRecvWindowSize / 2);
+ sessionCurrRecvWindow += sessionRecvWindowSize / 2;
+ }
+
+ if (inboundFrame.flags.testFlag(FrameFlag::END_STREAM)) {
+ closedStreams.insert(streamID); // Enter "half-closed remote" state.
+ streamWindows.erase(it);
+ emit receivedData(streamID);
+ }
+}
+
+void Http2Server::handleWINDOW_UPDATE()
+{
+ const auto streamID = inboundFrame.streamID;
+ if (!streamID) // We ignore this for now to keep things simple.
+ return;
+
+ if (streamID && suspendedStreams.find(streamID) == suspendedStreams.end()) {
+ if (closedStreams.find(streamID) == closedStreams.end()) {
+ sendRST_STREAM(streamID, PROTOCOL_ERROR);
+ emit invalidFrame();
+ connectionError = true;
+ }
+
+ return;
+ }
+
+ const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
+ if (!delta || delta > quint32(std::numeric_limits<qint32>::max())) {
+ sendRST_STREAM(streamID, PROTOCOL_ERROR);
+ emit invalidFrame();
+ connectionError = true;
+ return;
+ }
+
+ emit windowUpdate(streamID);
+ sendDATA(streamID, delta);
+}
+
+void Http2Server::sendResponse(quint32 streamID, bool emptyBody)
+{
+ Q_ASSERT(activeRequests.find(streamID) != activeRequests.end());
+
+ outboundFrame.start(FrameType::HEADERS, FrameFlag::END_HEADERS, streamID);
+ if (emptyBody)
+ outboundFrame.addFlag(FrameFlag::END_STREAM);
+
+ HttpHeader header = {{":status", "200"}};
+ if (!emptyBody) {
+ header.push_back(HPack::HeaderField("content-length",
+ QString("%1").arg(responseBody.size()).toLatin1()));
+ }
+
+ HPack::BitOStream ostream(outboundFrame.rawFrameBuffer());
+ const bool result = encoder.encodeResponse(ostream, header);
+ Q_ASSERT(result);
+ Q_UNUSED(result)
+
+ const quint32 maxFrameSize(clientSetting(Settings::MAX_FRAME_SIZE_ID, Http2::maxFrameSize));
+ outboundFrame.writeHEADERS(*socket, maxFrameSize);
+
+ if (!emptyBody) {
+ Q_ASSERT(suspendedStreams.find(streamID) == suspendedStreams.end());
+
+ const quint32 windowSize = clientSetting(Settings::INITIAL_WINDOW_SIZE_ID,
+ Http2::defaultSessionWindowSize);
+ // Suspend to immediately resume it.
+ suspendedStreams[streamID] = 0; // start sending from offset 0
+ sendDATA(streamID, windowSize);
+ } else {
+ activeRequests.erase(streamID);
+ closedStreams.insert(streamID);
+ }
+}
+
+void Http2Server::processRequest()
+{
+ Q_ASSERT(continuedRequest.size());
+
+ if (!continuedRequest.back().flags.testFlag(FrameFlag::END_HEADERS))
+ return;
+
+ // We test here:
+ // 1. stream is 'idle'.
+ // 2. has priority set and dependency (it's 0x0 at the moment).
+ // 3. header can be decompressed.
+ const auto &headersFrame = continuedRequest.front();
+ const auto streamID = headersFrame.streamID;
+ if (!is_valid_client_stream(streamID)) {
+ emit invalidRequest(streamID);
+ connectionError = true;
+ sendGOAWAY(connectionStreamID, PROTOCOL_ERROR, connectionStreamID);
+ return;
+ }
+
+ if (closedStreams.find(streamID) != closedStreams.end()) {
+ emit invalidFrame();
+ connectionError = true;
+ sendGOAWAY(connectionStreamID, PROTOCOL_ERROR, connectionStreamID);
+ return;
+ }
+
+ quint32 dep = 0;
+ uchar w = 0;
+ if (!headersFrame.priority(&dep, &w)) {
+ emit invalidFrame();
+ sendRST_STREAM(streamID, PROTOCOL_ERROR);
+ return;
+ }
+
+ // Assemble headers ...
+ quint32 totalSize = 0;
+ for (const auto &frame : continuedRequest) {
+ if (std::numeric_limits<quint32>::max() - frame.dataSize() < totalSize) {
+ // Resulted in overflow ...
+ emit invalidFrame();
+ connectionError = true;
+ sendGOAWAY(connectionStreamID, PROTOCOL_ERROR, connectionStreamID);
+ return;
+ }
+ totalSize += frame.dataSize();
+ }
+
+ std::vector<uchar> hpackBlock(totalSize);
+ auto dst = hpackBlock.begin();
+ for (const auto &frame : continuedRequest) {
+ if (!frame.dataSize())
+ continue;
+ std::copy(frame.dataBegin(), frame.dataBegin() + frame.dataSize(), dst);
+ dst += frame.dataSize();
+ }
+
+ HPack::BitIStream inputStream{&hpackBlock[0], &hpackBlock[0] + hpackBlock.size()};
+
+ if (!decoder.decodeHeaderFields(inputStream)) {
+ emit decompressionFailed(streamID);
+ sendRST_STREAM(streamID, COMPRESSION_ERROR);
+ closedStreams.insert(streamID);
+ return;
+ }
+
+ continuedRequest.clear();
+ // Actually, if needed, we can do a comparison here.
+ activeRequests[streamID] = decoder.decodedHeader();
+ if (headersFrame.flags.testFlag(FrameFlag::END_STREAM))
+ emit receivedRequest(streamID);
+ // else - we're waiting for incoming DATA frames ...
+}
+
+QT_END_NAMESPACE
diff --git a/tests/auto/network/access/http2/http2srv.h b/tests/auto/network/access/http2/http2srv.h
new file mode 100644
index 0000000000..00cfde944b
--- /dev/null
+++ b/tests/auto/network/access/http2/http2srv.h
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef HTTP2SRV_H
+#define HTTP2SRV_H
+
+#include <QtNetwork/private/http2protocol_p.h>
+#include <QtNetwork/private/http2frames_p.h>
+#include <QtNetwork/private/hpack_p.h>
+
+#include <QtCore/qscopedpointer.h>
+#include <QtNetwork/qtcpserver.h>
+#include <QtNetwork/qsslsocket.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qglobal.h>
+
+#include <vector>
+#include <map>
+#include <set>
+
+QT_BEGIN_NAMESPACE
+
+struct Http2Setting
+{
+ Http2::Settings identifier;
+ quint32 value = 0;
+
+ Http2Setting(Http2::Settings ident, quint32 v)
+ : identifier(ident),
+ value(v)
+ {}
+};
+
+using Http2Settings = std::vector<Http2Setting>;
+
+class Http2Server : public QTcpServer
+{
+ Q_OBJECT
+public:
+ Http2Server(const Http2Settings &serverSettings,
+ const Http2Settings &clientSettings);
+
+ ~Http2Server();
+
+ // To be called before server started:
+ void setResponseBody(const QByteArray &body);
+
+ // Invokables, since we can call them from the main thread,
+ // but server (can) work on its own thread.
+ Q_INVOKABLE void startServer();
+ Q_INVOKABLE void sendServerSettings();
+ Q_INVOKABLE void sendGOAWAY(quint32 streamID, quint32 error,
+ quint32 lastStreamID);
+ Q_INVOKABLE void sendRST_STREAM(quint32 streamID, quint32 error);
+ Q_INVOKABLE void sendDATA(quint32 streamID, quint32 windowSize);
+ Q_INVOKABLE void sendWINDOW_UPDATE(quint32 streamID, quint32 delta);
+
+ Q_INVOKABLE void handleConnectionPreface();
+ Q_INVOKABLE void handleIncomingFrame();
+ Q_INVOKABLE void handleSETTINGS();
+ Q_INVOKABLE void handleDATA();
+ Q_INVOKABLE void handleWINDOW_UPDATE();
+
+ Q_INVOKABLE void sendResponse(quint32 streamID, bool emptyBody);
+
+private:
+ void processRequest();
+
+Q_SIGNALS:
+ void serverStarted(quint16 port);
+ // Error/success notifications:
+ void clientPrefaceOK();
+ void clientPrefaceError();
+ void serverSettingsAcked();
+ void invalidFrame();
+ void invalidRequest(quint32 streamID);
+ void decompressionFailed(quint32 streamID);
+ void receivedRequest(quint32 streamID);
+ void receivedData(quint32 streamID);
+ void windowUpdate(quint32 streamID);
+
+private slots:
+ void connectionEncrypted();
+ void readReady();
+
+private:
+ void incomingConnection(qintptr socketDescriptor) Q_DECL_OVERRIDE;
+
+ quint32 clientSetting(Http2::Settings identifier, quint32 defaultValue);
+
+ QScopedPointer<QSslSocket> socket;
+
+ // Connection preface:
+ bool waitingClientPreface = false;
+ bool waitingClientSettings = false;
+ bool settingsSent = false;
+ bool waitingClientAck = false;
+
+ Http2Settings serverSettings;
+ std::map<quint16, quint32> expectedClientSettings;
+
+ bool connectionError = false;
+
+ Http2::FrameReader inboundFrame;
+ Http2::FrameWriter outboundFrame;
+
+ using FrameSequence = std::vector<Http2::FrameReader>;
+ FrameSequence continuedRequest;
+
+ std::map<quint32, quint32> streamWindows;
+
+ HPack::Decoder decoder{HPack::FieldLookupTable::DefaultSize};
+ HPack::Encoder encoder{HPack::FieldLookupTable::DefaultSize, true};
+
+ using Http2Requests = std::map<quint32, HPack::HttpHeader>;
+ Http2Requests activeRequests;
+ // 'remote half-closed' streams to keep
+ // track of streams with END_STREAM set:
+ std::set<quint32> closedStreams;
+ // streamID + offset in response body to send.
+ std::map<quint32, quint32> suspendedStreams;
+
+ // We potentially reset this once (see sendServerSettings)
+ // and do not change later:
+ quint32 sessionRecvWindowSize = Http2::defaultSessionWindowSize;
+ // This changes in the range [0, sessionRecvWindowSize]
+ // while handling DATA frames:
+ quint32 sessionCurrRecvWindow = sessionRecvWindowSize;
+ // This we potentially update only once (sendServerSettings).
+ quint32 streamRecvWindowSize = Http2::defaultSessionWindowSize;
+
+ QByteArray responseBody;
+
+protected slots:
+ void ignoreErrorSlot();
+};
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/tests/auto/network/access/http2/tst_http2.cpp b/tests/auto/network/access/http2/tst_http2.cpp
new file mode 100644
index 0000000000..dbb89db0f9
--- /dev/null
+++ b/tests/auto/network/access/http2/tst_http2.cpp
@@ -0,0 +1,449 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include "http2srv.h"
+
+#include <QtNetwork/qnetworkaccessmanager.h>
+#include <QtNetwork/qnetworkrequest.h>
+#include <QtNetwork/qnetworkreply.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qthread.h>
+#include <QtCore/qurl.h>
+
+#ifndef QT_NO_SSL
+#ifndef QT_NO_OPENSSL
+#include <QtNetwork/private/qsslsocket_openssl_symbols_p.h>
+#endif // NO_OPENSSL
+#endif // NO_SSL
+
+
+#include <cstdlib>
+
+// At the moment our HTTP/2 imlpementation requires ALPN and this means OpenSSL.
+#if !defined(QT_NO_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(OPENSSL_NO_TLSEXT)
+#define QT_ALPN
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class tst_Http2 : public QObject
+{
+ Q_OBJECT
+public:
+ tst_Http2();
+ ~tst_Http2();
+private slots:
+ // Tests:
+ void singleRequest();
+ void multipleRequests();
+ void flowControlClientSide();
+ void flowControlServerSide();
+
+protected slots:
+ // Slots to listen to our in-process server:
+ void serverStarted(quint16 port);
+ void clientPrefaceOK();
+ void clientPrefaceError();
+ void serverSettingsAcked();
+ void invalidFrame();
+ void invalidRequest(quint32 streamID);
+ void decompressionFailed(quint32 streamID);
+ void receivedRequest(quint32 streamID);
+ void receivedData(quint32 streamID);
+ void windowUpdated(quint32 streamID);
+ void replyFinished();
+
+private:
+ void clearHTTP2State();
+ // Run event for 'ms' milliseconds.
+ // The default value '5000' is enough for
+ // small payload.
+ void runEventLoop(int ms = 5000);
+ void stopEventLoop();
+ // TODO: different parameters like client/server settings ...
+ Http2Server *newServer(const Http2Settings &serverSettings);
+ // Send a get or post request, depending on a payload (empty or not).
+ void sendRequest(int streamNumber,
+ QNetworkRequest::Priority priority = QNetworkRequest::NormalPriority,
+ const QByteArray &payload = QByteArray());
+
+ quint16 serverPort = 0;
+ QThread *workerThread = nullptr;
+ QNetworkAccessManager manager;
+
+ QEventLoop eventLoop;
+ QTimer timer;
+
+ int nRequests = 0;
+
+ int windowUpdates = 0;
+ bool prefaceOK = false;
+ bool serverGotSettingsACK = false;
+
+ static const Http2Settings defaultServerSettings;
+};
+
+const Http2Settings tst_Http2::defaultServerSettings{{Http2::Settings::MAX_CONCURRENT_STREAMS_ID, 100}};
+
+tst_Http2::tst_Http2()
+ : workerThread(new QThread)
+{
+ workerThread->start();
+
+ timer.setInterval(10000);
+ timer.setSingleShot(true);
+
+ connect(&timer, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
+}
+
+tst_Http2::~tst_Http2()
+{
+ workerThread->quit();
+ workerThread->wait(5000);
+
+ if (workerThread->isFinished()) {
+ delete workerThread;
+ } else {
+ connect(workerThread, &QThread::finished,
+ workerThread, &QThread::deleteLater);
+ }
+}
+
+void tst_Http2::singleRequest()
+{
+#ifndef QT_ALPN
+ QSKIP("This test requires ALPN support");
+#endif
+ clearHTTP2State();
+
+ serverPort = 0;
+ nRequests = 1;
+
+ auto srv = newServer(defaultServerSettings);
+
+ QMetaObject::invokeMethod(srv, "startServer", Qt::QueuedConnection);
+ runEventLoop();
+
+ QVERIFY(serverPort != 0);
+
+ const QUrl url(QString("https://127.0.0.1:%1/index.html").arg(serverPort));
+
+ QNetworkRequest request(url);
+ request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true));
+
+ auto reply = manager.get(request);
+ connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
+ // Since we're using self-signed certificates,
+ // ignore SSL errors:
+ reply->ignoreSslErrors();
+
+ runEventLoop();
+
+ QVERIFY(nRequests == 0);
+ QVERIFY(prefaceOK);
+ QVERIFY(serverGotSettingsACK);
+
+ QCOMPARE(reply->error(), QNetworkReply::NoError);
+ QVERIFY(reply->isFinished());
+
+ QMetaObject::invokeMethod(srv, "deleteLater", Qt::QueuedConnection);
+}
+
+void tst_Http2::multipleRequests()
+{
+#ifndef QT_ALPN
+ QSKIP("This test requires ALPN support");
+#endif
+ clearHTTP2State();
+
+ serverPort = 0;
+ nRequests = 10;
+
+ auto srv = newServer(defaultServerSettings);
+
+ QMetaObject::invokeMethod(srv, "startServer", Qt::QueuedConnection);
+
+ runEventLoop();
+ QVERIFY(serverPort != 0);
+
+ // Just to make the order a bit more interesting
+ // we'll index this randomly:
+ QNetworkRequest::Priority priorities[] = {QNetworkRequest::HighPriority,
+ QNetworkRequest::NormalPriority,
+ QNetworkRequest::LowPriority};
+
+
+
+ for (int i = 0; i < nRequests; ++i)
+ sendRequest(i, priorities[std::rand() % 3]);
+
+ runEventLoop();
+
+ QVERIFY(nRequests == 0);
+ QVERIFY(prefaceOK);
+ QVERIFY(serverGotSettingsACK);
+
+ QMetaObject::invokeMethod(srv, "deleteLater", Qt::QueuedConnection);
+}
+
+void tst_Http2::flowControlClientSide()
+{
+#ifndef QT_ALPN
+ QSKIP("This test requires ALPN support");
+#endif
+ // Create a server but impose limits:
+ // 1. Small MAX frame size, so we test CONTINUATION frames.
+ // 2. Small client windows so server responses cause client streams
+ // to suspend and server sends WINDOW_UPDATE frames.
+ // 3. Few concurrent streams, to test protocol handler can resume
+ // suspended requests.
+
+ using namespace Http2;
+
+ clearHTTP2State();
+
+ serverPort = 0;
+ nRequests = 10;
+ windowUpdates = 0;
+
+ const Http2Settings serverSettings = {{Settings::MAX_CONCURRENT_STREAMS_ID, 3}};
+
+ auto srv = newServer(serverSettings);
+
+ const QByteArray respond(int(Http2::defaultSessionWindowSize * 100), 'x');
+ srv->setResponseBody(respond);
+
+ QMetaObject::invokeMethod(srv, "startServer", Qt::QueuedConnection);
+
+ runEventLoop();
+ QVERIFY(serverPort != 0);
+
+ for (int i = 0; i < nRequests; ++i)
+ sendRequest(i);
+
+ runEventLoop(10000);
+
+ QVERIFY(nRequests == 0);
+ QVERIFY(prefaceOK);
+ QVERIFY(serverGotSettingsACK);
+ QVERIFY(windowUpdates > 0);
+
+ QMetaObject::invokeMethod(srv, "deleteLater", Qt::QueuedConnection);
+}
+
+void tst_Http2::flowControlServerSide()
+{
+#ifndef QT_ALPN
+ QSKIP("This test requires ALPN support");
+#endif
+ // Quite aggressive test:
+ // low MAX_FRAME_SIZE forces a lot of small DATA frames,
+ // payload size exceedes stream/session RECV window sizes
+ // so that our implementation should deal with WINDOW_UPDATE
+ // on a session/stream level correctly + resume/suspend streams
+ // to let all replies finish without any error.
+ using namespace Http2;
+
+ clearHTTP2State();
+
+ serverPort = 0;
+ nRequests = 30;
+
+ const Http2Settings serverSettings = {{Settings::MAX_CONCURRENT_STREAMS_ID, 7}};
+
+ auto srv = newServer(serverSettings);
+
+ const QByteArray payload(int(Http2::defaultSessionWindowSize * 1000), 'x');
+
+ QMetaObject::invokeMethod(srv, "startServer", Qt::QueuedConnection);
+
+ runEventLoop();
+ QVERIFY(serverPort != 0);
+
+ for (int i = 0; i < nRequests; ++i)
+ sendRequest(i, QNetworkRequest::NormalPriority, payload);
+
+ runEventLoop(120000);
+
+ QVERIFY(nRequests == 0);
+ QVERIFY(prefaceOK);
+ QVERIFY(serverGotSettingsACK);
+
+ QMetaObject::invokeMethod(srv, "deleteLater", Qt::QueuedConnection);
+ srv = nullptr;
+}
+
+void tst_Http2::serverStarted(quint16 port)
+{
+ serverPort = port;
+ stopEventLoop();
+}
+
+void tst_Http2::clearHTTP2State()
+{
+ windowUpdates = 0;
+ prefaceOK = false;
+ serverGotSettingsACK = false;
+}
+
+void tst_Http2::runEventLoop(int ms)
+{
+ timer.setInterval(ms);
+ timer.start();
+ eventLoop.exec();
+}
+
+void tst_Http2::stopEventLoop()
+{
+ timer.stop();
+ eventLoop.quit();
+}
+
+Http2Server *tst_Http2::newServer(const Http2Settings &serverSettings)
+{
+ using namespace Http2;
+ // Client's settings are fixed by qhttp2protocolhandler.
+ const Http2Settings clientSettings = {{Settings::MAX_FRAME_SIZE_ID, quint32(Http2::maxFrameSize)},
+ {Settings::ENABLE_PUSH_ID, quint32(0)}};
+ auto srv = new Http2Server(serverSettings, clientSettings);
+
+ using Srv = Http2Server;
+ using Cl = tst_Http2;
+
+ connect(srv, &Srv::serverStarted, this, &Cl::serverStarted);
+ connect(srv, &Srv::clientPrefaceOK, this, &Cl::clientPrefaceOK);
+ connect(srv, &Srv::clientPrefaceError, this, &Cl::clientPrefaceError);
+ connect(srv, &Srv::serverSettingsAcked, this, &Cl::serverSettingsAcked);
+ connect(srv, &Srv::invalidFrame, this, &Cl::invalidFrame);
+ connect(srv, &Srv::invalidRequest, this, &Cl::invalidRequest);
+ connect(srv, &Srv::receivedRequest, this, &Cl::receivedRequest);
+ connect(srv, &Srv::receivedData, this, &Cl::receivedData);
+ connect(srv, &Srv::windowUpdate, this, &Cl::windowUpdated);
+
+ srv->moveToThread(workerThread);
+
+ return srv;
+}
+
+void tst_Http2::sendRequest(int streamNumber,
+ QNetworkRequest::Priority priority,
+ const QByteArray &payload)
+{
+ static const QString urlAsString("https://127.0.0.1:%1/stream%2.html");
+
+ const QUrl url(urlAsString.arg(serverPort).arg(streamNumber));
+ QNetworkRequest request(url);
+ request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true));
+ request.setPriority(priority);
+
+ QNetworkReply *reply = nullptr;
+ if (payload.size())
+ reply = manager.post(request, payload);
+ else
+ reply = manager.get(request);
+
+ reply->ignoreSslErrors();
+ connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
+}
+
+void tst_Http2::clientPrefaceOK()
+{
+ prefaceOK = true;
+}
+
+void tst_Http2::clientPrefaceError()
+{
+ prefaceOK = false;
+}
+
+void tst_Http2::serverSettingsAcked()
+{
+ serverGotSettingsACK = true;
+}
+
+void tst_Http2::invalidFrame()
+{
+}
+
+void tst_Http2::invalidRequest(quint32 streamID)
+{
+ Q_UNUSED(streamID)
+}
+
+void tst_Http2::decompressionFailed(quint32 streamID)
+{
+ Q_UNUSED(streamID)
+}
+
+void tst_Http2::receivedRequest(quint32 streamID)
+{
+ qDebug() << " server got a request on stream" << streamID;
+ Http2Server *srv = qobject_cast<Http2Server *>(sender());
+ Q_ASSERT(srv);
+ QMetaObject::invokeMethod(srv, "sendResponse", Qt::QueuedConnection,
+ Q_ARG(quint32, streamID),
+ Q_ARG(bool, false /*non-empty body*/));
+}
+
+void tst_Http2::receivedData(quint32 streamID)
+{
+ qDebug() << " server got a 'POST' request on stream" << streamID;
+ Http2Server *srv = qobject_cast<Http2Server *>(sender());
+ Q_ASSERT(srv);
+ QMetaObject::invokeMethod(srv, "sendResponse", Qt::QueuedConnection,
+ Q_ARG(quint32, streamID),
+ Q_ARG(bool, true /*HEADERS only*/));
+}
+
+void tst_Http2::windowUpdated(quint32 streamID)
+{
+ Q_UNUSED(streamID)
+
+ ++windowUpdates;
+}
+
+void tst_Http2::replyFinished()
+{
+ QVERIFY(nRequests);
+
+ if (const auto reply = qobject_cast<QNetworkReply *>(sender()))
+ QCOMPARE(reply->error(), QNetworkReply::NoError);
+
+ --nRequests;
+ if (!nRequests)
+ stopEventLoop();
+}
+
+QT_END_NAMESPACE
+
+QTEST_MAIN(tst_Http2)
+
+#include "tst_http2.moc"
diff --git a/tests/auto/network/access/qftp/qftp.pro b/tests/auto/network/access/qftp/qftp.pro
index 4294f27e74..6f1a4a7543 100644
--- a/tests/auto/network/access/qftp/qftp.pro
+++ b/tests/auto/network/access/qftp/qftp.pro
@@ -4,10 +4,3 @@ SOURCES += tst_qftp.cpp
requires(contains(QT_CONFIG,private_tests))
QT = core network network-private testlib
-
-wince {
- addFiles.files = rfc3252.txt
- addFiles.path = .
- DEPLOYMENT += addFiles
-}
-
diff --git a/tests/auto/network/access/qftp/tst_qftp.cpp b/tests/auto/network/access/qftp/tst_qftp.cpp
index edeb471401..a13fa86405 100644
--- a/tests/auto/network/access/qftp/tst_qftp.cpp
+++ b/tests/auto/network/access/qftp/tst_qftp.cpp
@@ -276,14 +276,9 @@ void tst_QFtp::init()
inFileDirExistsFunction = false;
-#if !defined(Q_OS_WINCE)
srand(time(0));
uniqueExtension = QString::number((quintptr)this) + QString::number(rand())
+ QString::number((qulonglong)time(0));
-#else
- srand(0);
- uniqueExtension = QString::number((quintptr)this) + QString::number(rand()) + QLatin1Char('0');
-#endif
}
void tst_QFtp::cleanup()
@@ -1353,11 +1348,7 @@ void tst_QFtp::abort_data()
QTest::newRow( "get_fluke02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("qtest/rfc3252") << QByteArray();
// Qt/CE test environment has too little memory for this test
-#if !defined(Q_OS_WINCE)
QByteArray bigData( 10*1024*1024, 0 );
-#else
- QByteArray bigData( 1*1024*1024, 0 );
-#endif
bigData.fill( 'B' );
QTest::newRow( "put_fluke01" ) << QtNetworkSettings::serverName() << (uint)21 << QString("qtest/upload/abort_put") << bigData;
}
diff --git a/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp b/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp
index ef742aaa9a..84766f5484 100644
--- a/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp
+++ b/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp
@@ -151,14 +151,7 @@ void tst_QHttpNetworkConnection::head()
QHttpNetworkRequest request(protocol + host + path, QHttpNetworkRequest::Head);
QHttpNetworkReply *reply = connection.sendRequest(request);
- QTime stopWatch;
- stopWatch.start();
- do {
- QCoreApplication::instance()->processEvents();
- if (stopWatch.elapsed() >= 30000)
- break;
- } while (!reply->isFinished());
-
+ QTRY_VERIFY_WITH_TIMEOUT(reply->isFinished(), 30000);
QCOMPARE(reply->statusCode(), statusCode);
QCOMPARE(reply->reasonPhrase(), statusString);
// only check it if it is set and expected
@@ -208,15 +201,7 @@ void tst_QHttpNetworkConnection::get()
QHttpNetworkRequest request(protocol + host + path);
QHttpNetworkReply *reply = connection.sendRequest(request);
- QTime stopWatch;
- stopWatch.start();
- forever {
- QCoreApplication::instance()->processEvents();
- if (reply->bytesAvailable())
- break;
- if (stopWatch.elapsed() >= 30000)
- break;
- }
+ QTRY_VERIFY_WITH_TIMEOUT(reply->bytesAvailable(), 30000);
QCOMPARE(reply->statusCode(), statusCode);
QCOMPARE(reply->reasonPhrase(), statusString);
@@ -224,17 +209,8 @@ void tst_QHttpNetworkConnection::get()
if (reply->contentLength() != -1 && contentLength != -1)
QCOMPARE(reply->contentLength(), qint64(contentLength));
- stopWatch.start();
- QByteArray ba;
- do {
- QCoreApplication::instance()->processEvents();
- while (reply->bytesAvailable())
- ba += reply->readAny();
- if (stopWatch.elapsed() >= 30000)
- break;
- } while (!reply->isFinished());
-
- QVERIFY(reply->isFinished());
+ QTRY_VERIFY_WITH_TIMEOUT(reply->isFinished(), 30000);
+ QByteArray ba = reply->readAll();
//do not require server generated error pages to be a fixed size
if (downloadSize != -1)
QCOMPARE(ba.size(), downloadSize);
@@ -303,13 +279,7 @@ void tst_QHttpNetworkConnection::put()
connect(reply, SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),
SLOT(finishedWithError(QNetworkReply::NetworkError,QString)));
- QTime stopWatch;
- stopWatch.start();
- do {
- QCoreApplication::instance()->processEvents();
- if (stopWatch.elapsed() >= 30000)
- break;
- } while (!reply->isFinished() && !finishedCalled && !finishedWithErrorCalled);
+ QTRY_VERIFY_WITH_TIMEOUT(reply->isFinished() || finishedCalled || finishedWithErrorCalled, 30000);
if (reply->isFinished()) {
QByteArray ba;
@@ -385,16 +355,7 @@ void tst_QHttpNetworkConnection::post()
QHttpNetworkReply *reply = connection.sendRequest(request);
- QTime stopWatch;
- stopWatch.start();
- forever {
- QCoreApplication::instance()->processEvents();
- if (reply->bytesAvailable())
- break;
- if (stopWatch.elapsed() >= 30000)
- break;
- }
-
+ QTRY_VERIFY_WITH_TIMEOUT(reply->bytesAvailable(), 30000);
QCOMPARE(reply->statusCode(), statusCode);
QCOMPARE(reply->reasonPhrase(), statusString);
@@ -411,17 +372,8 @@ void tst_QHttpNetworkConnection::post()
}
}
- stopWatch.start();
- QByteArray ba;
- do {
- QCoreApplication::instance()->processEvents();
- while (reply->bytesAvailable())
- ba += reply->readAny();
- if (stopWatch.elapsed() >= 30000)
- break;
- } while (!reply->isFinished());
-
- QVERIFY(reply->isFinished());
+ QTRY_VERIFY_WITH_TIMEOUT(reply->isFinished(), 30000);
+ QByteArray ba = reply->readAll();
//don't require fixed size for generated error pages
if (downloadSize != -1)
QCOMPARE(ba.size(), downloadSize);
@@ -536,17 +488,7 @@ void tst_QHttpNetworkConnection::get401()
connect(reply, SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),
SLOT(finishedWithError(QNetworkReply::NetworkError,QString)));
- QTime stopWatch;
- stopWatch.start();
- forever {
- QCoreApplication::instance()->processEvents();
- if (finishedCalled)
- break;
- if (finishedWithErrorCalled)
- break;
- if (stopWatch.elapsed() >= 30000)
- break;
- }
+ QTRY_VERIFY_WITH_TIMEOUT(finishedCalled || finishedWithErrorCalled, 30000);
QCOMPARE(reply->statusCode(), statusCode);
delete reply;
}
@@ -595,16 +537,8 @@ void tst_QHttpNetworkConnection::compression()
if (!autoCompress)
request.setHeaderField("Accept-Encoding", contentCoding.toLatin1());
QHttpNetworkReply *reply = connection.sendRequest(request);
- QTime stopWatch;
- stopWatch.start();
- forever {
- QCoreApplication::instance()->processEvents();
- if (reply->bytesAvailable())
- break;
- if (stopWatch.elapsed() >= 30000)
- break;
- }
+ QTRY_VERIFY_WITH_TIMEOUT(reply->bytesAvailable(), 30000);
QCOMPARE(reply->statusCode(), statusCode);
QCOMPARE(reply->reasonPhrase(), statusString);
bool isLengthOk = (reply->contentLength() == qint64(contentLength)
@@ -613,17 +547,8 @@ void tst_QHttpNetworkConnection::compression()
QVERIFY(isLengthOk);
- stopWatch.start();
- QByteArray ba;
- do {
- QCoreApplication::instance()->processEvents();
- while (reply->bytesAvailable())
- ba += reply->readAny();
- if (stopWatch.elapsed() >= 30000)
- break;
- } while (!reply->isFinished());
-
- QVERIFY(reply->isFinished());
+ QTRY_VERIFY_WITH_TIMEOUT(reply->isFinished(), 30000);
+ QByteArray ba = reply->readAll();
QCOMPARE(ba.size(), downloadSize);
delete reply;
@@ -694,17 +619,7 @@ void tst_QHttpNetworkConnection::ignoresslerror()
connect(reply, SIGNAL(finished()), SLOT(finishedReply()));
- QTime stopWatch;
- stopWatch.start();
- forever {
- QCoreApplication::instance()->processEvents();
- if (reply->bytesAvailable())
- break;
- if (statusCode == 100 && finishedWithErrorCalled)
- break;
- if (stopWatch.elapsed() >= 30000)
- break;
- }
+ QTRY_VERIFY_WITH_TIMEOUT(reply->bytesAvailable() || (statusCode == 100 && finishedWithErrorCalled), 30000);
QCOMPARE(reply->statusCode(), statusCode);
delete reply;
}
@@ -746,15 +661,7 @@ void tst_QHttpNetworkConnection::nossl()
connect(reply, SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),
SLOT(finishedWithError(QNetworkReply::NetworkError,QString)));
- QTime stopWatch;
- stopWatch.start();
- forever {
- QCoreApplication::instance()->processEvents();
- if (finishedWithErrorCalled)
- break;
- if (stopWatch.elapsed() >= 30000)
- break;
- }
+ QTRY_VERIFY_WITH_TIMEOUT(finishedWithErrorCalled, 30000);
QCOMPARE(netErrorCode, networkError);
delete reply;
}
@@ -774,6 +681,15 @@ void tst_QHttpNetworkConnection::getMultiple_data()
QTest::newRow("1 connection, pipelining allowed, 100 requests") << quint16(1) << true << 100;
}
+static bool allRepliesFinished(const QList<QHttpNetworkReply*> *_replies)
+{
+ const QList<QHttpNetworkReply*> &replies = *_replies;
+ for (int i = 0; i < replies.length(); i++)
+ if (!replies.at(i)->isFinished())
+ return false;
+ return true;
+}
+
void tst_QHttpNetworkConnection::getMultiple()
{
QFETCH(quint16, connectionCount);
@@ -797,27 +713,7 @@ void tst_QHttpNetworkConnection::getMultiple()
replies.append(reply);
}
- QTime stopWatch;
- stopWatch.start();
- int finishedCount = 0;
- do {
- QCoreApplication::instance()->processEvents();
- if (stopWatch.elapsed() >= 60000)
- break;
-
- finishedCount = 0;
- for (int i = 0; i < replies.length(); i++)
- if (replies.at(i)->isFinished())
- finishedCount++;
-
- } while (finishedCount != replies.length());
-
- // redundant
- for (int i = 0; i < replies.length(); i++)
- QVERIFY(replies.at(i)->isFinished());
-
- qDebug() << "===" << stopWatch.elapsed() << "msec ===";
-
+ QTRY_VERIFY_WITH_TIMEOUT(allRepliesFinished(&replies), 60000);
qDeleteAll(requests);
qDeleteAll(replies);
}
@@ -854,24 +750,10 @@ void tst_QHttpNetworkConnection::getMultipleWithPipeliningAndMultiplePriorities(
replies.append(reply);
}
- QTime stopWatch;
- stopWatch.start();
- int finishedCount = 0;
- do {
- QCoreApplication::instance()->processEvents();
- if (stopWatch.elapsed() >= 60000)
- break;
-
- finishedCount = 0;
- for (int i = 0; i < replies.length(); i++)
- if (replies.at(i)->isFinished())
- finishedCount++;
-
- } while (finishedCount != replies.length());
+ QTRY_VERIFY_WITH_TIMEOUT(allRepliesFinished(&replies), 60000);
int pipelinedCount = 0;
for (int i = 0; i < replies.length(); i++) {
- QVERIFY(replies.at(i)->isFinished());
QVERIFY (!(replies.at(i)->request().isPipeliningAllowed() == false
&& replies.at(i)->isPipeliningUsed()));
@@ -885,8 +767,6 @@ void tst_QHttpNetworkConnection::getMultipleWithPipeliningAndMultiplePriorities(
// requests had been pipelined)
QVERIFY(pipelinedCount >= requestCount / 2);
- qDebug() << "===" << stopWatch.elapsed() << "msec ===";
-
qDeleteAll(requests);
qDeleteAll(replies);
}
@@ -1062,17 +942,7 @@ void tst_QHttpNetworkConnection::getAndThenDeleteObject()
QHttpNetworkReply *reply = connection->sendRequest(request);
reply->setDownstreamLimited(true);
- QTime stopWatch;
- stopWatch.start();
- forever {
- QCoreApplication::instance()->processEvents();
- if (reply->bytesAvailable())
- break;
- if (stopWatch.elapsed() >= 30000)
- break;
- }
-
- QVERIFY(reply->bytesAvailable());
+ QTRY_VERIFY_WITH_TIMEOUT(reply->bytesAvailable(), 30000);
QCOMPARE(reply->statusCode() ,200);
QVERIFY(!reply->isFinished()); // must not be finished
diff --git a/tests/auto/network/access/qnetworkreply/qnetworkreply.pro b/tests/auto/network/access/qnetworkreply/qnetworkreply.pro
index bd10c77252..d3a92436ac 100644
--- a/tests/auto/network/access/qnetworkreply/qnetworkreply.pro
+++ b/tests/auto/network/access/qnetworkreply/qnetworkreply.pro
@@ -1,5 +1,5 @@
TEMPLATE = subdirs
-!winrt:!wince: SUBDIRS += echo
+!winrt:SUBDIRS += echo
test.depends += $$SUBDIRS
SUBDIRS += test
diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
index 93bdbdd6a5..43e05c95f9 100644
--- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
+++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
@@ -205,6 +205,7 @@ private Q_SLOTS:
void invalidProtocol();
void getFromData_data();
void getFromData();
+ void getFromFile_data();
void getFromFile();
void getFromFileSpecial_data();
void getFromFileSpecial();
@@ -650,8 +651,10 @@ private slots:
#endif
void slotError(QAbstractSocket::SocketError err)
{
- Q_ASSERT(!client.isNull());
- qDebug() << "slotError" << err << client->errorString();
+ if (client.isNull())
+ qDebug() << "slotError" << err;
+ else
+ qDebug() << "slotError" << err << client->errorString();
}
public slots:
@@ -1674,14 +1677,26 @@ void tst_QNetworkReply::getFromData()
QCOMPARE(reply->readAll(), expected);
}
+void tst_QNetworkReply::getFromFile_data()
+{
+ QTest::addColumn<bool>("backgroundAttribute");
+
+ QTest::newRow("no-background-attribute") << false;
+ QTest::newRow("background-attribute") << true;
+}
+
void tst_QNetworkReply::getFromFile()
{
+ QFETCH(bool, backgroundAttribute);
+
// create the file:
QTemporaryFile file(QDir::currentPath() + "/temp-XXXXXX");
file.setAutoRemove(true);
QVERIFY2(file.open(), qPrintable(file.errorString()));
QNetworkRequest request(QUrl::fromLocalFile(file.fileName()));
+ if (backgroundAttribute)
+ request.setAttribute(QNetworkRequest::BackgroundRequestAttribute, QVariant::fromValue(true));
QNetworkReplyPtr reply;
static const char fileData[] = "This is some data that is in the file.\r\n";
@@ -1691,6 +1706,7 @@ void tst_QNetworkReply::getFromFile()
QCOMPARE(file.size(), qint64(data.size()));
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply));
+ QVERIFY(waitForFinish(reply) != Timeout);
QCOMPARE(reply->url(), request.url());
QCOMPARE(reply->error(), QNetworkReply::NoError);
@@ -4319,9 +4335,6 @@ void tst_QNetworkReply::ioPutToFileFromProcess()
QSKIP("No qprocess support", SkipAll);
#else
-#if defined(Q_OS_WINCE)
- QSKIP("Currently no stdin/out supported for Windows CE");
-#else
#ifdef Q_OS_WIN
if (qstrcmp(QTest::currentDataTag(), "small") == 0)
QSKIP("When passing a CR-LF-LF sequence through Windows stdio, it gets converted, "
@@ -4355,7 +4368,6 @@ void tst_QNetworkReply::ioPutToFileFromProcess()
QCOMPARE(file.size(), qint64(data.size()));
QByteArray contents = file.readAll();
QCOMPARE(contents, data);
-#endif
#endif // QT_NO_PROCESS
}
@@ -6328,17 +6340,7 @@ void tst_QNetworkReply::getAndThenDeleteObject()
reply->setReadBufferSize(1);
reply->setParent((QObject*)0); // must be 0 because else it is the manager
- QTime stopWatch;
- stopWatch.start();
- forever {
- QCoreApplication::instance()->processEvents();
- if (reply->bytesAvailable())
- break;
- if (stopWatch.elapsed() >= 30000)
- break;
- }
-
- QVERIFY(reply->bytesAvailable());
+ QTRY_VERIFY_WITH_TIMEOUT(reply->bytesAvailable(), 30000);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QVERIFY(!reply->isFinished()); // must not be finished
@@ -6561,12 +6563,7 @@ void tst_QNetworkReply::getFromHttpIntoBuffer2()
QFETCH(bool, useDownloadBuffer);
// On my Linux Desktop the results are already visible with 128 kB, however we use this to have good results.
-#if defined(Q_OS_WINCE_WM)
- // Show some mercy to non-desktop platform/s
- enum {UploadSize = 4*1024*1024}; // 4 MB
-#else
enum {UploadSize = 32*1024*1024}; // 32 MB
-#endif
GetFromHttpIntoBuffer2Server server(UploadSize, true, false);
@@ -7892,10 +7889,6 @@ void tst_QNetworkReply::backgroundRequestInterruption()
QNetworkSessionPrivate::setUsagePolicies(*const_cast<QNetworkSession *>(session.data()), original);
QVERIFY(reply->isFinished());
-#ifdef Q_OS_OSX
- if (QSysInfo::MacintoshVersion == QSysInfo::MV_10_8)
- QEXPECT_FAIL("ftp, bg, nobg", "See QTBUG-32435", Abort);
-#endif
QCOMPARE(reply->error(), error);
#endif
}
diff --git a/tests/auto/network/kernel/kernel.pro b/tests/auto/network/kernel/kernel.pro
index bb13c7dd7d..6c3a234de5 100644
--- a/tests/auto/network/kernel/kernel.pro
+++ b/tests/auto/network/kernel/kernel.pro
@@ -7,6 +7,7 @@ SUBDIRS=\
qauthenticator \
qnetworkproxy \
qnetworkinterface \
+ qnetworkdatagram \
qnetworkaddressentry \
qhostaddress \
diff --git a/tests/auto/network/kernel/qhostaddress/qhostaddress.pro b/tests/auto/network/kernel/qhostaddress/qhostaddress.pro
index 19d74dfd9b..a79fa2f59d 100644
--- a/tests/auto/network/kernel/qhostaddress/qhostaddress.pro
+++ b/tests/auto/network/kernel/qhostaddress/qhostaddress.pro
@@ -5,10 +5,4 @@ SOURCES += tst_qhostaddress.cpp
QT = core network testlib
-win32: {
-wince {
- LIBS += -lws2
-} else {
- LIBS += -lws2_32
-}
-}
+win32:LIBS += -lws2_32
diff --git a/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp b/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp
index 4fb97fe1f2..419c781aab 100644
--- a/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp
+++ b/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp
@@ -62,6 +62,8 @@ private slots:
void specialAddresses();
void compare_data();
void compare();
+ void isEqual_data();
+ void isEqual();
void assignment();
void scopeId();
void hashKey();
@@ -291,6 +293,7 @@ void tst_QHostAddress::compare_data()
QTest::newRow("5") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::Broadcast) << false;
QTest::newRow("6") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHostIPv6) << false;
QTest::newRow("7") << QHostAddress() << QHostAddress(QHostAddress::LocalHostIPv6) << false;
+ QTest::newRow("any4-any6") << QHostAddress(QHostAddress::AnyIPv4) << QHostAddress(QHostAddress::AnyIPv6) << false;
Q_IPV6ADDR localhostv4mapped = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 127, 0, 0, 1 } };
QTest::newRow("v4-v4mapped") << QHostAddress(QHostAddress::LocalHost) << QHostAddress("::ffff:127.0.0.1") << false;
@@ -309,6 +312,53 @@ void tst_QHostAddress::compare()
QCOMPARE(qHash(first), qHash(second));
}
+void tst_QHostAddress::isEqual_data()
+{
+ QTest::addColumn<QHostAddress>("first");
+ QTest::addColumn<QHostAddress>("second");
+ QTest::addColumn<int>("flags");
+ QTest::addColumn<bool>("result");
+
+ // QHostAddress::StrictConversion is already tested in compare()
+ QTest::newRow("localhost4to6-local") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHostIPv6) << (int)QHostAddress::ConvertLocalHost << true;
+ QTest::newRow("localhost4to6-compat") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHostIPv6) << (int)QHostAddress::ConvertV4CompatToIPv4 << false;
+ QTest::newRow("localhost4to6-mapped") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHostIPv6) << (int)QHostAddress::ConvertV4MappedToIPv4 << false;
+ QTest::newRow("localhost4to6-unspec") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHostIPv6) << (int)QHostAddress::ConvertUnspecifiedAddress << false;
+ QTest::newRow("0.0.0.1-::1-local") << QHostAddress("0.0.0.1") << QHostAddress(QHostAddress::LocalHostIPv6) << (int)QHostAddress::ConvertLocalHost << false;
+ QTest::newRow("v4-v4compat-local") << QHostAddress("192.168.1.1") << QHostAddress("::192.168.1.1") << (int)QHostAddress::ConvertLocalHost << false;
+ QTest::newRow("v4-v4mapped-local") << QHostAddress("192.168.1.1") << QHostAddress("::ffff:192.168.1.1") << (int)QHostAddress::ConvertLocalHost << false;
+ QTest::newRow("0.0.0.1-::1-unspec") << QHostAddress("0.0.0.1") << QHostAddress(QHostAddress::LocalHostIPv6) << (int)QHostAddress::ConvertUnspecifiedAddress << false;
+ QTest::newRow("v4-v4compat-unspec") << QHostAddress("192.168.1.1") << QHostAddress("::192.168.1.1") << (int)QHostAddress::ConvertUnspecifiedAddress << false;
+ QTest::newRow("v4-v4mapped-unspec") << QHostAddress("192.168.1.1") << QHostAddress("::ffff:192.168.1.1") << (int)QHostAddress::ConvertUnspecifiedAddress << false;
+ QTest::newRow("0.0.0.1-::1-compat") << QHostAddress("0.0.0.1") << QHostAddress(QHostAddress::LocalHostIPv6) << (int)QHostAddress::ConvertV4CompatToIPv4 << false;
+ QTest::newRow("v4-v4compat-compat") << QHostAddress("192.168.1.1") << QHostAddress("::192.168.1.1") << (int)QHostAddress::ConvertV4CompatToIPv4 << true;
+ QTest::newRow("v4-v4mapped-compat") << QHostAddress("192.168.1.1") << QHostAddress("::ffff:192.168.1.1") << (int)QHostAddress::ConvertV4CompatToIPv4 << false;
+ QTest::newRow("0.0.0.1-::1-mapped") << QHostAddress("0.0.0.1") << QHostAddress(QHostAddress::LocalHostIPv6) << (int)QHostAddress::ConvertV4MappedToIPv4 << false;
+ QTest::newRow("v4-v4compat-mapped") << QHostAddress("192.168.1.1") << QHostAddress("::192.168.1.1") << (int)QHostAddress::ConvertV4MappedToIPv4 << false;
+ QTest::newRow("v4-v4mapped-mapped") << QHostAddress("192.168.1.1") << QHostAddress("::FFFF:192.168.1.1") << (int)QHostAddress::ConvertV4MappedToIPv4 << true;
+ QTest::newRow("undef-any-local") << QHostAddress() << QHostAddress(QHostAddress::Any) << (int)QHostAddress::ConvertLocalHost << false;
+ QTest::newRow("undef-any-unspec") << QHostAddress() << QHostAddress(QHostAddress::Any) << (int)QHostAddress::ConvertUnspecifiedAddress << false;
+ QTest::newRow("anyv6-anyv4-compat") << QHostAddress(QHostAddress::AnyIPv6) << QHostAddress(QHostAddress::AnyIPv4) << (int)QHostAddress::ConvertV4CompatToIPv4 << true;
+ QTest::newRow("anyv6-anyv4-mapped") << QHostAddress(QHostAddress::AnyIPv6) << QHostAddress(QHostAddress::AnyIPv4) << (int)QHostAddress::ConvertV4MappedToIPv4 << false;
+ QTest::newRow("anyv6-anyv4-unspec") << QHostAddress(QHostAddress::AnyIPv6) << QHostAddress(QHostAddress::AnyIPv4) << (int)QHostAddress::ConvertUnspecifiedAddress << true;
+ QTest::newRow("any-anyv4-unspec") << QHostAddress(QHostAddress::Any) << QHostAddress(QHostAddress::AnyIPv4) << (int)QHostAddress::ConvertUnspecifiedAddress << true;
+ QTest::newRow("any-anyv6-unspec") << QHostAddress(QHostAddress::Any) << QHostAddress(QHostAddress::AnyIPv6) << (int)QHostAddress::ConvertUnspecifiedAddress << true;
+ QTest::newRow("anyv6-anyv4-local") << QHostAddress(QHostAddress::AnyIPv6) << QHostAddress(QHostAddress::AnyIPv4) << (int)QHostAddress::ConvertLocalHost << false;
+ QTest::newRow("any-anyv4-local") << QHostAddress(QHostAddress::Any) << QHostAddress(QHostAddress::AnyIPv4) << (int)QHostAddress::ConvertLocalHost << false;
+ QTest::newRow("any-anyv6-local") << QHostAddress(QHostAddress::Any) << QHostAddress(QHostAddress::AnyIPv6) << (int)QHostAddress::ConvertLocalHost << false;
+}
+
+void tst_QHostAddress::isEqual()
+{
+ QFETCH(QHostAddress, first);
+ QFETCH(QHostAddress, second);
+ QFETCH(int, flags);
+ QFETCH(bool, result);
+
+ QCOMPARE(first.isEqual(second, QHostAddress::ConversionModeFlag(flags)), result);
+ QCOMPARE(second.isEqual(first, QHostAddress::ConversionModeFlag(flags)), result);
+}
+
void tst_QHostAddress::assignment()
{
QHostAddress address;
diff --git a/tests/auto/network/kernel/qhostinfo/qhostinfo.pro b/tests/auto/network/kernel/qhostinfo/qhostinfo.pro
index 12858c97ee..4ea415c529 100644
--- a/tests/auto/network/kernel/qhostinfo/qhostinfo.pro
+++ b/tests/auto/network/kernel/qhostinfo/qhostinfo.pro
@@ -6,11 +6,7 @@ SOURCES += tst_qhostinfo.cpp
requires(contains(QT_CONFIG,private_tests))
QT = core-private network-private testlib
-wince {
- LIBS += ws2.lib
-} else {
- win32:LIBS += -lws2_32
-}
+win32:LIBS += -lws2_32
# needed for getaddrinfo with official MinGW
mingw:DEFINES += _WIN32_WINNT=0x0501
diff --git a/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp b/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp
index 13d4442ada..f6d9b71aa2 100644
--- a/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp
+++ b/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp
@@ -66,11 +66,7 @@
#include "private/qhostinfo_p.h"
#if !defined(QT_NO_GETADDRINFO)
-# if !defined(Q_OS_WINCE)
# include <sys/types.h>
-# else
-# include <types.h>
-# endif
# if defined(Q_OS_UNIX)
# include <sys/socket.h>
# endif
@@ -399,11 +395,7 @@ protected:
void tst_QHostInfo::threadSafety()
{
const int nattempts = 5;
-#if defined(Q_OS_WINCE)
- const int runs = 10;
-#else
const int runs = 100;
-#endif
LookupThread thr[nattempts];
for (int j = 0; j < runs; ++j) {
for (int i = 0; i < nattempts; ++i)
diff --git a/tests/auto/network/kernel/qnetworkdatagram/qnetworkdatagram.pro b/tests/auto/network/kernel/qnetworkdatagram/qnetworkdatagram.pro
new file mode 100644
index 0000000000..a2fe44060e
--- /dev/null
+++ b/tests/auto/network/kernel/qnetworkdatagram/qnetworkdatagram.pro
@@ -0,0 +1,5 @@
+CONFIG += testcase console
+CONFIG -= app_bundle
+TARGET = tst_qnetworkdatagram
+SOURCES += tst_qnetworkdatagram.cpp
+QT = core network testlib
diff --git a/tests/auto/network/kernel/qnetworkdatagram/tst_qnetworkdatagram.cpp b/tests/auto/network/kernel/qnetworkdatagram/tst_qnetworkdatagram.cpp
new file mode 100644
index 0000000000..3295580432
--- /dev/null
+++ b/tests/auto/network/kernel/qnetworkdatagram/tst_qnetworkdatagram.cpp
@@ -0,0 +1,146 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Intel Corporation.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QNetworkDatagram>
+#include <QtTest>
+#include <QCoreApplication>
+
+class tst_QNetworkDatagram : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QNetworkDatagram();
+
+private Q_SLOTS:
+ void getSetCheck();
+ void makeReply_data();
+ void makeReply();
+};
+
+tst_QNetworkDatagram::tst_QNetworkDatagram()
+{
+}
+
+void tst_QNetworkDatagram::getSetCheck()
+{
+ QNetworkDatagram dg;
+
+ QVERIFY(dg.isNull());
+ QVERIFY(!dg.isValid());
+ QCOMPARE(dg.senderAddress(), QHostAddress());
+ QCOMPARE(dg.destinationAddress(), QHostAddress());
+ QCOMPARE(dg.senderPort(), -1);
+ QCOMPARE(dg.destinationPort(), -1);
+ QCOMPARE(dg.hopLimit(), -1);
+ QCOMPARE(dg.interfaceIndex(), 0U);
+
+ dg.setHopLimit(1);
+ QCOMPARE(dg.hopLimit(), 1);
+ dg.setHopLimit(255);
+ QCOMPARE(dg.hopLimit(), 255);
+
+ dg.setInterfaceIndex(1);
+ QCOMPARE(dg.interfaceIndex(), 1U);
+ dg.setInterfaceIndex(1234567U);
+ QCOMPARE(dg.interfaceIndex(), 1234567U);
+
+ dg.setSender(QHostAddress::Any, 12345);
+ QCOMPARE(dg.senderAddress(), QHostAddress(QHostAddress::Any));
+ QCOMPARE(dg.senderPort(), 12345);
+ dg.setSender(QHostAddress::LocalHost);
+ QCOMPARE(dg.senderAddress(), QHostAddress(QHostAddress::LocalHost));
+ QCOMPARE(dg.senderPort(), 0);
+
+ dg.setDestination(QHostAddress::LocalHostIPv6, 12345);
+ QCOMPARE(dg.destinationAddress(), QHostAddress(QHostAddress::LocalHostIPv6));
+ QCOMPARE(dg.destinationPort(), 12345);
+ dg.setDestination(QHostAddress::Broadcast, 137);
+ QCOMPARE(dg.destinationAddress(), QHostAddress(QHostAddress::Broadcast));
+ QCOMPARE(dg.destinationPort(), 137);
+}
+
+void tst_QNetworkDatagram::makeReply_data()
+{
+ qRegisterMetaType<QNetworkDatagram>();
+ QTest::addColumn<QNetworkDatagram>("dgram");
+ QTest::addColumn<QString>("localAddress");
+
+ QNetworkDatagram dgram("some data", QHostAddress("192.0.2.1"), 10001);
+ dgram.setHopLimit(64);
+ dgram.setSender(QHostAddress::LocalHost, 12345);
+ QTest::newRow("ipv4") << dgram << "192.0.2.1";
+
+ dgram.setDestination(QHostAddress("224.0.0.1"), 10002);
+ QTest::newRow("ipv4-multicast") << dgram << QString();
+
+ dgram.setSender(QHostAddress::LocalHostIPv6, 12346);
+ dgram.setDestination(QHostAddress("2001:db8::1"), 12347);
+ QTest::newRow("ipv6") << dgram << "2001:db8::1";
+
+ dgram.setSender(QHostAddress("fe80::1%1"), 10003);
+ dgram.setDestination(QHostAddress("fe80::2%1"), 10004);
+ dgram.setInterfaceIndex(1);
+ QTest::newRow("ipv6-linklocal") << dgram << "fe80::2%1";
+
+ dgram.setDestination(QHostAddress("ff02::1%1"), 10005);
+ QTest::newRow("ipv6-multicast") << dgram << QString();
+}
+
+void tst_QNetworkDatagram::makeReply()
+{
+ QFETCH(QNetworkDatagram, dgram);
+ QFETCH(QString, localAddress);
+
+ {
+ QNetworkDatagram reply = dgram.makeReply("World");
+ QCOMPARE(reply.data(), QByteArray("World"));
+ QCOMPARE(reply.senderAddress(), QHostAddress(localAddress));
+ QCOMPARE(reply.senderPort(), localAddress.isEmpty() ? -1 : dgram.destinationPort());
+ QCOMPARE(reply.destinationAddress(), dgram.senderAddress());
+ QCOMPARE(reply.destinationPort(), dgram.senderPort());
+ QCOMPARE(reply.interfaceIndex(), dgram.interfaceIndex());
+ QCOMPARE(reply.hopLimit(), -1);
+ }
+
+ QNetworkDatagram copy = dgram;
+ copy.setData(copy.data());
+ {
+ QNetworkDatagram reply = qMove(copy).makeReply("World");
+ QCOMPARE(reply.data(), QByteArray("World"));
+ QCOMPARE(reply.senderAddress(), QHostAddress(localAddress));
+ QCOMPARE(reply.senderPort(), localAddress.isEmpty() ? -1 : dgram.destinationPort());
+ QCOMPARE(reply.destinationAddress(), dgram.senderAddress());
+ QCOMPARE(reply.destinationPort(), dgram.senderPort());
+ QCOMPARE(reply.interfaceIndex(), dgram.interfaceIndex());
+ QCOMPARE(reply.hopLimit(), -1);
+ }
+}
+
+QTEST_MAIN(tst_QNetworkDatagram)
+#include "tst_qnetworkdatagram.moc"
diff --git a/tests/auto/network/socket/platformsocketengine/platformsocketengine.pri b/tests/auto/network/socket/platformsocketengine/platformsocketengine.pri
index a3b4e89450..6c7857ea1d 100644
--- a/tests/auto/network/socket/platformsocketengine/platformsocketengine.pri
+++ b/tests/auto/network/socket/platformsocketengine/platformsocketengine.pri
@@ -4,13 +4,7 @@ QNETWORK_SRC = $$QT_SOURCE_TREE/src/network
INCLUDEPATH += $$QNETWORK_SRC
-win32 {
- wince {
- LIBS += -lws2
- } else {
- LIBS += -lws2_32
- }
-}
+win32:LIBS += -lws2_32
unix:contains(QT_CONFIG, reduce_exports) {
SOURCES += $$QNETWORK_SRC/socket/qnativesocketengine_unix.cpp
diff --git a/tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp b/tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp
index 041a0ce16c..d159d6d683 100644
--- a/tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp
+++ b/tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp
@@ -494,9 +494,6 @@ void tst_PlatformSocketEngine::readWriteBufferSize()
qint64 bufferSize = device.receiveBufferSize();
QVERIFY(bufferSize != -1);
device.setReceiveBufferSize(bufferSize + 1);
-#if defined(Q_OS_WINCE)
- QEXPECT_FAIL(0, "Not supported by default on WinCE", Continue);
-#endif
QVERIFY(device.receiveBufferSize() > bufferSize);
bufferSize = device.sendBufferSize();
@@ -649,7 +646,7 @@ void tst_PlatformSocketEngine::receiveUrgentData()
QByteArray response;
// Native OOB data test doesn't work on HP-UX or WinCE
-#if !defined(Q_OS_HPUX) && !defined(Q_OS_WINCE)
+#if !defined(Q_OS_HPUX)
// The server sends an urgent message
msg = 'Q';
QCOMPARE(int(::send(socketDescriptor, &msg, sizeof(msg), MSG_OOB)), 1);
diff --git a/tests/auto/network/socket/qhttpsocketengine/tst_qhttpsocketengine.cpp b/tests/auto/network/socket/qhttpsocketengine/tst_qhttpsocketengine.cpp
index 7237542e5c..68f3ea059b 100644
--- a/tests/auto/network/socket/qhttpsocketengine/tst_qhttpsocketengine.cpp
+++ b/tests/auto/network/socket/qhttpsocketengine/tst_qhttpsocketengine.cpp
@@ -626,11 +626,7 @@ void tst_QHttpSocketEngine::downloadBigFile()
QTime stopWatch;
stopWatch.start();
-#if defined(Q_OS_WINCE)
- QTestEventLoop::instance().enterLoop(240);
-#else
QTestEventLoop::instance().enterLoop(60);
-#endif
if (QTestEventLoop::instance().timeout())
QFAIL("Network operation timed out");
diff --git a/tests/auto/network/socket/qlocalsocket/test/test.pro b/tests/auto/network/socket/qlocalsocket/test/test.pro
index 6a5df7f9b6..ab9ed90b1d 100644
--- a/tests/auto/network/socket/qlocalsocket/test/test.pro
+++ b/tests/auto/network/socket/qlocalsocket/test/test.pro
@@ -2,13 +2,7 @@ CONFIG += testcase
DEFINES += QLOCALSERVER_DEBUG
DEFINES += QLOCALSOCKET_DEBUG
-
-wince* {
- DEFINES += QT_LOCALSOCKET_TCP
- DEFINES += SRCDIR=\\\"../\\\"
-} else {
- DEFINES += SRCDIR=\\\"$$PWD/../\\\"
-}
+DEFINES += SRCDIR=\\\"$$PWD/../\\\"
QT = core network testlib
diff --git a/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp b/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp
index 4bea9b116c..00e01094a4 100644
--- a/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp
+++ b/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp
@@ -867,10 +867,8 @@ void tst_QLocalSocket::threadedConnection_data()
QTest::newRow("1 client") << 1;
QTest::newRow("2 clients") << 2;
QTest::newRow("5 clients") << 5;
-#ifndef Q_OS_WINCE
QTest::newRow("10 clients") << 10;
QTest::newRow("20 clients") << 20;
-#endif
}
void tst_QLocalSocket::threadedConnection()
diff --git a/tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp b/tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp
index b4955df107..c945d77cda 100644
--- a/tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp
+++ b/tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp
@@ -785,11 +785,7 @@ void tst_QSocks5SocketEngine::downloadBigFile()
QTime stopWatch;
stopWatch.start();
-#if !defined(Q_OS_WINCE)
QTestEventLoop::instance().enterLoop(60);
-#else
- QTestEventLoop::instance().enterLoop(180);
-#endif
if (QTestEventLoop::instance().timeout())
QFAIL("Network operation timed out");
diff --git a/tests/auto/network/socket/qtcpserver/crashingServer/main.cpp b/tests/auto/network/socket/qtcpserver/crashingServer/main.cpp
index fdf1c48adf..1a8e7920d3 100644
--- a/tests/auto/network/socket/qtcpserver/crashingServer/main.cpp
+++ b/tests/auto/network/socket/qtcpserver/crashingServer/main.cpp
@@ -47,16 +47,8 @@ int main(int argc, char *argv[])
return 1;
}
-#if defined(Q_OS_WINCE)
- QFile file(QLatin1String("/test_signal.txt"));
- file.open(QIODevice::WriteOnly);
- file.write("Listening\n");
- file.flush();
- file.close();
-#else
printf("Listening\n");
fflush(stdout);
-#endif
server.waitForNewConnection(5000);
qFatal("Crash");
diff --git a/tests/auto/network/socket/qtcpserver/test/test.pro b/tests/auto/network/socket/qtcpserver/test/test.pro
index f0abfbc085..4491523383 100644
--- a/tests/auto/network/socket/qtcpserver/test/test.pro
+++ b/tests/auto/network/socket/qtcpserver/test/test.pro
@@ -1,16 +1,7 @@
CONFIG += testcase
SOURCES += ../tst_qtcpserver.cpp
-win32: {
-wince {
- LIBS += -lws2
- crashApp.files = ../crashingServer/crashingServer.exe
- crashApp.path = crashingServer
- DEPLOYMENT += crashApp
-} else {
- LIBS += -lws2_32
-}
-}
+win32:LIBS += -lws2_32
TARGET = ../tst_qtcpserver
diff --git a/tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp b/tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp
index 30aab3bf34..5a0baf73b5 100644
--- a/tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp
+++ b/tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp
@@ -106,6 +106,8 @@ private slots:
void eagainBlockingAccept();
+ void canAccessPendingConnectionsWhileNotListening();
+
private:
#ifndef QT_NO_BEARERMANAGEMENT
QNetworkSession *networkSession;
@@ -467,11 +469,7 @@ void tst_QTcpServer::waitForConnectionTest()
ThreadConnector connector(findLocalIpSocket.localAddress(), server.serverPort());
connector.start();
-#if defined(Q_OS_WINCE)
- QVERIFY(server.waitForNewConnection(9000, &timeout));
-#else
QVERIFY(server.waitForNewConnection(3000, &timeout));
-#endif
QVERIFY(!timeout);
}
@@ -562,21 +560,6 @@ void tst_QTcpServer::addressReusable()
QSKIP("No proxy support");
#endif // QT_NO_NETWORKPROXY
}
-#if defined(Q_OS_WINCE)
- QString signalName = QString::fromLatin1("/test_signal.txt");
- QFile::remove(signalName);
- // The crashingServer process will crash once it gets a connection.
- QProcess process;
- QString processExe = crashingServerDir + "/crashingServer";
- process.start(processExe);
- QVERIFY2(process.waitForStarted(), qPrintable(
- QString::fromLatin1("Could not start %1: %2").arg(processExe, process.errorString())));
- int waitCount = 5;
- while (waitCount-- && !QFile::exists(signalName))
- QTest::qWait(1000);
- QVERIFY(QFile::exists(signalName));
- QFile::remove(signalName);
-#else
// The crashingServer process will crash once it gets a connection.
QProcess process;
QString processExe = crashingServerDir + "/crashingServer";
@@ -584,7 +567,6 @@ void tst_QTcpServer::addressReusable()
QVERIFY2(process.waitForStarted(), qPrintable(
QString::fromLatin1("Could not start %1: %2").arg(processExe, process.errorString())));
QVERIFY(process.waitForReadyRead(5000));
-#endif
QTcpSocket socket;
socket.connectToHost(QHostAddress::LocalHost, 49199);
@@ -990,5 +972,22 @@ void tst_QTcpServer::eagainBlockingAccept()
server.close();
}
+class NonListeningTcpServer : public QTcpServer
+{
+public:
+ void addSocketFromOutside(QTcpSocket* s)
+ {
+ addPendingConnection(s);
+ }
+};
+
+void tst_QTcpServer::canAccessPendingConnectionsWhileNotListening()
+{
+ NonListeningTcpServer server;
+ QTcpSocket socket;
+ server.addSocketFromOutside(&socket);
+ QCOMPARE(&socket, server.nextPendingConnection());
+}
+
QTEST_MAIN(tst_QTcpServer)
#include "tst_qtcpserver.moc"
diff --git a/tests/auto/network/socket/qtcpsocket/qtcpsocket.pro b/tests/auto/network/socket/qtcpsocket/qtcpsocket.pro
index fe6042b8a7..5686f4a697 100644
--- a/tests/auto/network/socket/qtcpsocket/qtcpsocket.pro
+++ b/tests/auto/network/socket/qtcpsocket/qtcpsocket.pro
@@ -1,6 +1,6 @@
TEMPLATE = subdirs
SUBDIRS = test
-!wince:!vxworks: SUBDIRS += stressTest
+!vxworks: SUBDIRS += stressTest
requires(contains(QT_CONFIG,private_tests))
diff --git a/tests/auto/network/socket/qtcpsocket/test/test.pro b/tests/auto/network/socket/qtcpsocket/test/test.pro
index 3e64b87b53..337e75b372 100644
--- a/tests/auto/network/socket/qtcpsocket/test/test.pro
+++ b/tests/auto/network/socket/qtcpsocket/test/test.pro
@@ -2,13 +2,7 @@ CONFIG += testcase
QT = core-private network-private testlib
SOURCES += ../tst_qtcpsocket.cpp
-win32: {
-wince {
- LIBS += -lws2
-} else {
- LIBS += -lws2_32
-}
-}
+win32:LIBS += -lws2_32
TARGET = tst_qtcpsocket
diff --git a/tests/auto/network/socket/qudpsocket/test/test.pro b/tests/auto/network/socket/qudpsocket/test/test.pro
index e4812416dc..73486a2bc3 100644
--- a/tests/auto/network/socket/qudpsocket/test/test.pro
+++ b/tests/auto/network/socket/qudpsocket/test/test.pro
@@ -15,10 +15,4 @@ win32 {
DESTDIR = ../
}
-wince* {
- addApp.files = ../clientserver/clientserver.exe
- addApp.path = clientserver
- DEPLOYMENT += addApp
-}
-
TARGET = tst_qudpsocket
diff --git a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp
index ba49e8b041..aa01384350 100644
--- a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp
+++ b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp
@@ -39,6 +39,7 @@
#include <qhostinfo.h>
#include <qtcpsocket.h>
#include <qmap.h>
+#include <qnetworkdatagram.h>
#include <QNetworkProxy>
#include <QNetworkInterface>
@@ -114,6 +115,7 @@ protected slots:
void async_readDatagramSlot();
private:
+ QList<QHostAddress> allAddresses;
#ifndef QT_NO_BEARERMANAGEMENT
QNetworkConfigurationManager *netConfMan;
QNetworkConfiguration networkConfiguration;
@@ -173,6 +175,7 @@ void tst_QUdpSocket::initTestCase()
{
if (!QtNetworkSettings::verifyTestNetworkSettings())
QSKIP("No network test server available");
+ allAddresses = QNetworkInterface::allAddresses();
}
void tst_QUdpSocket::init()
@@ -252,6 +255,11 @@ void tst_QUdpSocket::unconnectedServerAndClientTest()
int(strlen(message[i])));
buf[strlen(message[i])] = '\0';
QCOMPARE(QByteArray(buf), QByteArray(message[i]));
+ QCOMPARE(port, clientSocket.localPort());
+ if (host.toIPv4Address()) // in case the sender is IPv4 mapped in IPv6
+ QCOMPARE(host.toIPv4Address(), makeNonAny(clientSocket.localAddress()).toIPv4Address());
+ else
+ QCOMPARE(host, makeNonAny(clientSocket.localAddress()));
}
}
@@ -325,14 +333,32 @@ void tst_QUdpSocket::broadcasting()
QVERIFY(serverSocket.hasPendingDatagrams());
do {
- QByteArray arr; arr.resize(serverSocket.pendingDatagramSize() + 1);
- QHostAddress host;
- quint16 port;
const int messageLength = int(strlen(message[i]));
- QCOMPARE((int) serverSocket.readDatagram(arr.data(), arr.size() - 1, &host, &port),
- messageLength);
+ QNetworkDatagram dgram = serverSocket.receiveDatagram();
+ QVERIFY(dgram.isValid());
+ QByteArray arr = dgram.data();
+
+ QCOMPARE(arr.length(), messageLength);
arr.resize(messageLength);
QCOMPARE(arr, QByteArray(message[i]));
+
+ if (dgram.senderAddress().toIPv4Address()) // in case it's a v6-mapped address
+ QVERIFY2(allAddresses.contains(QHostAddress(dgram.senderAddress().toIPv4Address())),
+ dgram.senderAddress().toString().toLatin1());
+ else if (!dgram.senderAddress().isNull())
+ QVERIFY2(allAddresses.contains(dgram.senderAddress()),
+ dgram.senderAddress().toString().toLatin1());
+ QCOMPARE(dgram.senderPort(), int(broadcastSocket.localPort()));
+ if (!dgram.destinationAddress().isNull()) {
+ QVERIFY2(dgram.destinationAddress() == QHostAddress::Broadcast
+ || broadcastAddresses.contains(dgram.destinationAddress()),
+ dgram.destinationAddress().toString().toLatin1());
+ QCOMPARE(dgram.destinationPort(), int(serverSocket.localPort()));
+ }
+
+ int ttl = dgram.hopLimit();
+ if (ttl != -1)
+ QVERIFY(ttl != 0);
} while (serverSocket.hasPendingDatagrams());
}
}
@@ -435,13 +461,8 @@ void tst_QUdpSocket::ipv6Loop()
char peterBuffer[16*1024];
char paulBuffer[16*1024];
-#if !defined(Q_OS_WINCE)
- QVERIFY2(peter.waitForReadyRead(5000), QtNetworkSettings::msgSocketError(peter).constData());
- QVERIFY2(paul.waitForReadyRead(5000), QtNetworkSettings::msgSocketError(paul).constData());
-#else
- QVERIFY(peter.waitForReadyRead(15000));
- QVERIFY(paul.waitForReadyRead(15000));
-#endif
+ QVERIFY(peter.waitForReadyRead(5000));
+ QVERIFY(paul.waitForReadyRead(5000));
if (success) {
QCOMPARE(peter.readDatagram(peterBuffer, sizeof(peterBuffer)), qint64(paulMessage.length()));
QCOMPARE(paul.readDatagram(paulBuffer, sizeof(peterBuffer)), qint64(peterMessage.length()));
@@ -938,9 +959,6 @@ void tst_QUdpSocket::outOfProcessConnectedClientServerTest()
#ifdef QT_NO_PROCESS
QSKIP("No qprocess support", SkipAll);
#else
-#if defined(Q_OS_WINCE)
- QSKIP("This test depends on reading data from QProcess (not supported on Qt/WinCE).");
-#endif
QProcess serverProcess;
serverProcess.start(QLatin1String("clientserver/clientserver server 1 1"),
QIODevice::ReadWrite | QIODevice::Text);
@@ -1002,9 +1020,6 @@ void tst_QUdpSocket::outOfProcessUnconnectedClientServerTest()
#ifdef QT_NO_PROCESS
QSKIP("No qprocess support", SkipAll);
#else
-#if defined(Q_OS_WINCE)
- QSKIP("This test depends on reading data from QProcess (not supported on Qt/WinCE).");
-#endif
QProcess serverProcess;
serverProcess.start(QLatin1String("clientserver/clientserver server 1 1"),
QIODevice::ReadWrite | QIODevice::Text);
@@ -1081,7 +1096,7 @@ void tst_QUdpSocket::zeroLengthDatagram()
#ifdef FORCE_SESSION
sender.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
- QCOMPARE(sender.writeDatagram(QByteArray(), QHostAddress::LocalHost, receiver.localPort()), qint64(0));
+ QCOMPARE(sender.writeDatagram(QNetworkDatagram(QByteArray(), QHostAddress::LocalHost, receiver.localPort())), qint64(0));
QVERIFY2(receiver.waitForReadyRead(1000), QtNetworkSettings::msgSocketError(receiver).constData());
QVERIFY(receiver.hasPendingDatagrams());
@@ -1366,10 +1381,20 @@ void tst_QUdpSocket::multicast()
QVERIFY(receiver.hasPendingDatagrams());
QList<QByteArray> receivedDatagrams;
while (receiver.hasPendingDatagrams()) {
- QByteArray datagram;
- datagram.resize(receiver.pendingDatagramSize());
- receiver.readDatagram(datagram.data(), datagram.size(), 0, 0);
- receivedDatagrams << datagram;
+ QNetworkDatagram dgram = receiver.receiveDatagram();
+ receivedDatagrams << dgram.data();
+
+ QVERIFY2(allAddresses.contains(dgram.senderAddress()),
+ dgram.senderAddress().toString().toLatin1());
+ QCOMPARE(dgram.senderPort(), int(sender.localPort()));
+ if (!dgram.destinationAddress().isNull()) {
+ QCOMPARE(dgram.destinationAddress(), groupAddress);
+ QCOMPARE(dgram.destinationPort(), int(receiver.localPort()));
+ }
+
+ int ttl = dgram.hopLimit();
+ if (ttl != -1)
+ QVERIFY(ttl != 0);
}
QCOMPARE(receivedDatagrams, datagrams);
@@ -1464,7 +1489,8 @@ void tst_QUdpSocket::linkLocalIPv6()
quint16 port = 0;
foreach (const QHostAddress& addr, addresses) {
QUdpSocket *s = new QUdpSocket;
- QVERIFY2(s->bind(addr, port), qPrintable(s->errorString()));
+ QVERIFY2(s->bind(addr, port), addr.toString().toLatin1()
+ + '/' + QByteArray::number(port) + ": " + qPrintable(s->errorString()));
port = s->localPort(); //bind same port, different networks
sockets << s;
}
@@ -1474,24 +1500,25 @@ void tst_QUdpSocket::linkLocalIPv6()
QSignalSpy neutralReadSpy(&neutral, SIGNAL(readyRead()));
QByteArray testData("hello");
- QByteArray receiveBuffer("xxxxx");
foreach (QUdpSocket *s, sockets) {
QSignalSpy spy(s, SIGNAL(readyRead()));
neutralReadSpy.clear();
QVERIFY(s->writeDatagram(testData, s->localAddress(), neutral.localPort()));
QTRY_VERIFY(neutralReadSpy.count() > 0); //note may need to accept a firewall prompt
- QHostAddress from;
- quint16 fromPort;
- QCOMPARE((int)neutral.readDatagram(receiveBuffer.data(), receiveBuffer.length(), &from, &fromPort), testData.length());
- QCOMPARE(from, s->localAddress());
- QCOMPARE(fromPort, s->localPort());
- QCOMPARE(receiveBuffer, testData);
-
- QVERIFY(neutral.writeDatagram(testData, s->localAddress(), s->localPort()));
+
+ QNetworkDatagram dgram = neutral.receiveDatagram(testData.length() * 2);
+ QVERIFY(dgram.isValid());
+ QCOMPARE(dgram.senderAddress(), s->localAddress());
+ QCOMPARE(dgram.senderPort(), int(s->localPort()));
+ QCOMPARE(dgram.data().length(), testData.length());
+ QCOMPARE(dgram.data(), testData);
+
+ QVERIFY(neutral.writeDatagram(dgram.makeReply(testData)));
QTRY_VERIFY(spy.count() > 0); //note may need to accept a firewall prompt
- QCOMPARE((int)s->readDatagram(receiveBuffer.data(), receiveBuffer.length(), &from, &fromPort), testData.length());
- QCOMPARE(receiveBuffer, testData);
+
+ dgram = s->receiveDatagram(testData.length() * 2);
+ QCOMPARE(dgram.data(), testData);
//sockets bound to other interfaces shouldn't have received anything
foreach (QUdpSocket *s2, sockets) {
@@ -1546,21 +1573,23 @@ void tst_QUdpSocket::linkLocalIPv4()
QVERIFY(neutral.bind(QHostAddress(QHostAddress::AnyIPv4)));
QByteArray testData("hello");
- QByteArray receiveBuffer("xxxxx");
foreach (QUdpSocket *s, sockets) {
QVERIFY(s->writeDatagram(testData, s->localAddress(), neutral.localPort()));
QVERIFY2(neutral.waitForReadyRead(10000), QtNetworkSettings::msgSocketError(neutral).constData());
- QHostAddress from;
- quint16 fromPort;
- QCOMPARE((int)neutral.readDatagram(receiveBuffer.data(), receiveBuffer.length(), &from, &fromPort), testData.length());
- QCOMPARE(from, s->localAddress());
- QCOMPARE(fromPort, s->localPort());
- QCOMPARE(receiveBuffer, testData);
-
- QVERIFY(neutral.writeDatagram(testData, s->localAddress(), s->localPort()));
+
QVERIFY2(s->waitForReadyRead(10000), QtNetworkSettings::msgSocketError(*s).constData());
- QCOMPARE((int)s->readDatagram(receiveBuffer.data(), receiveBuffer.length(), &from, &fromPort), testData.length());
- QCOMPARE(receiveBuffer, testData);
+ QNetworkDatagram dgram = neutral.receiveDatagram(testData.length() * 2);
+ QVERIFY(dgram.isValid());
+ QCOMPARE(dgram.senderAddress(), s->localAddress());
+ QCOMPARE(dgram.senderPort(), int(s->localPort()));
+ QCOMPARE(dgram.data().length(), testData.length());
+ QCOMPARE(dgram.data(), testData);
+
+ QVERIFY(neutral.writeDatagram(dgram.makeReply(testData)));
+
+ dgram = s->receiveDatagram(testData.length() * 2);
+ QVERIFY(dgram.isValid());
+ QCOMPARE(dgram.data(), testData);
//sockets bound to other interfaces shouldn't have received anything
foreach (QUdpSocket *s2, sockets) {
diff --git a/tests/auto/network/ssl/qsslcertificate/qsslcertificate.pro b/tests/auto/network/ssl/qsslcertificate/qsslcertificate.pro
index 87a210c051..7c1cd5b66b 100644
--- a/tests/auto/network/ssl/qsslcertificate/qsslcertificate.pro
+++ b/tests/auto/network/ssl/qsslcertificate/qsslcertificate.pro
@@ -1,7 +1,7 @@
CONFIG += testcase
SOURCES += tst_qsslcertificate.cpp
-!wince:win32:LIBS += -lws2_32
+win32:LIBS += -lws2_32
QT = core network testlib
TARGET = tst_qsslcertificate
diff --git a/tests/auto/network/ssl/qsslcipher/qsslcipher.pro b/tests/auto/network/ssl/qsslcipher/qsslcipher.pro
index 4cb2dfebab..81ef2d8d9a 100644
--- a/tests/auto/network/ssl/qsslcipher/qsslcipher.pro
+++ b/tests/auto/network/ssl/qsslcipher/qsslcipher.pro
@@ -1,7 +1,7 @@
CONFIG += testcase
SOURCES += tst_qsslcipher.cpp
-win32:!wince: LIBS += -lws2_32
+win32:LIBS += -lws2_32
QT = core network testlib
TARGET = tst_qsslcipher
diff --git a/tests/auto/network/ssl/qssldiffiehellmanparameters/qssldiffiehellmanparameters.pro b/tests/auto/network/ssl/qssldiffiehellmanparameters/qssldiffiehellmanparameters.pro
new file mode 100644
index 0000000000..b8053f9eb3
--- /dev/null
+++ b/tests/auto/network/ssl/qssldiffiehellmanparameters/qssldiffiehellmanparameters.pro
@@ -0,0 +1,8 @@
+CONFIG += testcase
+CONFIG += parallel_test
+
+SOURCES += tst_qssldiffiehellmanparameters.cpp
+!wince*:win32:LIBS += -lws2_32
+QT = core network testlib
+
+TARGET = tst_qssldiffiehellmanparameters
diff --git a/tests/auto/network/ssl/qssldiffiehellmanparameters/tst_qssldiffiehellmanparameters.cpp b/tests/auto/network/ssl/qssldiffiehellmanparameters/tst_qssldiffiehellmanparameters.cpp
new file mode 100644
index 0000000000..32cc982a65
--- /dev/null
+++ b/tests/auto/network/ssl/qssldiffiehellmanparameters/tst_qssldiffiehellmanparameters.cpp
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QSslDiffieHellmanParameters>
+#include <QSslSocket>
+#include <QByteArray>
+
+class tst_QSslDiffieHellmanParameters : public QObject
+{
+ Q_OBJECT
+
+#ifndef QT_NO_SSL
+private Q_SLOTS:
+ void constructionEmpty();
+ void constructionDefault();
+ void constructionDER();
+ void constructionPEM();
+ void unsafe512Bits();
+ void unsafeNonPrime();
+#endif
+};
+
+#ifndef QT_NO_SSL
+
+void tst_QSslDiffieHellmanParameters::constructionEmpty()
+{
+ QSslDiffieHellmanParameters dh;
+
+ QCOMPARE(dh.isEmpty(), true);
+ QCOMPARE(dh.isValid(), true);
+ QCOMPARE(dh.error(), QSslDiffieHellmanParameters::NoError);
+}
+
+void tst_QSslDiffieHellmanParameters::constructionDefault()
+{
+ QSslDiffieHellmanParameters dh = QSslDiffieHellmanParameters::defaultParameters();
+
+#ifndef QT_NO_OPENSSL
+ QCOMPARE(dh.isValid(), true);
+ QCOMPARE(dh.error(), QSslDiffieHellmanParameters::NoError);
+#endif
+}
+
+void tst_QSslDiffieHellmanParameters::constructionDER()
+{
+ // Uniquely generated with 'openssl dhparam -outform DER -out out.der -check -2 4096'
+ QSslDiffieHellmanParameters dh(QByteArray::fromBase64(QByteArrayLiteral(
+ "MIICCAKCAgEAsbQYx57ZlyEyWF8jD5WYEswGR2aTVFsHqP3026SdyTwcjY+YlMOae0EagK"
+ "jDA0UlPcih1kguQOvOVgyc5gI3YbBb4pCNEdy048xITlsdqG7qC3+2VvFR3vfixEbQQll9"
+ "2cGIIneD/36p7KJcDnBNUwwWj/VJKhTwelTfKTj2T39si9xGMkqZiQuCaXRk6vSKZ4ZDPk"
+ "jiq5Ti1kHVFbL9SMWRa8zplPtDMrVfhSyw10njgD4qKd1UoUPdmhEPhRZlHaZ/cAHNSHMj"
+ "uhDakeMpN+XP2/sl5IpPZ3/vVOk9PhBDFO1NYzKx/b7RQgZCUmXoglKYpfBiz8OheoI0hK"
+ "V0fU/OCtHjRrP4hE9vIHA2aE+gaQZiYCciGcR9BjHQ7Y8K9qHyTX8UIz2G4ZKzQZK9G+pA"
+ "K0xD+1H3qZ/MaUhzNDQOwwihnTjjXzTjfIGqYDdbouAhw+tX51CsGonI0cL3s3QMa3CwGH"
+ "mw+AH2b/Z68dTSy0sC3CYn9cNbrctqyeHwQrsx9FfpOz+Z6sk2WsPgqgSp/pDVVgm5oSfO"
+ "2mN7WAWgUlf9TQuj1HIRCTI+PbBq2vYvn+YResMRo+8ng1QptKAAgQoVVGNRYxZ9iAZlvO"
+ "52DcHKlsqDuafQ1XVGmzVIrKtBi2gfLtPqY4v6g6v26l8gbzK67PpWstllHiPb4VMCAQI="
+ )), QSsl::Der);
+
+#ifndef QT_NO_OPENSSL
+ QCOMPARE(dh.isValid(), true);
+ QCOMPARE(dh.error(), QSslDiffieHellmanParameters::NoError);
+#endif
+}
+
+void tst_QSslDiffieHellmanParameters::constructionPEM()
+{
+ // Uniquely generated with 'openssl dhparam -outform PEM -out out.pem -check -2 4096'
+ QSslDiffieHellmanParameters dh(QByteArrayLiteral(
+ "-----BEGIN DH PARAMETERS-----\n"
+ "MIICCAKCAgEA9QTdqhQkbGuhWzBsW5X475AjjrITpg1BHX5+mp1sstUd84Lshq1T\n"
+ "+S2QQQtdl25EPoUblpyyLAf8krFSH4YwR7jjLWklA8paDOwRYod0zLmVZ1Wx6og3\n"
+ "PRc8P+SCs+6gKTXfv//bJJhiJXnM73lDFsGHbSqN+msf20ei/zy5Rwey2t8dPjLC\n"
+ "Q+qkb/avlovi2t2rsUWcxMT1875TQ4HuApayqw3R3lTQe9u05b9rTrinmT7AE4mm\n"
+ "xGqO9FZJdXYE2sOKwwJkpM48KFyV90uJANmqJnQrkgdukaGTHwxZxgAyO6ur/RWC\n"
+ "kzf9STFT6IY4Qy05q+oZVJfh8xPHszKmmC8nWaLfiHMYBnL5fv+1kh/aU11Kz9TG\n"
+ "iDXwQ+tzhKAutQPUwe3IGQUYQMZPwZI4vegdU88/7YPXuWt7b/0Il5+2ma5FbtG2\n"
+ "u02PMi+J3JZsYi/tEUv1tJBVHGH0kDpgcyOm8rvkCtNbNkETzfwUPoEgA0oPMhVt\n"
+ "sFGub1av+jLRyFNGNBJcqXAO+Tq2zXG00DxbGY+aooJ50qU/Lh5gfnCEMDXlMM9P\n"
+ "T8JVpWaaNLCC+0Z5txsfYp+FO8mOttIPIF6F8FtmTnm/jhNntvqKvsU+NHylIYzr\n"
+ "o42EpiWwS7ktPPUS2GtG+IUdy8rvdO1xJ5kNxs7ZlygY4W1htOhbUusCAQI=\n"
+ "-----END DH PARAMETERS-----\n"
+ ), QSsl::Pem);
+
+#ifndef QT_NO_OPENSSL
+ QCOMPARE(dh.isValid(), true);
+ QCOMPARE(dh.error(), QSslDiffieHellmanParameters::NoError);
+#endif
+}
+
+void tst_QSslDiffieHellmanParameters::unsafe512Bits()
+{
+ // Uniquely generated with 'openssl dhparam -outform PEM -out out.pem -check -2 512'
+ QSslDiffieHellmanParameters dh(QByteArrayLiteral(
+ "-----BEGIN DH PARAMETERS-----\n"
+ "MEYCQQCf8goDn56akiliAtEL1ZG7VH+9wfLxsv8/B1emTUG+rMKB1yaVAU7HaAiM\n"
+ "Gtmo2bAWUqBczUTOTzqmWTm28P6bAgEC\n"
+ "-----END DH PARAMETERS-----\n"
+ ), QSsl::Pem);
+
+#ifndef QT_NO_OPENSSL
+ QCOMPARE(dh.isValid(), false);
+ QCOMPARE(dh.error(), QSslDiffieHellmanParameters::UnsafeParametersError);
+#endif
+}
+
+void tst_QSslDiffieHellmanParameters::unsafeNonPrime()
+{
+ // Uniquely generated with 'openssl dhparam -outform DER -out out.der -check -2 1024'
+ // and then modified by hand to make P not be a prime number.
+ QSslDiffieHellmanParameters dh(QByteArray::fromBase64(QByteArrayLiteral(
+ "MIGHAoGBALLcOLg+ow8TMnbCUeNjwys6wUTIH9mn4ZSeIbD6qvCsJgg4cUxXwJQmPY"
+ "Xl15AsKXgkXWh0n+/N6tjH0sSRJnzDvN2H3KxFLKkvxmBYrDOJMdCuMgZD50aOsVyd"
+ "vholAW9zilkoYkB6sqwxY1Z2dbpTWajCsUAWZQ0AIP4Y5nesAgEC"
+ )), QSsl::Der);
+
+#ifndef QT_NO_OPENSSL
+ QCOMPARE(dh.isValid(), false);
+ QCOMPARE(dh.error(), QSslDiffieHellmanParameters::UnsafeParametersError);
+#endif
+}
+
+#endif // QT_NO_SSL
+
+QTEST_MAIN(tst_QSslDiffieHellmanParameters)
+#include "tst_qssldiffiehellmanparameters.moc"
diff --git a/tests/auto/network/ssl/qsslellipticcurve/qsslellipticcurve.pro b/tests/auto/network/ssl/qsslellipticcurve/qsslellipticcurve.pro
index e67b64b2b7..a180086c5e 100644
--- a/tests/auto/network/ssl/qsslellipticcurve/qsslellipticcurve.pro
+++ b/tests/auto/network/ssl/qsslellipticcurve/qsslellipticcurve.pro
@@ -1,7 +1,7 @@
CONFIG += testcase
SOURCES += tst_qsslellipticcurve.cpp
-win32:!wince: LIBS += -lws2_32
+win32:LIBS += -lws2_32
QT = core network testlib
TARGET = tst_qsslellipticcurve
diff --git a/tests/auto/network/ssl/qsslerror/qsslerror.pro b/tests/auto/network/ssl/qsslerror/qsslerror.pro
index 7737aae3f1..117fd4ac27 100644
--- a/tests/auto/network/ssl/qsslerror/qsslerror.pro
+++ b/tests/auto/network/ssl/qsslerror/qsslerror.pro
@@ -1,7 +1,7 @@
CONFIG += testcase
SOURCES += tst_qsslerror.cpp
-win32:!wince: LIBS += -lws2_32
+win32:LIBS += -lws2_32
QT = core network testlib
TARGET = tst_qsslerror
diff --git a/tests/auto/network/ssl/qsslkey/qsslkey.pro b/tests/auto/network/ssl/qsslkey/qsslkey.pro
index 7eb04793f3..52377a9005 100644
--- a/tests/auto/network/ssl/qsslkey/qsslkey.pro
+++ b/tests/auto/network/ssl/qsslkey/qsslkey.pro
@@ -1,7 +1,7 @@
CONFIG += testcase
SOURCES += tst_qsslkey.cpp
-win32:!wince: LIBS += -lws2_32
+win32:LIBS += -lws2_32
QT = core network testlib
contains(QT_CONFIG, private_tests) {
QT += core-private network-private
diff --git a/tests/auto/network/ssl/qsslsocket/qsslsocket.pro b/tests/auto/network/ssl/qsslsocket/qsslsocket.pro
index de2be8e126..9a525408fd 100644
--- a/tests/auto/network/ssl/qsslsocket/qsslsocket.pro
+++ b/tests/auto/network/ssl/qsslsocket/qsslsocket.pro
@@ -1,7 +1,7 @@
CONFIG += testcase
SOURCES += tst_qsslsocket.cpp
-win32:!wince: LIBS += -lws2_32
+win32:LIBS += -lws2_32
QT = core core-private network-private testlib
TARGET = tst_qsslsocket
@@ -20,14 +20,6 @@ contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) {
LIBS += $$OPENSSL_LIBS
}
-wince* {
- DEFINES += SRCDIR=\\\"./\\\"
-
- certFiles.files = certs ssl.tar.gz
- certFiles.path = .
- DEPLOYMENT += certFiles
-} else {
- DEFINES += SRCDIR=\\\"$$PWD/\\\"
-}
+DEFINES += SRCDIR=\\\"$$PWD/\\\"
requires(contains(QT_CONFIG,private_tests))
diff --git a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
index e85c6b9922..bf38a09aeb 100644
--- a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
+++ b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
@@ -220,6 +220,10 @@ private slots:
void qtbug18498_peek();
void qtbug18498_peek2();
void dhServer();
+#ifndef QT_NO_OPENSSL
+ void dhServerCustomParamsNull();
+ void dhServerCustomParams();
+#endif
void ecdhServer();
void verifyClientCertificate_data();
void verifyClientCertificate();
@@ -229,6 +233,8 @@ private slots:
void simplePskConnect();
void ephemeralServerKey_data();
void ephemeralServerKey();
+ void allowedProtocolNegotiation();
+ void pskServer();
#endif
void setEmptyDefaultConfiguration(); // this test should be last
@@ -1159,7 +1165,9 @@ void tst_QSslSocket::protocolServerSide_data()
#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
QTest::newRow("ssl2-ssl2") << QSsl::SslV2 << QSsl::SslV2 << false; // no idea why it does not work, but we don't care about SSL 2
#endif
+#if !defined(OPENSSL_NO_SSL3)
QTest::newRow("ssl3-ssl3") << QSsl::SslV3 << QSsl::SslV3 << true;
+#endif
QTest::newRow("tls1.0-tls1.0") << QSsl::TlsV1_0 << QSsl::TlsV1_0 << true;
QTest::newRow("tls1ssl3-tls1ssl3") << QSsl::TlsV1SslV3 << QSsl::TlsV1SslV3 << true;
QTest::newRow("any-any") << QSsl::AnyProtocol << QSsl::AnyProtocol << true;
@@ -1173,23 +1181,27 @@ void tst_QSslSocket::protocolServerSide_data()
QTest::newRow("ssl2-any") << QSsl::SslV2 << QSsl::AnyProtocol << false; // no idea why it does not work, but we don't care about SSL 2
#endif
-#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
+#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT) && !defined(OPENSSL_NO_SSL3)
QTest::newRow("ssl3-ssl2") << QSsl::SslV3 << QSsl::SslV2 << false;
#endif
+#if !defined(OPENSSL_NO_SSL3)
QTest::newRow("ssl3-tls1.0") << QSsl::SslV3 << QSsl::TlsV1_0 << false;
QTest::newRow("ssl3-tls1ssl3") << QSsl::SslV3 << QSsl::TlsV1SslV3 << true;
QTest::newRow("ssl3-secure") << QSsl::SslV3 << QSsl::SecureProtocols << false;
-#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
+#endif
+#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT) && !defined(OPENSSL_NO_SSL3)
QTest::newRow("ssl3-any") << QSsl::SslV3 << QSsl::AnyProtocol << false; // we won't set a SNI header here because we connect to a
// numerical IP, so OpenSSL will send a SSL 2 handshake
-#else
+#elif !defined(OPENSSL_NO_SSL3)
QTest::newRow("ssl3-any") << QSsl::SslV3 << QSsl::AnyProtocol << true;
#endif
#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
QTest::newRow("tls1.0-ssl2") << QSsl::TlsV1_0 << QSsl::SslV2 << false;
#endif
+#if !defined(OPENSSL_NO_SSL3)
QTest::newRow("tls1.0-ssl3") << QSsl::TlsV1_0 << QSsl::SslV3 << false;
+#endif
QTest::newRow("tls1-tls1ssl3") << QSsl::TlsV1_0 << QSsl::TlsV1SslV3 << true;
QTest::newRow("tls1.0-secure") << QSsl::TlsV1_0 << QSsl::SecureProtocols << true;
#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
@@ -1202,7 +1214,9 @@ void tst_QSslSocket::protocolServerSide_data()
#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
QTest::newRow("tls1ssl3-ssl2") << QSsl::TlsV1SslV3 << QSsl::SslV2 << false;
#endif
+#if !defined(OPENSSL_NO_SSL3)
QTest::newRow("tls1ssl3-ssl3") << QSsl::TlsV1SslV3 << QSsl::SslV3 << true;
+#endif
QTest::newRow("tls1ssl3-tls1.0") << QSsl::TlsV1SslV3 << QSsl::TlsV1_0 << true;
QTest::newRow("tls1ssl3-secure") << QSsl::TlsV1SslV3 << QSsl::SecureProtocols << true;
QTest::newRow("tls1ssl3-any") << QSsl::TlsV1SslV3 << QSsl::AnyProtocol << true;
@@ -1210,7 +1224,9 @@ void tst_QSslSocket::protocolServerSide_data()
#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
QTest::newRow("secure-ssl2") << QSsl::SecureProtocols << QSsl::SslV2 << false;
#endif
+#if !defined(OPENSSL_NO_SSL3)
QTest::newRow("secure-ssl3") << QSsl::SecureProtocols << QSsl::SslV3 << false;
+#endif
QTest::newRow("secure-tls1.0") << QSsl::SecureProtocols << QSsl::TlsV1_0 << true;
QTest::newRow("secure-tls1ssl3") << QSsl::SecureProtocols << QSsl::TlsV1SslV3 << true;
QTest::newRow("secure-any") << QSsl::SecureProtocols << QSsl::AnyProtocol << true;
@@ -1218,7 +1234,9 @@ void tst_QSslSocket::protocolServerSide_data()
#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
QTest::newRow("any-ssl2") << QSsl::AnyProtocol << QSsl::SslV2 << false; // no idea why it does not work, but we don't care about SSL 2
#endif
+#if !defined(OPENSSL_NO_SSL3)
QTest::newRow("any-ssl3") << QSsl::AnyProtocol << QSsl::SslV3 << true;
+#endif
QTest::newRow("any-tls1.0") << QSsl::AnyProtocol << QSsl::TlsV1_0 << true;
QTest::newRow("any-tls1ssl3") << QSsl::AnyProtocol << QSsl::TlsV1SslV3 << true;
QTest::newRow("any-secure") << QSsl::AnyProtocol << QSsl::SecureProtocols << true;
@@ -2846,10 +2864,8 @@ void tst_QSslSocket::qtbug18498_peek2()
void tst_QSslSocket::dhServer()
{
- if (!QSslSocket::supportsSsl()) {
- qWarning("SSL not supported, skipping test");
- return;
- }
+ if (!QSslSocket::supportsSsl())
+ QSKIP("No SSL support");
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
@@ -2874,6 +2890,87 @@ void tst_QSslSocket::dhServer()
QCOMPARE(client->state(), QAbstractSocket::ConnectedState);
}
+#ifndef QT_NO_OPENSSL
+void tst_QSslSocket::dhServerCustomParamsNull()
+{
+ if (!QSslSocket::supportsSsl())
+ QSKIP("No SSL support");
+
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ return;
+
+ SslServer server;
+ server.ciphers = QLatin1String("DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA");
+
+ QSslConfiguration cfg = server.config;
+ cfg.setDiffieHellmanParameters(QSslDiffieHellmanParameters());
+ server.config = cfg;
+
+ QVERIFY(server.listen());
+
+ QEventLoop loop;
+ QTimer::singleShot(5000, &loop, SLOT(quit()));
+
+ QSslSocketPtr client(new QSslSocket);
+ socket = client.data();
+ connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit()));
+ connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
+ connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));
+
+ client->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
+
+ loop.exec();
+
+ QVERIFY(client->state() != QAbstractSocket::ConnectedState);
+}
+#endif // QT_NO_OPENSSL
+
+#ifndef QT_NO_OPENSSL
+void tst_QSslSocket::dhServerCustomParams()
+{
+ if (!QSslSocket::supportsSsl())
+ QSKIP("No SSL support");
+
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ return;
+
+ SslServer server;
+ server.ciphers = QLatin1String("DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA");
+
+ QSslConfiguration cfg = server.config;
+
+ // Custom 2048-bit DH parameters generated with 'openssl dhparam -outform DER -out out.der -check -2 2048'
+ QSslDiffieHellmanParameters dh(QByteArray::fromBase64(QByteArrayLiteral(
+ "MIIBCAKCAQEAvVA7b8keTfjFutCtTJmP/pnQfw/prKa+GMed/pBWjrC4N1YwnI8h/A861d9WE/VWY7XMTjvjX3/0"
+ "aaU8wEe0EXNpFdlTH+ZMQctQTSJOyQH0RCTwJfDGPCPT9L+c9GKwEKWORH38Earip986HJc0w3UbnfIwXUdsWHiXi"
+ "Z6r3cpyBmTKlsXTFiDVAOUXSiO8d/zOb6zHZbDfyB/VbtZRmnA7TXVn9oMzC0g9+FXHdrV4K+XfdvNZdCegvoAZiy"
+ "R6ZQgNG9aZ36/AQekhg060hp55f9HDPgXqYeNeXBiferjUtU7S9b3s83XhOJAr01/0Tf5dENwCfg2gK36TM8cC4wI"
+ "BAg==")), QSsl::Der);
+ cfg.setDiffieHellmanParameters(dh);
+
+ server.config = cfg;
+
+ QVERIFY(server.listen());
+
+ QEventLoop loop;
+ QTimer::singleShot(5000, &loop, SLOT(quit()));
+
+ QSslSocketPtr client(new QSslSocket);
+ socket = client.data();
+ connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit()));
+ connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
+ connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));
+
+ client->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
+
+ loop.exec();
+
+ QVERIFY(client->state() == QAbstractSocket::ConnectedState);
+}
+#endif // QT_NO_OPENSSL
+
void tst_QSslSocket::ecdhServer()
{
if (!QSslSocket::supportsSsl()) {
@@ -3064,8 +3161,12 @@ class PskProvider : public QObject
Q_OBJECT
public:
+ bool m_server;
+ QByteArray m_identity;
+ QByteArray m_psk;
+
explicit PskProvider(QObject *parent = 0)
- : QObject(parent)
+ : QObject(parent), m_server(false)
{
}
@@ -3084,7 +3185,11 @@ public slots:
{
QVERIFY(authenticator);
QCOMPARE(authenticator->identityHint(), PSK_SERVER_IDENTITY_HINT);
- QVERIFY(authenticator->maximumIdentityLength() > 0);
+ if (m_server)
+ QCOMPARE(authenticator->maximumIdentityLength(), 0);
+ else
+ QVERIFY(authenticator->maximumIdentityLength() > 0);
+
QVERIFY(authenticator->maximumPreSharedKeyLength() > 0);
if (!m_identity.isEmpty()) {
@@ -3097,12 +3202,61 @@ public slots:
QCOMPARE(authenticator->preSharedKey(), m_psk);
}
}
-
-private:
- QByteArray m_identity;
- QByteArray m_psk;
};
+class PskServer : public QTcpServer
+{
+ Q_OBJECT
+public:
+ PskServer()
+ : socket(0),
+ config(QSslConfiguration::defaultConfiguration()),
+ ignoreSslErrors(true),
+ peerVerifyMode(QSslSocket::AutoVerifyPeer),
+ protocol(QSsl::TlsV1_0),
+ m_pskProvider()
+ {
+ m_pskProvider.m_server = true;
+ }
+ QSslSocket *socket;
+ QSslConfiguration config;
+ bool ignoreSslErrors;
+ QSslSocket::PeerVerifyMode peerVerifyMode;
+ QSsl::SslProtocol protocol;
+ QString ciphers;
+ PskProvider m_pskProvider;
+
+protected:
+ void incomingConnection(qintptr socketDescriptor)
+ {
+ socket = new QSslSocket(this);
+ socket->setSslConfiguration(config);
+ socket->setPeerVerifyMode(peerVerifyMode);
+ socket->setProtocol(protocol);
+ if (ignoreSslErrors)
+ connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
+
+ if (!ciphers.isEmpty()) {
+ socket->setCiphers(ciphers);
+ }
+
+ QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState));
+ QVERIFY(!socket->peerAddress().isNull());
+ QVERIFY(socket->peerPort() != 0);
+ QVERIFY(!socket->localAddress().isNull());
+ QVERIFY(socket->localPort() != 0);
+
+ connect(socket, &QSslSocket::preSharedKeyAuthenticationRequired, &m_pskProvider, &PskProvider::providePsk);
+
+ socket->startServerEncryption();
+ }
+
+protected slots:
+ void ignoreErrorSlot()
+ {
+ socket->ignoreSslErrors();
+ }
+};
void tst_QSslSocket::simplePskConnect_data()
{
QTest::addColumn<PskConnectTestType>("pskTestType");
@@ -3404,6 +3558,129 @@ void tst_QSslSocket::ephemeralServerKey()
QCOMPARE(client->sslConfiguration().ephemeralServerKey().isNull(), emptyKey);
}
+void tst_QSslSocket::allowedProtocolNegotiation()
+{
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(OPENSSL_NO_TLSEXT)
+
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ return;
+
+ const QByteArray expectedNegotiated("cool-protocol");
+ QList<QByteArray> serverProtos;
+ serverProtos << expectedNegotiated << "not-so-cool-protocol";
+ QList<QByteArray> clientProtos;
+ clientProtos << "uber-cool-protocol" << expectedNegotiated << "not-so-cool-protocol";
+
+
+ SslServer server;
+ server.config.setAllowedNextProtocols(serverProtos);
+ QVERIFY(server.listen());
+
+ QSslSocket clientSocket;
+ auto configuration = clientSocket.sslConfiguration();
+ configuration.setAllowedNextProtocols(clientProtos);
+ clientSocket.setSslConfiguration(configuration);
+
+ clientSocket.connectToHostEncrypted("127.0.0.1", server.serverPort());
+ clientSocket.ignoreSslErrors();
+
+ QEventLoop loop;
+ QTimer::singleShot(5000, &loop, SLOT(quit()));
+ connect(&clientSocket, SIGNAL(encrypted()), &loop, SLOT(quit()));
+ loop.exec();
+
+ QVERIFY(server.socket->sslConfiguration().nextNegotiatedProtocol() ==
+ clientSocket.sslConfiguration().nextNegotiatedProtocol());
+ QVERIFY(server.socket->sslConfiguration().nextNegotiatedProtocol() == expectedNegotiated);
+
+#endif // OPENSSL_VERSION_NUMBER
+}
+
+void tst_QSslSocket::pskServer()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ if (!QSslSocket::supportsSsl() || setProxy)
+ return;
+
+ QSslSocket socket;
+ this->socket = &socket;
+
+ QSignalSpy connectedSpy(&socket, SIGNAL(connected()));
+ QVERIFY(connectedSpy.isValid());
+
+ QSignalSpy disconnectedSpy(&socket, SIGNAL(disconnected()));
+ QVERIFY(disconnectedSpy.isValid());
+
+ QSignalSpy connectionEncryptedSpy(&socket, SIGNAL(encrypted()));
+ QVERIFY(connectionEncryptedSpy.isValid());
+
+ QSignalSpy pskAuthenticationRequiredSpy(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)));
+ QVERIFY(pskAuthenticationRequiredSpy.isValid());
+
+ connect(&socket, SIGNAL(connected()), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(disconnected()), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(modeChanged(QSslSocket::SslMode)), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(encrypted()), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(peerVerifyError(QSslError)), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(exitLoop()));
+
+ // force a PSK cipher w/o auth
+ socket.setCiphers(PSK_CIPHER_WITHOUT_AUTH);
+
+ PskProvider provider;
+ provider.setIdentity(PSK_CLIENT_IDENTITY);
+ provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY);
+ connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*)));
+ socket.setPeerVerifyMode(QSslSocket::VerifyNone);
+
+ PskServer server;
+ server.m_pskProvider.setIdentity(provider.m_identity);
+ server.m_pskProvider.setPreSharedKey(provider.m_psk);
+ server.config.setPreSharedKeyIdentityHint(PSK_SERVER_IDENTITY_HINT);
+ QVERIFY(server.listen());
+
+ // Start connecting
+ socket.connectToHost(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
+ enterLoop(5);
+
+ // Entered connected state
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+ QCOMPARE(socket.mode(), QSslSocket::UnencryptedMode);
+ QVERIFY(!socket.isEncrypted());
+ QCOMPARE(connectedSpy.count(), 1);
+ QCOMPARE(disconnectedSpy.count(), 0);
+
+ // Enter encrypted mode
+ socket.startClientEncryption();
+ QCOMPARE(socket.mode(), QSslSocket::SslClientMode);
+ QVERIFY(!socket.isEncrypted());
+ QCOMPARE(connectionEncryptedSpy.count(), 0);
+
+ // Start handshake.
+ enterLoop(10);
+
+ // We must get the PSK signal in all cases
+ QCOMPARE(pskAuthenticationRequiredSpy.count(), 1);
+
+ QCOMPARE(connectionEncryptedSpy.count(), 1);
+ QVERIFY(socket.isEncrypted());
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+
+ // check writing
+ socket.write("Hello from Qt TLS/PSK!");
+ QVERIFY(socket.waitForBytesWritten());
+
+ // disconnect
+ socket.disconnectFromHost();
+ enterLoop(10);
+
+ QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState);
+ QCOMPARE(disconnectedSpy.count(), 1);
+}
+
#endif // QT_NO_OPENSSL
#endif // QT_NO_SSL
diff --git a/tests/auto/network/ssl/qsslsocket_onDemandCertificates_member/qsslsocket_onDemandCertificates_member.pro b/tests/auto/network/ssl/qsslsocket_onDemandCertificates_member/qsslsocket_onDemandCertificates_member.pro
index ae911e43ed..4ea878f840 100644
--- a/tests/auto/network/ssl/qsslsocket_onDemandCertificates_member/qsslsocket_onDemandCertificates_member.pro
+++ b/tests/auto/network/ssl/qsslsocket_onDemandCertificates_member/qsslsocket_onDemandCertificates_member.pro
@@ -2,7 +2,7 @@ CONFIG += testcase
testcase.timeout = 300 # this test is slow
SOURCES += tst_qsslsocket_onDemandCertificates_member.cpp
-win32:!wince: LIBS += -lws2_32
+win32:LIBS += -lws2_32
QT = core core-private network-private testlib
TARGET = tst_qsslsocket_onDemandCertificates_member
@@ -15,10 +15,6 @@ win32 {
}
}
-wince* {
- DEFINES += SRCDIR=\\\"./\\\"
-} else {
- DEFINES += SRCDIR=\\\"$$PWD/\\\"
-}
+DEFINES += SRCDIR=\\\"$$PWD/\\\"
requires(contains(QT_CONFIG,private_tests))
diff --git a/tests/auto/network/ssl/qsslsocket_onDemandCertificates_static/qsslsocket_onDemandCertificates_static.pro b/tests/auto/network/ssl/qsslsocket_onDemandCertificates_static/qsslsocket_onDemandCertificates_static.pro
index 25e5a5d5c7..d69ea7fb3d 100644
--- a/tests/auto/network/ssl/qsslsocket_onDemandCertificates_static/qsslsocket_onDemandCertificates_static.pro
+++ b/tests/auto/network/ssl/qsslsocket_onDemandCertificates_static/qsslsocket_onDemandCertificates_static.pro
@@ -1,7 +1,7 @@
CONFIG += testcase
SOURCES += tst_qsslsocket_onDemandCertificates_static.cpp
-win32:!wince: LIBS += -lws2_32
+win32:LIBS += -lws2_32
QT = core core-private network-private testlib
TARGET = tst_qsslsocket_onDemandCertificates_static
@@ -14,10 +14,6 @@ win32 {
}
}
-wince* {
- DEFINES += SRCDIR=\\\"./\\\"
-} else {
- DEFINES += SRCDIR=\\\"$$PWD/\\\"
-}
+DEFINES += SRCDIR=\\\"$$PWD/\\\"
requires(contains(QT_CONFIG,private_tests))
diff --git a/tests/auto/network/ssl/ssl.pro b/tests/auto/network/ssl/ssl.pro
index 25d79ebfe8..df7c10ed19 100644
--- a/tests/auto/network/ssl/ssl.pro
+++ b/tests/auto/network/ssl/ssl.pro
@@ -21,6 +21,7 @@ winrt: SUBDIRS -= \
contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) {
contains(QT_CONFIG, private_tests) {
- SUBDIRS += qasn1element
+ SUBDIRS += qasn1element \
+ qssldiffiehellmanparameters
}
}